1
0

7 Komitmen 965c6c48d6 ... b4ad5bfeff

Pembuat SHA1 Pesan Tanggal
  Joseph Brandenburg b4ad5bfeff Add profiler to help optimize nodes 10 bulan lalu
  Joseph Brandenburg b2507f4983 Refactor: xForm_info data class for setting parents 11 bulan lalu
  Joseph Brandenburg ead2ed726a add utility function to clear traverse targets 11 bulan lalu
  Joseph Brandenburg d2a048a2b7 Fix Vector Division 11 bulan lalu
  Joseph Brandenburg 18522fe086 lots of renamed variables, functions, etc 11 bulan lalu
  Joseph Brandenburg 8860af32ad rename node_container_common to node_common 11 bulan lalu
  Joseph Brandenburg 364dd262c1 rename internal_containers to internal_nodes 11 bulan lalu

+ 21 - 14
base_definitions.py

@@ -160,7 +160,7 @@ class MantisTree(NodeTree):
         #    - 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.
 
-    def execute_tree(self,context, error_popups = False):
+    def execute_tree(self,context, error_popups = False, profile=False):
         self.prevent_next_exec = False
         if not self.hash:
             return
@@ -171,7 +171,7 @@ class MantisTree(NodeTree):
         from . import readtree
         try:
             context.scene.render.use_lock_interface = True
-            readtree.execute_tree(self.parsed_tree, self, context, error_popups)
+            readtree.execute_tree(self.parsed_tree, self, context, error_popups, profile=profile)
         except RecursionError as e:
             prRed("Recursion error while parsing tree.")
         finally:
@@ -737,8 +737,8 @@ class MantisNode:
     
     @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
+        from .utilities import get_ui_node
+        return get_ui_node(self.ui_signature, self.base_tree).bl_idname
     
     def reset_execution(self) -> None:
         """ Reset the node for additional execution without re-building the tree."""
@@ -775,6 +775,12 @@ class MantisNode:
             self.inputs[a].set_traverse_target(self.outputs[b])
             self.outputs[b].set_traverse_target(self.inputs[a])
 
+    def clear_traverse(self, inputs = [str], outputs = [str]) -> None:
+        for inp in inputs:
+            self.inputs[inp].set_traverse(None)
+        for out in outputs:
+            self.inputs[out].set_traverse(None)
+
     def flush_links(self) -> None:
         for inp in self.inputs.values():
             inp.flush_links()
@@ -845,7 +851,7 @@ class MantisNode:
             conn.reset_execution_recursive()
     
     def evaluate_input(self, input_name, index=0)  -> Any:
-        from .node_container_common import trace_single_line
+        from .node_common import trace_single_line
         if not (self.inputs.get(input_name)): # get the named parameter if there is no input
             return self.parameters.get(input_name) # this will return None if the parameter does not exist.
         # this trace() should give a key error if there is a problem
@@ -855,16 +861,16 @@ class MantisNode:
         return prop
     
     def fill_parameters(self, ui_node=None)  -> None:
-        from .utilities import get_node_prototype
-        from .node_container_common import get_socket_value
+        from .utilities import get_ui_node
+        from .node_common import get_socket_value
         if not ui_node:
             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??
-                ui_node = get_node_prototype(self.signature, self.base_tree)
+                ui_node = get_ui_node(self.signature, self.base_tree)
             if not ui_node:
-                raise RuntimeError(wrapRed("No node prototype found for... %s" % ( [self.base_tree] + list(self.signature[1:]) ) ) )
+                raise RuntimeError(wrapRed("No UI Node found for... %s" % ( [self.base_tree] + list(self.signature[1:]) ) ) )
         for key in self.parameters.keys():
             node_socket = ui_node.inputs.get(key)
             if self.parameters[key] is not None: # the parameters are usually initialized as None.
@@ -971,11 +977,11 @@ class MantisNode:
 # do I need this and the link class above?
 class DummyLink:
     #gonna use this for faking links to keep the interface consistent
-    def __init__(self, from_socket, to_socket, nc_from=None, nc_to=None, original_from=None, multi_input_sort_id=0):
+    def __init__(self, from_socket, to_socket, from_mantis_node=None, to_mantis_node=None, original_from=None, multi_input_sort_id=0):
         self.from_socket = from_socket
         self.to_socket = to_socket
-        self.nc_from = nc_from
-        self.nc_to = nc_to
+        self.from_mantis_node = from_mantis_node
+        self.to_mantis_node = to_mantis_node
         self.multi_input_sort_id = multi_input_sort_id
         # self.from_node = from_socket.node
         # self.to_node = to_socket.node
@@ -984,7 +990,7 @@ class DummyLink:
         else:
             self.original_from = self.from_socket
     def __repr__(self):
-        return(self.nc_from.__repr__()+":"+self.from_socket.name + " -> " + self.nc_to.__repr__()+":"+self.to_socket.name)
+        return(self.from_mantis_node.__repr__()+":"+self.from_socket.name + " -> " + self.to_mantis_node.__repr__()+":"+self.to_socket.name)
 
 
 def detect_hierarchy_link(from_node, from_socket, to_node, to_socket,):
@@ -1082,7 +1088,8 @@ class NodeSocket:
     
     def set_traverse_target(self, traverse_target):
         self.traverse_target = traverse_target
-        self.can_traverse = True
+        if traverse_target: self.can_traverse = True
+        else: self.can_traverse = False
     
     def flush_links(self):
         """ Removes dead links from this socket."""

+ 6 - 6
deformer_nodes.py

@@ -1,4 +1,4 @@
-from .node_container_common import *
+from .node_common import *
 from .xForm_nodes import xFormGeometryObject, xFormObjectInstance
 from .misc_nodes import InputExistingGeometryObject
 from .base_definitions import MantisNode, MantisSocketTemplate
@@ -26,12 +26,12 @@ def TellClasses():
 # object instance probably can't use the deformer but it doesn't hurt to try.
 deformable_types= (xFormGeometryObject, InputExistingGeometryObject, xFormObjectInstance)
 
-def trace_xForm_back(nc, socket):
-    if (trace := trace_single_line(nc, socket)[0] ) :
+def trace_xForm_back(mantis_node, socket):
+    if (trace := trace_single_line(mantis_node, socket)[0] ) :
         for i in range(len(trace)): # have to look in reverse, actually
             if ( isinstance(trace[ i ], deformable_types ) ):
                 return trace[ i ].bGetObject()
-        raise GraphError(wrapRed(f"No other object found for {nc}."))
+        raise GraphError(wrapRed(f"No other object found for {mantis_node}."))
 
 class MantisDeformerNode(MantisNode):
     def __init__(self, signature : tuple,
@@ -52,9 +52,9 @@ class MantisDeformerNode(MantisNode):
         else:
             return super().evaluate_input(input_name, index)
     
-    def GetxForm(nc, output_name="Deformer"):
+    def GetxForm(mantis_node, output_name="Deformer"):
         break_condition= lambda node : node.__class__ in deformable_types
-        xforms = trace_line_up_branching(nc, output_name, break_condition)
+        xforms = trace_line_up_branching(mantis_node, output_name, break_condition)
         return_me=[]
         for xf in xforms:
             if xf.node_type != 'XFORM':

+ 3 - 3
deformer_nodes_ui.py

@@ -61,9 +61,9 @@ class DeformerArmatureNode(Node, DeformerNode):
     def display_update(self, parsed_tree, context):
         self.inputs["Copy Skin Weights From"].hide = True
         node_tree = context.space_data.path[0].node_tree
-        nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-        if nc:
-            self.inputs["Copy Skin Weights From"].hide = not (nc.evaluate_input("Skinning Method") == "COPY_FROM_OBJECT")
+        mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+        if mantis_node:
+            self.inputs["Copy Skin Weights From"].hide = not (mantis_node.evaluate_input("Skinning Method") == "COPY_FROM_OBJECT")
                 
 
 class DeformerHook(Node, DeformerNode):

+ 152 - 0
dev_helpers/profile_nodes.py

@@ -0,0 +1,152 @@
+import time
+import json
+from dataclasses import dataclass, field
+from typing import Dict, List, Optional, Tuple
+
+
+
+@dataclass
+class PassRecord:
+    pass_name: str
+    durations: List[float] = field(default_factory=list)
+
+    def add_duration(self, duration: float):
+        self.durations.append(duration)
+
+    def stats(self):
+        if not self.durations:
+            return {"count": 0, "avg": 0, "total": 0, "max": 0, "min": 0}
+        return {
+            "count": len(self.durations),
+            "avg": sum(self.durations) / len(self.durations),
+            "total": sum(self.durations),
+            "max": max(self.durations),
+            "min": min(self.durations),
+        }
+
+
+@dataclass
+class NodeRecord:
+    node_type: str
+    node_id: Tuple[Optional[str]] = field(default_factory=tuple)
+    passes: Dict[str, PassRecord] = field(default_factory=dict)
+
+    def record_pass(self, pass_name: str, duration: float):
+        if pass_name not in self.passes:
+            self.passes[pass_name] = PassRecord(pass_name)
+        self.passes[pass_name].add_duration(duration)
+
+    def stats(self):
+        return {pname: prec.stats() for pname, prec in self.passes.items()}
+
+
+@dataclass
+class ProfileSession:
+    name: str
+    nodes: Dict[Tuple[Optional[str]], NodeRecord] = field(default_factory=dict)
+
+    def record(self, node_id: Tuple[Optional[str]], node_type: str, pass_name: str, duration: float):
+        if node_id not in self.nodes:
+            self.nodes[node_id] = NodeRecord(node_id, node_type)
+        self.nodes[node_id].record_pass(pass_name, duration)
+
+    def stats(self):
+        return {nid: node.stats() for nid, node in self.nodes.items()}
+
+
+class NodeProfiler:
+    def __init__(self):
+        self.sessions: Dict[str, ProfileSession] = {}
+
+    def new_session(self, name: str):
+        self.sessions[name] = ProfileSession(name)
+        self._current = self.sessions[name]
+
+    def record(self, node, pass_name, *args, **kwargs):
+        """Profile execution of a function (e.g., node pass)."""
+        node_id = node.signature
+        node_type = node.__class__.__name__
+        func = getattr(node, pass_name)
+        start = time.perf_counter()
+        result = func(*args, **kwargs)
+        duration = time.perf_counter() - start
+        self._current.record(node_type, node_id, pass_name, duration)
+        return result
+
+    def get_stats(self, session_name: Optional[str]):
+        if session_name is None:
+            session_name = list(self.sessions.keys())[-1] # get the lastest one
+        return self.sessions[session_name].stats()
+
+    def to_json(self):
+        def encode(obj):
+            if hasattr(obj, "__dict__"):
+                return obj.__dict__
+            return obj
+        return json.dumps(self.sessions, default=encode, indent=2)
+
+    @classmethod
+    def from_json(cls, data: str):
+        raw = json.loads(data)
+        profiler = cls()
+        for sname, sdata in raw.items():
+            session = ProfileSession(name=sdata["name"])
+            for nid, ndata in sdata["nodes"].items():
+                node = NodeRecord(node_id=ndata["node_id"], node_type=ndata["node_type"])
+                for pname, pdata in ndata["passes"].items():
+                    record = PassRecord(pname, pdata["durations"])
+                    node.passes[pname] = record
+                session.nodes[nid] = node
+            profiler.sessions[sname] = session
+        return profiler
+
+
+def summarize_profile(session, pass_name = "", sort_key='total'):
+    """
+    Print a formatted summary from a ProfileSession object.
+    Columns: Node Type | Count | Total | Average | Min | Max
+    """
+    from ..base_definitions import FLOAT_EPSILON
+    from collections import defaultdict
+
+    summary = defaultdict(lambda: {"count": 0, "total": 0.0, "min": float("inf"), "max": 0.0})
+
+    for node in session.nodes.values():
+        node_type = node.node_type
+        prec = node.passes.get(pass_name)
+        if prec: # not every node does every pass.
+            for d in prec.durations:
+                s = summary[node_type]
+                s["count"] += 1
+                s["total"] += d
+                s["min"] = min(s["min"], d)
+                s["max"] = max(s["max"], d)
+
+    # Prepare and print the table
+    print(f"Pass: {pass_name}\n")
+    header =  f"{'Node Type':<40}{'Count':>8}{'Total(s)':>12}{'Avg(s)':>12}{'Min(s)':>12}{'Max(s)':>12}"
+    print(header)
+    print("-" * len(header))
+
+    sorted_items = list(summary.items())
+    sorted_items.sort(key = lambda a : -a[1][sort_key]) # for some reason this reverse sorts if I don't negate it?
+
+    accumulated_count = 0; accumulated_total = 0; overall_avg = 0
+    overall_min = float("inf"); overall_max = -1
+
+    for node_type, data in sorted_items:
+        count = data["count"]; total = data["total"]
+        accumulated_total += total # always accumulate this even for noop
+        avg = total / count if count else 0
+        if avg < 0.0000033: continue # try to avoid printing it if it is a no-op or not significant
+        accumulated_count += count
+        overall_min = min(overall_min, data['min']); overall_max = max(overall_max, data['max'])
+        print(f"{node_type:<40}{count:>8}{total:>12.4f}{avg:>12.4f}{data['min']:>12.4f}{data['max']:>12.4f}")
+    
+    if accumulated_count != 0: # avoid zero-division. The average is not meaningful in this case, anyway.
+        overall_avg = accumulated_total/accumulated_count
+
+    footer =  f"{f'Summary({pass_name}): ':<40}{int(accumulated_count):>8}{accumulated_total:>12.4f}{overall_avg:>12.4f}{overall_min:>12.4f}{overall_max:>12.4f}"
+    print("-" * len(footer))
+    print(footer)
+    print ("\n")

+ 7 - 7
i_o.py

@@ -150,10 +150,10 @@ def fix_custom_parameter(n, property_definition, ):
 #     armatures = set()
 #     curves    = set()
 #     for node in base_tree.parsed_tree.values():
-#         from .utilities import get_node_prototype
+#         from .utilities import get_ui_node
 #         if node.ui_signature is None:
 #             continue
-#         ui_node = get_node_prototype(node.ui_signature, node.base_tree)
+#         ui_node = get_ui_node(node.ui_signature, node.base_tree)
 #         if ui_node is None or ui_node.id_data != current_tree:
 #             continue
 #         if hasattr(node, "bGetObject"):
@@ -189,11 +189,11 @@ def scan_tree_dependencies(base_tree, curves:set, armatures:set, ):
     else:
         base_tree.update_tree(context=context)
         nodes = base_tree.parsed_tree
-    for nc in nodes.values():
-        nc.reset_execution()
-        check_and_add_root(nc, xForm_pass)
+    for mantis_node in nodes.values():
+        mantis_node.reset_execution()
+        check_and_add_root(mantis_node, xForm_pass)
     from .readtree import sort_execution
-    from .utilities import get_node_prototype
+    from .utilities import get_ui_node
     sorted_nodes, execution_failed = sort_execution(nodes, xForm_pass)
     if execution_failed:
         prRed("Error reading dependencies from tree, skipping")
@@ -201,7 +201,7 @@ def scan_tree_dependencies(base_tree, curves:set, armatures:set, ):
     for n in sorted_nodes:
         if n.ui_signature is None:
             continue # doesn't matter
-        ui_node = get_node_prototype(n.ui_signature, n.base_tree)
+        ui_node = get_ui_node(n.ui_signature, n.base_tree)
         if not ui_node:
             continue
         # we need to see if it is receiving a Curve

+ 7 - 7
internal_containers.py → internal_nodes.py

@@ -1,25 +1,25 @@
-from .node_container_common import *
+from .node_common import *
 from bpy.types import Node
 from .base_definitions import MantisNode
 from uuid import uuid4
 
 class DummyNode(MantisNode):
-    def __init__(self, signature, base_tree, prototype = None, ui_signature=None):
+    def __init__(self, signature, base_tree, ui_node = None, ui_signature=None):
         super().__init__(signature, base_tree)
-        self.prototype = prototype
+        self.ui_node = ui_node
         self.node_type = 'DUMMY'
         self.prepared = True
         self.uuid = uuid4()
         self.solver = None
-        if prototype:
-            if prototype.bl_idname in ["MantisSchemaGroup"]:
+        if ui_node:
+            if ui_node.bl_idname in ["MantisSchemaGroup"]:
                 self.node_type = 'DUMMY_SCHEMA'
                 self.prepared = False
-            for sock in prototype.inputs:
+            for sock in ui_node.inputs:
                 if sock.identifier == "__extend__" or sock.name == "__extend__":
                     continue
                 self.inputs[sock.identifier] = NodeSocket(is_input = True, name = sock.identifier, node = self)
-            for sock in prototype.outputs:
+            for sock in ui_node.outputs:
                 if sock.identifier == "__extend__" or sock.name == "__extend__":
                     continue
                 self.outputs[sock.identifier] = NodeSocket(is_input = False, name = sock.identifier, node = self)

+ 12 - 4
link_nodes.py

@@ -1,4 +1,4 @@
-from .node_container_common import *
+from .node_common import *
 from bpy.types import Bone, NodeTree
 from .base_definitions import MantisNode, GraphError, FLOAT_EPSILON
 from .link_socket_templates import *
@@ -37,7 +37,7 @@ def TellClasses():
             ]
 
 # set the name if it is available, otherwise just use the constraint's nice name
-set_constraint_name = lambda nc : nc.evaluate_input("Name") if nc.evaluate_input("Name") else nc.__class__.__name__
+set_constraint_name = lambda mantis_node : mantis_node.evaluate_input("Name") if mantis_node.evaluate_input("Name") else mantis_node.__class__.__name__
 
 
 class MantisLinkNode(MantisNode):
@@ -48,6 +48,10 @@ class MantisLinkNode(MantisNode):
         self.node_type = 'LINK'
         self.prepared = True; self.bObject=[]
 
+    def bTransformPass(self, bContext=None):
+        parent_xForm_info = get_parent_xForm_info(self, 'Input Relationship')
+        self.parameters['Output Relationship'] = parent_xForm_info
+
     def evaluate_input(self, input_name, index=0):
         # should catch 'Target', 'Pole Target' and ArmatureConstraint targets, too
         if ('Target' in input_name) and input_name not in  ["Target Space", "Use Target Z"]:
@@ -84,9 +88,9 @@ class MantisLinkNode(MantisNode):
                 else:
                     c.space_object=xf
 
-    def GetxForm(nc, output_name="Output Relationship"):
+    def GetxForm(mantis_node, output_name="Output Relationship"):
         break_condition= lambda node : node.node_type=='XFORM'
-        xforms = trace_line_up_branching(nc, output_name, break_condition)
+        xforms = trace_line_up_branching(mantis_node, output_name, break_condition)
         return_me=[]
         for xf in xforms:
             if xf.node_type != 'XFORM':
@@ -115,6 +119,10 @@ class LinkInherit(MantisLinkNode):
         super().__init__(signature, base_tree, LinkInheritSockets)
         self.init_parameters()
         self.set_traverse([('Parent', 'Inheritance')])
+
+    def bTransformPass(self, bContext=None):
+        parent_xForm_info = get_parent_xForm_info(self, 'Parent')
+        self.parameters['Inheritance'] = parent_xForm_info
         self.executed = True
 
     def GetxForm(self):

+ 13 - 13
link_nodes_ui.py

@@ -60,16 +60,16 @@ class LinkInheritNode(Node, LinkNode):
 
     def display_update(self, parsed_tree, context):
         node_tree = context.space_data.path[0].node_tree
-        nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-        if nc:
+        mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+        if mantis_node:
             bone_prev, bone_next = False, False
-            if (inp := nc.inputs["Parent"]).is_connected:
+            if (inp := mantis_node.inputs["Parent"]).is_connected:
                 if  from_node := inp.links[0].from_node:
                     if from_node.__class__.__name__ in ["xFormBone"]:
                         bone_prev=True
             bone_next=True
             try:
-                xForm = nc.GetxForm()
+                xForm = mantis_node.GetxForm()
                 if xForm.__class__.__name__ not in "xFormBone":
 
                     bone_next=False
@@ -318,23 +318,23 @@ class LinkTransformationNode(Node, LinkNode):
 
     def display_update(self, parsed_tree, context):
         node_tree = context.space_data.path[0].node_tree
-        nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-        if nc:
-            if nc.evaluate_input("Map From") == "ROTATION":
+        mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+        if mantis_node:
+            if mantis_node.evaluate_input("Map From") == "ROTATION":
                 self.inputs["Rotation Mode"].hide=False
             else:
                 self.inputs["Rotation Mode"].hide=True
-            if   nc.evaluate_input("Map To") == "TRANSLATION":
+            if   mantis_node.evaluate_input("Map To") == "TRANSLATION":
                     self.inputs["Rotation Order"].hide=True
                     self.inputs["Mix Mode (Translation)"].hide=False
                     self.inputs["Mix Mode (Rotation)"].hide=True
                     self.inputs["Mix Mode (Scale)"].hide=True
-            elif nc.evaluate_input("Map To") == "ROTATION":
+            elif mantis_node.evaluate_input("Map To") == "ROTATION":
                     self.inputs["Rotation Order"].hide=False
                     self.inputs["Mix Mode (Translation)"].hide=True
                     self.inputs["Mix Mode (Rotation)"].hide=False
                     self.inputs["Mix Mode (Scale)"].hide=True
-            elif nc.evaluate_input("Map To") == "SCALE":
+            elif mantis_node.evaluate_input("Map To") == "SCALE":
                     self.inputs["Rotation Order"].hide=True
                     self.inputs["Mix Mode (Translation)"].hide=True
                     self.inputs["Mix Mode (Rotation)"].hide=True
@@ -409,10 +409,10 @@ class LinkShrinkWrapNode(Node, LinkNode):
         shrink_type = self.inputs['Mode'].default_value
         if self.inputs['Mode'].is_linked:# 1% or less of cases
             node_tree = context.space_data.path[0].node_tree
-            nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
+            mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
             shrink_is_project = False
-            if nc:
-                shrink_type = nc.evaluate_input("Mode")
+            if mantis_node:
+                shrink_type = mantis_node.evaluate_input("Mode")
         if shrink_type != "PROJECT":
             self.inputs['Project Axis'].hide=True
             self.inputs['Space'].hide=True

+ 11 - 0
mantis_dataclasses.py

@@ -0,0 +1,11 @@
+from dataclasses import dataclass, field
+
+@dataclass
+class xForm_info():
+    object_type          :  str = field(default="")
+    root_armature        :  str = field(default="")
+    parent_pose_name     :  str = field(default="")
+    parent_edit_name     :  str = field(default="")
+    self_pose_name       :  str = field(default="")
+    self_edit_name      :  str = field(default="")
+# should I add node signatures to this?

+ 4 - 2
math_nodes.py

@@ -1,4 +1,4 @@
-from .node_container_common import *
+from .node_common import *
 from .base_definitions import MantisNode, NodeSocket
 
 def TellClasses():
@@ -137,7 +137,9 @@ class MathStaticVector(MantisNode):
         if self.evaluate_input("Operation") == "MULTIPLY":
             v_result = a*b
         if self.evaluate_input("Operation") == "DIVIDE":
-            v_result = a/b
+            v_result = Vector((0,0,0)) # channel-wise division
+            for i, (axis_a, axis_b) in enumerate(zip(a,b)):
+                v_result[i] = axis_a/axis_b
         if self.evaluate_input("Operation") == "POWER":
             v_result = a**b
         # since these are unary, we need to make a copy lest we create spooky effects elsewhere.

+ 6 - 6
math_nodes_ui.py

@@ -38,8 +38,8 @@ class MathStaticInt(Node, MantisUINode):
     def display_update(self, parsed_tree, context):
         if context.space_data:
             node_tree = context.space_data.path[0].node_tree
-            nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-            op = nc.evaluate_input("Operation")
+            mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+            op = mantis_node.evaluate_input("Operation")
             if op in ['ABSOLUTE']:
                 self.inputs["Int B"].hide = True
             else:
@@ -66,8 +66,8 @@ class MathStaticFloatNode(Node, MantisUINode):
     def display_update(self, parsed_tree, context):
         if context.space_data:
             node_tree = context.space_data.path[0].node_tree
-            nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-            op = nc.evaluate_input("Operation")
+            mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+            op = mantis_node.evaluate_input("Operation")
             if op in ['ABSOLUTE', 'FLOOR', 'CEIL', 'ROUND']:
                 self.inputs["Float B"].hide = True
             else:
@@ -93,8 +93,8 @@ class MathStaticVectorNode(Node, MantisUINode):
     def display_update(self, parsed_tree, context):
         if context.space_data:
             node_tree = context.space_data.path[0].node_tree
-            nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-            op = nc.evaluate_input("Operation")
+            mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+            op = mantis_node.evaluate_input("Operation")
             # Scalar output
             if op in ['LENGTH', 'DOT']:
                 self.outputs["Result Vector"].hide = True

+ 8 - 8
misc_nodes.py

@@ -1,4 +1,4 @@
-from .node_container_common import *
+from .node_common import *
 from .base_definitions import MantisNode, NodeSocket, FLOAT_EPSILON
 from .xForm_nodes import xFormArmature, xFormBone
 from .misc_nodes_socket_templates import *
@@ -309,8 +309,8 @@ class InputThemeBoneColorSets(SimpleInputNode):
 
     def fill_parameters(self, ui_node=None):
         if not ui_node:
-            from .utilities import get_node_prototype
-            ui_node = get_node_prototype(self.ui_signature, self.base_tree)
+            from .utilities import get_ui_node
+            ui_node = get_ui_node(self.ui_signature, self.base_tree)
         for i in range(20):
             self.parameters[f"Color {str(i).zfill(2)}"] = ui_node.outputs[i].default_value
         return super().fill_parameters(ui_node)
@@ -322,8 +322,8 @@ class InputColorSetPallete(SimpleInputNode):
 
     def fill_parameters(self, ui_node=None):
         if not ui_node:
-            from .utilities import get_node_prototype
-            ui_node = get_node_prototype(self.ui_signature, self.base_tree)
+            from .utilities import get_ui_node
+            ui_node = get_ui_node(self.ui_signature, self.base_tree)
         from .base_definitions import MantisSocketTemplate
         outputs = []
         for o in ui_node.outputs:
@@ -812,7 +812,7 @@ class UtilityBoneProperties(SimpleInputNode):
         self.outputs.init_sockets(outputs)
         self.init_parameters()
 
-    def fill_parameters(self, prototype=None):
+    def fill_parameters(self, ui_node=None):
         return
 
 # TODO this should probably be moved to Links
@@ -1417,8 +1417,8 @@ class UtilityDeclareCollections(MantisNode):
 
     def fill_parameters(self, ui_node=None):
         if ui_node is None:
-            from .utilities import get_node_prototype
-            ui_node = get_node_prototype(self.ui_signature, self.base_tree)
+            from .utilities import get_ui_node
+            ui_node = get_ui_node(self.ui_signature, self.base_tree)
         from .base_definitions import MantisSocketTemplate as SockTemplate
         templates=[]
         for out in ui_node.outputs:

+ 25 - 25
misc_nodes_ui.py

@@ -457,10 +457,10 @@ class UtilityMetaRigNode(Node, MantisUINode):
         self.initialized = True
     
     def display_update(self, parsed_tree, context):
-        nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-        if nc:
-            self.armature= nc.evaluate_input("Meta-Armature")
-            self.pose_bone= nc.evaluate_input("Meta-Bone")
+        mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+        if mantis_node:
+            self.armature= mantis_node.evaluate_input("Meta-Armature")
+            self.pose_bone= mantis_node.evaluate_input("Meta-Bone")
         if not self.armature:
             self.inputs["Meta-Bone"].hide=True
         else:
@@ -517,9 +517,9 @@ class UtilityDriverVariableNode(Node, MantisUINode):
         if self.inputs["Variable Type"].is_linked:
             if context.space_data:
                 node_tree = context.space_data.path[0].node_tree
-                nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-                if nc:
-                    driver_type = nc.evaluate_input("Variable Type")
+                mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+                if mantis_node:
+                    driver_type = mantis_node.evaluate_input("Variable Type")
         else:
             driver_type = self.inputs[0].default_value
         if driver_type == 'SINGLE_PROP':
@@ -594,9 +594,9 @@ class UtilityDriverNode(Node, MantisUINode):
     def display_update(self, parsed_tree, context):
         if not self.inputs["Driver Type"].is_linked:
             dType = self.inputs["Driver Type"].default_value
-        nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-        if nc:
-            dType = nc.evaluate_input("Driver Type")
+        mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+        if mantis_node:
+            dType = mantis_node.evaluate_input("Driver Type")
         if dType == 'SCRIPTED':
             self.inputs["Expression"].hide = False
         else:
@@ -692,14 +692,14 @@ class UtilityCatStringsNode(Node, MantisUINode):
         
     def display_update(self, parsed_tree, context):
         if context.space_data:
-            nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
+            mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
             self.inputs['String_1'].display_text = ""
             self.inputs['String_2'].display_text = ""
             self.outputs['OutputString'].display_text = ""
-            if nc:
+            if mantis_node:
                 try:
-                    self.inputs['String_1'].display_text = a = nc.evaluate_input("String_1")
-                    self.inputs['String_2'].display_text = b = nc.evaluate_input("String_2")
+                    self.inputs['String_1'].display_text = a = mantis_node.evaluate_input("String_1")
+                    self.inputs['String_2'].display_text = b = mantis_node.evaluate_input("String_2")
                     # cat the strings here, since the node may not have run yet.
                     self.outputs['OutputString'].display_text = a+b
                 except KeyError:
@@ -746,9 +746,9 @@ class InputExistingGeometryObjectNode(Node, MantisUINode):
         self.initialized = True
     
     def display_update(self, parsed_tree, context):
-        nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-        if nc: # this is done here so I don't have to define yet another custom socket.
-            self.object_reference = bpy.data.objects.get(nc.evaluate_input("Name"))
+        mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+        if mantis_node: # this is done here so I don't have to define yet another custom socket.
+            self.object_reference = bpy.data.objects.get(mantis_node.evaluate_input("Name"))
     
 # TODO: maybe I should hold a data reference here, too.
 #       but it is complicated by the fact that Mantis does not distinguish b/tw geo types
@@ -980,12 +980,12 @@ class UtilityKeyframe(Node, MantisUINode):
 
     # def display_update(self, parsed_tree, context):
     #     if context.space_data:
-    #         nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-    #         if nc.evaluate_input("Interpolation") in ["CONSTANT", "LINEAR"]:
+    #         mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+    #         if mantis_node.evaluate_input("Interpolation") in ["CONSTANT", "LINEAR"]:
     #             for inp in self.inputs[1:6]:
     #                 inp.hide = True
     #         else:
-    #             if nc.evaluate_input("Left Handle Type") in ["FREE", "ALIGNED"]:
+    #             if mantis_node.evaluate_input("Left Handle Type") in ["FREE", "ALIGNED"]:
 
     #             for inp in self.inputs[1:6]:
     #                 inp.hide = False
@@ -1109,8 +1109,8 @@ class UtilityTransformationMatrix(Node, MantisUINode):
         operation = self.inputs['Operation'].default_value
         if self.inputs['Operation'].is_linked:
             if context.space_data:
-                nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-                operation = nc.evaluate_input("Operation")
+                mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+                operation = mantis_node.evaluate_input("Operation")
         if operation in ["ROTATE_AXIS_ANGLE", "SCALE"]:
             self.inputs["Vector"].hide = False
             self.inputs["W"].hide = False
@@ -1298,9 +1298,9 @@ class UtilityChoose(Node, MantisUINode):
         # if both inputs are the same color, then use that color for the result
         if self.inputs['Condition'].is_linked:
             from .base_definitions import get_signature_from_edited_tree
-            nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
-            if nc:
-                condition = nc.evaluate_input('Condition')
+            mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+            if mantis_node:
+                condition = mantis_node.evaluate_input('Condition')
         else:
             condition = self.inputs['Condition'].default_value
         if condition == True:

+ 107 - 75
node_container_common.py → node_common.py

@@ -18,12 +18,12 @@ def get_socket_value(node_socket):
     return value
 
 # TODO: modify this to work with multi-input nodes
-def trace_single_line(node_container, input_name, link_index=0):
+def trace_single_line(mantis_node, input_name, link_index=0):
     # DO: refactor this for new link class
     """Traces a line to its input."""
-    nodes = [node_container]
+    nodes = [mantis_node]
     # Trace a single line
-    if (socket := node_container.inputs.get(input_name) ):
+    if (socket := mantis_node.inputs.get(input_name) ):
         while (socket.is_linked):
             link = socket.links[link_index]; link_index = 0
             if (socket := link.from_node.outputs.get(link.from_socket)):
@@ -37,12 +37,12 @@ def trace_single_line(node_container, input_name, link_index=0):
     return nodes, socket
 
 # this is same as the other, just flip from/to and in/out
-def trace_single_line_up(node_container, output_name,):
+def trace_single_line_up(mantis_node, output_name,):
     """I use this to get the xForm from a link node."""
-    nodes = [node_container]
-    if hasattr(node_container, "outputs"):
+    nodes = [mantis_node]
+    if hasattr(mantis_node, "outputs"):
         # Trace a single line
-        if (socket := node_container.outputs.get(output_name) ):
+        if (socket := mantis_node.outputs.get(output_name) ):
             while (socket.is_linked):
                 # This is bad, but it's efficient for nodes that only expect
                 #  one path along the given line
@@ -60,13 +60,13 @@ def trace_single_line_up(node_container, output_name,):
                     break
     return nodes, socket
 
-def trace_line_up_branching(node : MantisNode, output_name : str,
-        break_condition : Callable = lambda node : False):
+def trace_line_up_branching(mantis_node : MantisNode, output_name : str,
+        break_condition : Callable = lambda mantis_node : False):
     """ Returns all leaf nodes at the ends of branching lines from an output."""
     leaf_nodes = []
-    if hasattr(node, "outputs"):
+    if hasattr(mantis_node, "outputs"):
         # Trace a single line
-        if (socket := node.outputs.get(output_name) ):
+        if (socket := mantis_node.outputs.get(output_name) ):
             check_sockets={socket}
             while (check_sockets):
                 # This is bad, but it's efficient for nodes that only expect
@@ -84,57 +84,89 @@ def trace_line_up_branching(node : MantisNode, output_name : str,
                             leaf_nodes.append(socket.node)
     return leaf_nodes
 
-def setup_custom_props(nc):
-    from .utilities import get_node_prototype
-    if nc.signature[0] == 'SCHEMA_AUTOGENERATED':
+# this function exists to walk back to the first ancestor node of XFORM or LINK without
+#  breaking the traverse behaviour which I still need for various reasons.
+def get_parent_xForm_info(mantis_node, socket_name = 'Relationship'):
+    from .mantis_dataclasses import xForm_info
+    # we re-implement the trace logic here for performance.
+    # because this kind of connection has traversal (for Links to find their children), we must
+    # simply seek back to the first available xForm or Link and get its xForm info.\
+    parent_node = None
+    if (socket := mantis_node.inputs.get(socket_name) ):
+        while (socket.is_linked):
+            link = socket.links[0]
+            if (socket := link.from_node.outputs.get(link.from_socket)):
+                if socket.node.node_type in ['LINK', 'XFORM']:
+                    parent_node = socket.node; break
+                if socket.can_traverse:
+                    socket = socket.traverse_target
+                else: # this is an output.
+                    break
+            else:
+                break
+    parent_xForm_info = None
+    if parent_node:
+        if parent_node.node_type == 'XFORM':
+            parent_xForm_info = parent_node.parameters['xForm Out']
+        else:
+            if 'Output Relationship' in parent_node.parameters.keys():
+                parent_xForm_info = parent_node.parameters['Output Relationship']
+            else: # TODO refactor Inherit Node to have the same output as the other link nodes.
+                parent_xForm_info = parent_node.parameters['Inheritance']
+    if parent_xForm_info is None: parent_xForm_info = xForm_info() # default to empty
+    return parent_xForm_info
+
+def setup_custom_props(mantis_node):
+    from .utilities import get_ui_node
+    if mantis_node.signature[0] == 'SCHEMA_AUTOGENERATED':
         from .base_definitions import custom_props_types
-        if nc.__class__.__name__ not in custom_props_types:
-            # prRed(f"Reminder: figure out how to deal with custom property setting for Schema Node {nc}")
-            raise RuntimeError(wrapRed(f"Custom Properties not set up for node {nc}"))
+        if mantis_node.__class__.__name__ not in custom_props_types:
+            # prRed(f"Reminder: figure out how to deal with custom property setting for Schema Node {mantis_node}")
+            raise RuntimeError(wrapRed(f"Custom Properties not set up for node {mantis_node}"))
         return
     else:
-        np = get_node_prototype(nc.signature, nc.base_tree)
-    if np:
-        setup_custom_props_from_np(nc, np)
+        ui_node = get_ui_node(mantis_node.signature, mantis_node.base_tree)
+    if ui_node:
+        setup_custom_props_from_np(mantis_node, ui_node)
     else:
-        prRed("Failed to setup custom properties for: nc")
+        prRed("Failed to setup custom properties for: mantis_node")
 
-def setup_custom_props_from_np(nc, np):
-    for inp in np.inputs:
+def setup_custom_props_from_np(mantis_node, ui_node):
+    for inp in ui_node.inputs:
         if inp.identifier == "__extend__": continue
-        if not (inp.name in nc.inputs.keys()) :
-            socket = NodeSocket(is_input = True, name = inp.name, node = nc,)
-            nc.inputs[inp.name] = socket
-            nc.parameters[inp.name] = None
+        if not (inp.name in mantis_node.inputs.keys()) :
+            socket = NodeSocket(is_input = True, name = inp.name, node = mantis_node,)
+            mantis_node.inputs[inp.name] = socket
+            mantis_node.parameters[inp.name] = None
             for attr_name in ["min", "max", "soft_min", "soft_max", "description"]:
                 try:
                     setattr(socket, attr_name, getattr(inp, attr_name))
                 except AttributeError:
                     pass
-    for out in np.outputs:
+    for out in ui_node.outputs:
         if out.identifier == "__extend__": continue
-        if not (out.name in nc.outputs.keys()) :
-            nc.outputs[out.name] = NodeSocket(is_input = False, name = out.name, node = nc,)
+        if not (out.name in mantis_node.outputs.keys()) :
+            mantis_node.outputs[out.name] = NodeSocket(is_input = False, name = out.name, node = mantis_node,)
             
-def prepare_parameters(nc):
+def prepare_parameters(mantis_node):
     # some nodes add new parameters at runtime, e.g. Drivers
-    # so we need to take that stuff from the node_containers that have
+    # so we need to take that stuff from the mantis_nodes that have
     #  been executed prior to this node.
-    for s_name, sock in nc.inputs.items():
+    for s_name, sock in mantis_node.inputs.items():
         if not (sock.is_linked):
             continue
         if (sock.name  in sock.links[0].from_node.parameters.keys()):
-            nc.parameters[s_name] = sock.links[0].from_node.parameters[sock.name]
+            mantis_node.parameters[s_name] = sock.links[0].from_node.parameters[sock.name]
     # should work, this is ugly.
 
-def check_for_driver(node_container, input_name, index = None):
-    prop = node_container.evaluate_input(input_name)
+def check_for_driver(mantis_node, input_name, index = None):
+    prop = mantis_node.evaluate_input(input_name)
     if (index is not None):
         prop = prop[index]
     return (prop.__class__.__name__ == 'MantisDriver')
 
 # TODO: this should handle sub-properties better
-def evaluate_sockets(nc, b_object, props_sockets,):
+def evaluate_sockets(mantis_node, b_object, props_sockets,):
     # this is neccesary because some things use dict properties for dynamic properties and setattr doesn't work
     def safe_setattr(ob, att_name, val):
         if ob.__class__.__name__ in ["NodesModifier"]:
@@ -158,7 +190,7 @@ def evaluate_sockets(nc, b_object, props_sockets,):
         index = None
         if isinstance(sock, tuple):
             index = sock[1]; sock = sock[0]
-        if (check_for_driver(nc, sock, index)):
+        if (check_for_driver(mantis_node, sock, index)):
             sock = (sock, index)
             original_prop = prop
             # TODO: deduplicate this terrible hack
@@ -178,60 +210,60 @@ def evaluate_sockets(nc, b_object, props_sockets,):
             # this is really stupid
             else:
                 safe_setattr(b_object, prop, default)
-            if nc.node_type in ['LINK',]:
+            if mantis_node.node_type in ['LINK',]:
                 printname  = wrapOrange(b_object.id_data.name)
-            elif nc.node_type in ['XFORM',]:
-                printname  = wrapOrange(nc.bGetObject().name)
+            elif mantis_node.node_type in ['XFORM',]:
+                printname  = wrapOrange(mantis_node.bGetObject().name)
             else:
-                printname = wrapOrange(nc)
-            print("Adding driver %s to %s in %s" % (wrapPurple(original_prop), wrapWhite(nc.signature[-1]), printname))
+                printname = wrapOrange(mantis_node)
+            print("Adding driver %s to %s in %s" % (wrapPurple(original_prop), wrapWhite(mantis_node.signature[-1]), printname))
             if b_object.__class__.__name__ in ["NodesModifier"]:
-                nc.drivers[sock] = "[\""+original_prop+"\"]" # lol. It is a dict element not a "true" property
+                mantis_node.drivers[sock] = "[\""+original_prop+"\"]" # lol. It is a dict element not a "true" property
             elif b_object.__class__.__name__ in ["Key"]:
-                nc.drivers[sock] = "key_blocks[\""+original_prop+"\"].value"
+                mantis_node.drivers[sock] = "key_blocks[\""+original_prop+"\"].value"
             else:
-                nc.drivers[sock] = original_prop
+                mantis_node.drivers[sock] = original_prop
         else: # here we can do error checking for the socket if needed
             if (index is not None):
-                safe_setattr(b_object, prop, nc.evaluate_input(sock)[index])
+                safe_setattr(b_object, prop, mantis_node.evaluate_input(sock)[index])
             else:                    # 'mute' is better than 'enabled'
                 # UGLY HACK          # because it is available in older
                 if (prop == 'mute'): # Blenders.
-                    safe_setattr(b_object, prop, not bool(nc.evaluate_input(sock)))
+                    safe_setattr(b_object, prop, not bool(mantis_node.evaluate_input(sock)))
                 elif (prop == 'hide'): # this will not cast it for me, annoying.
-                    safe_setattr(b_object, prop, bool(nc.evaluate_input(sock)))
+                    safe_setattr(b_object, prop, bool(mantis_node.evaluate_input(sock)))
                 else:
                     try:
-                        # prRed(b_object.name, nc, prop, nc.evaluate_input(sock) )
-                        # print( nc.evaluate_input(sock))
-                    # value_eval = nc.evaluate_input(sock)
+                        # prRed(b_object.name, mantis_node, prop, mantis_node.evaluate_input(sock) )
+                        # print( mantis_node.evaluate_input(sock))
+                    # value_eval = mantis_node.evaluate_input(sock)
                     # just wanna see if we are dealing with some collection
                     # check hasattr in case it is one of those ["such-and-such"] props, and ignore those
                         if hasattr(b_object, prop) and (not isinstance(getattr(b_object, prop), str)) and hasattr(getattr(b_object, prop), "__getitem__"):
                             # prGreen("Doing the thing")
-                            for val_index, value in enumerate(nc.evaluate_input(sock)):
+                            for val_index, value in enumerate(mantis_node.evaluate_input(sock)):
                                 # assume this will work, both because val should have the right number of elements, and because this should be the right data type.
                                 from .drivers import MantisDriver
                                 if isinstance(value, MantisDriver):
                                     getattr(b_object,prop)[val_index] =  default[val_index]
-                                    print("Adding driver %s to %s in %s" % (wrapPurple(prop), wrapWhite(nc.signature[-1]), nc))
+                                    print("Adding driver %s to %s in %s" % (wrapPurple(prop), wrapWhite(mantis_node.signature[-1]), mantis_node))
                                     try:
-                                        nc.drivers[sock].append((prop, val_index))
+                                        mantis_node.drivers[sock].append((prop, val_index))
                                     except:
-                                        nc.drivers[sock] = [(prop, val_index)]
+                                        mantis_node.drivers[sock] = [(prop, val_index)]
                                 else:
                                     getattr(b_object,prop)[val_index] =  value
                         else:
                             # prOrange("Skipping the Thing", getattr(b_object, prop))
-                            safe_setattr(b_object, prop, nc.evaluate_input(sock))
+                            safe_setattr(b_object, prop, mantis_node.evaluate_input(sock))
                     except Exception as e:
-                        prRed(b_object, nc, prop, sock, nc.evaluate_input(sock))
+                        prRed(b_object, mantis_node, prop, sock, mantis_node.evaluate_input(sock))
                         raise e
 
-def finish_driver(nc, b_object, driver_item, prop):
-    # prWhite(nc, prop)
+def finish_driver(mantis_node, b_object, driver_item, prop):
+    # prWhite(mantis_node, prop)
     index = driver_item[1]; driver_sock = driver_item[0]
-    driver_trace = trace_single_line(nc, driver_sock)
+    driver_trace = trace_single_line(mantis_node, driver_sock)
     driver_provider, driver_socket = driver_trace[0][-1], driver_trace[1]
     if index is not None:
         driver = driver_provider.parameters[driver_socket.name][index].copy()
@@ -240,7 +272,7 @@ def finish_driver(nc, b_object, driver_item, prop):
     else:
         driver = driver_provider.parameters[driver_socket.name].copy()
     if driver:
-        if ("." in prop) and nc.__class__.__name__ != "DeformerMorphTargetDeform": # this is a property of a property...
+        if ("." in prop) and mantis_node.__class__.__name__ != "DeformerMorphTargetDeform": # this is a property of a property...
             sub_props = [b_object]
             while ("." in prop):
                 split_prop = prop.split(".")
@@ -253,15 +285,15 @@ def finish_driver(nc, b_object, driver_item, prop):
                 else:
                     sub_props.append(getattr(sub_props[-1], sub_prop))
             driver["owner"] = sub_props[-1]
-        elif nc.node_type in ['XFORM',] and nc.__class__.__name__ in ['xFormBone']:
+        elif mantis_node.node_type in ['XFORM',] and mantis_node.__class__.__name__ in ['xFormBone']:
             # TODO: I really shouldn't have to hardcode this. Look into better solutions.
             if prop in ['hide', 'show_wire']: # we need to get the bone, not the pose bone.
-                bone_col = nc.bGetParentArmature().data.bones
+                bone_col = mantis_node.bGetParentArmature().data.bones
             else:
-                bone_col = nc.bGetParentArmature().pose.bones
+                bone_col = mantis_node.bGetParentArmature().pose.bones
             driver["owner"] = bone_col[b_object] # we use "unsafe" brackets instead of get() because we want to see any errors that occur
         # HACK having special cases here is indicitave of a deeper problem that should be refactored
-        elif nc.__class__.__name__ in ['xFormCurvePin'] and \
+        elif mantis_node.__class__.__name__ in ['xFormCurvePin'] and \
                       prop in ['offset_factor', 'forward_axis', 'up_axis']:
                 driver["owner"] = b_object.constraints['Curve Pin']
         else:
@@ -276,21 +308,21 @@ def finish_driver(nc, b_object, driver_item, prop):
         prRed("Failed to create driver for %s" % prop)
         return None
 
-def finish_drivers(nc):
+def finish_drivers(mantis_node):
     drivers = []
-    if not hasattr(nc, "drivers"):
+    if not hasattr(mantis_node, "drivers"):
         return # HACK
-    for driver_item, prop in nc.drivers.items():
-        b_objects = [nc.bObject]
-        if nc.node_type == 'LINK':
-            b_objects = nc.bObject # this is already a list
+    for driver_item, prop in mantis_node.drivers.items():
+        b_objects = [mantis_node.bObject]
+        if mantis_node.node_type == 'LINK':
+            b_objects = mantis_node.bObject # this is already a list
         for b_object in b_objects:
             if isinstance(prop, list):
                 for sub_item in prop:
-                        drivers.append(finish_driver(nc, b_object, (driver_item, sub_item[1]), sub_item[0]))
+                        drivers.append(finish_driver(mantis_node, b_object, (driver_item, sub_item[1]), sub_item[0]))
                 else:
-                    drivers.append(finish_driver(nc, b_object, (driver_item, sub_item[1]), sub_item[0]))
+                    drivers.append(finish_driver(mantis_node, b_object, (driver_item, sub_item[1]), sub_item[0]))
             else:
-                drivers.append(finish_driver(nc, b_object, driver_item, prop))
+                drivers.append(finish_driver(mantis_node, b_object, driver_item, prop))
     from .drivers import CreateDrivers
     CreateDrivers(drivers)

+ 1 - 1
ops_nodegroup.py

@@ -291,7 +291,7 @@ class ExecuteNodeTree(Operator):
             from pstats import SortKey
             with cProfile.Profile() as pr:
                 tree.update_tree(context, error_popups = pass_error)
-                tree.execute_tree(context, error_popups = pass_error)
+                tree.execute_tree(context, error_popups = pass_error, profile=True)
                 # from the Python docs at https://docs.python.org/3/library/profile.html#module-cProfile
                 s = io.StringIO()
                 sortby = SortKey.TIME

+ 1 - 1
primitives_nodes.py

@@ -1,4 +1,4 @@
-from .node_container_common import *
+from .node_common import *
 from .base_definitions import MantisNode, NodeSocket
 
 def TellClasses():

+ 148 - 132
readtree.py

@@ -1,93 +1,91 @@
 from .utilities import prRed, prGreen, prPurple, prWhite, prOrange, \
                         wrapRed, wrapGreen, wrapPurple, wrapWhite, wrapOrange
 
-    
-
 
-def grp_node_reroute_common(nc, nc_to, all_nc):
+def grp_node_reroute_common(mantis_node, to_mantis_node, all_mantis_nodes):
     # we need to do this: go  to the to-node
     # then reroute the link in the to_node all the way to the beginning
     # so that the number of links in "real" nodes is unchanged
     # then the links in the dummy nodes need to be deleted
-    for inp_name, inp in nc.inputs.items():
+    for inp_name, inp in mantis_node.inputs.items():
         # assume each input socket only has one input for now
         if inp.is_connected:
             while (inp.links):
                 in_link = inp.links.pop()
-                from_nc = in_link.from_node
+                from_mantis_node = in_link.from_node
                 from_socket = in_link.from_socket
                 links = []
-                from_links = from_nc.outputs[from_socket].links.copy()
+                from_links = from_mantis_node.outputs[from_socket].links.copy()
                 while(from_links):
                     from_link = from_links.pop()
                     if from_link == in_link:
                         from_link.die()
                         continue # DELETE the dummy node link 
                     links.append(from_link)
-                from_nc.outputs[from_socket].links = links
-                down = nc_to.outputs[inp_name]
+                from_mantis_node.outputs[from_socket].links = links
+                down = to_mantis_node.outputs[inp_name]
                 for downlink in down.links:
-                    downlink.from_node = from_nc
+                    downlink.from_node = from_mantis_node
                     downlink.from_socket = from_socket
-                    from_nc.outputs[from_socket].links.append(downlink)
+                    from_mantis_node.outputs[from_socket].links.append(downlink)
                     if hasattr(downlink.to_node, "reroute_links"):
-                        downlink.to_node.reroute_links(downlink.to_node, all_nc)
+                        downlink.to_node.reroute_links(downlink.to_node, all_mantis_nodes)
                 in_link.die()
 
-def reroute_links_grp(nc, all_nc):
-    if nc.inputs:
-        if (nc_to := all_nc.get( ( *nc.signature, "NodeGroupInput") )):
-            grp_node_reroute_common(nc, nc_to, all_nc)
+def reroute_links_grp(mantis_node, all_mantis_nodes):
+    if mantis_node.inputs:
+        if (to_mantis_node := all_mantis_nodes.get( ( *mantis_node.signature, "NodeGroupInput") )):
+            grp_node_reroute_common(mantis_node, to_mantis_node, all_mantis_nodes)
         else:
             raise RuntimeError("internal error: failed to enter a node group ")
 
-def reroute_links_grpout(nc, all_nc):
-    if (nc_to := all_nc.get( ( *nc.signature[:-1],) )):
-        grp_node_reroute_common(nc, nc_to, all_nc)
+def reroute_links_grpout(mantis_node, all_mantis_nodes):
+    if (to_mantis_node := all_mantis_nodes.get( ( *mantis_node.signature[:-1],) )):
+        grp_node_reroute_common(mantis_node, to_mantis_node, all_mantis_nodes)
     else:
         raise RuntimeError("error leaving a node group (maybe you are running the tree from inside a node group?)")
 
 # FIXME I don't think these signatures are unique.
-def insert_lazy_parents(nc):
+def insert_lazy_parents(mantis_node):
     from .link_nodes import LinkInherit
     from .base_definitions import NodeLink
-    inherit_nc = None
-    if nc.inputs["Relationship"].is_connected:
-        link = nc.inputs["Relationship"].links[0]
-        # print(nc)
-        from_nc = link.from_node
-        if from_nc.node_type in ["XFORM"] and link.from_socket in ["xForm Out"]:
-            inherit_nc = LinkInherit(("MANTIS_AUTOGENERATED", *nc.signature[1:], "LAZY_INHERIT"), nc.base_tree)
-            for from_link in from_nc.outputs["xForm Out"].links:
-                if from_link.to_node == nc and from_link.to_socket == "Relationship":
+    inherit_mantis_node = None
+    if mantis_node.inputs["Relationship"].is_connected:
+        link = mantis_node.inputs["Relationship"].links[0]
+        # print(mantis_node)
+        from_mantis_node = link.from_node
+        if from_mantis_node.node_type in ["XFORM"] and link.from_socket in ["xForm Out"]:
+            inherit_mantis_node = LinkInherit(("MANTIS_AUTOGENERATED", *mantis_node.signature[1:], "LAZY_INHERIT"), mantis_node.base_tree)
+            for from_link in from_mantis_node.outputs["xForm Out"].links:
+                if from_link.to_node == mantis_node and from_link.to_socket == "Relationship":
                     break # this is it
-            from_link.to_node = inherit_nc; from_link.to_socket="Parent"
+            from_link.to_node = inherit_mantis_node; 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()
-                if to_link.from_node == from_nc and to_link.from_socket == "xForm Out":
+            while (mantis_node.inputs["Relationship"].links):
+                to_link = mantis_node.inputs["Relationship"].links.pop()
+                if to_link.from_node == from_mantis_node and to_link.from_socket == "xForm Out":
                     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)
+            mantis_node.inputs["Relationship"].links=links
+            link=NodeLink(from_node=inherit_mantis_node, from_socket="Inheritance", to_node=mantis_node, to_socket="Relationship")
+            inherit_mantis_node.inputs["Parent"].links.append(from_link)
             
-            inherit_nc.parameters = {
+            inherit_mantis_node.parameters = {
                                      "Parent":None,
                                      "Inherit Rotation":True,
                                      "Inherit Scale":'FULL',
                                      "Connected":False,
                                     }
             # because the from node may have already been done.
-            init_connections(from_nc)
-            init_dependencies(from_nc)
-            init_connections(inherit_nc)
-            init_dependencies(inherit_nc)
-    return inherit_nc
+            init_connections(from_mantis_node)
+            init_dependencies(from_mantis_node)
+            init_connections(inherit_mantis_node)
+            init_dependencies(inherit_mantis_node)
+    return inherit_mantis_node
 
 # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** #
 #                                  DATA FROM NODES                                  #
@@ -97,10 +95,10 @@ from .base_definitions import replace_types, NodeSocket
 
 def autogen_node(base_tree, ui_socket, signature, mContext):
     mantis_node=None
-    from .utilities import  gen_nc_input_for_data
-    # nc_cls = gen_nc_input_for_data(ui_socket)
-    # if (nc_cls):
-    from .internal_containers import AutoGenNode
+    from .utilities import  gen_mantis_node_input_for_data
+    # mantis_class = gen_mantis_node_input_for_data(ui_socket)
+    # if (mantis_class):
+    from .internal_nodes import AutoGenNode
     mantis_node = AutoGenNode(signature, base_tree)
     mantis_node.mContext = mContext
     mantis_node.outputs.init_sockets([ui_socket.name])
@@ -109,31 +107,31 @@ def autogen_node(base_tree, ui_socket, signature, mContext):
 
 # TODO: investigate whether I can set the properties in the downstream nodes directly.
 #       I am doing this in Schema Solver and it seems to work quite efficiently.
-def make_connections_to_ng_dummy(base_tree, tree_path_names, local_nc, all_nc, nc_to):
+def make_connections_to_ng_dummy(base_tree, tree_path_names, local_mantis_nodes, all_mantis_nodes, to_mantis_node):
     from .socket_definitions import no_default_value
-    for inp in nc_to.prototype.inputs:
+    for inp in to_mantis_node.ui_node.inputs:
         if inp.bl_idname in no_default_value:
             continue
-        nc_from = None
+        from_mantis_node = None
         to_s = inp.identifier
         if not inp.is_linked: # make an autogenerated NC for the inputs of the group node
             # This can be run inside schema. Make it unique with uuid() to be safe.
             from uuid import uuid4
-            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
+            signature = ("MANTIS_AUTOGENERATED", *tree_path_names, to_mantis_node.ui_signature[-1], inp.name, inp.identifier, str(uuid4()))
+            from_mantis_node = all_mantis_nodes.get(signature) # creating this without checking and
             #  using UUID signature leads to TERRIBLE CONFUSING BUGS.
-            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.
-                nc_from.parameters = {inp.name:get_socket_value(inp)}
-                local_nc[signature] = nc_from; all_nc[signature] = nc_from
-                nc_from.outputs[inp.name].connect(node=nc_to, socket=to_s, sort_id=0)
+            if from_mantis_node is None: 
+                from_mantis_node = autogen_node(base_tree, inp, signature, to_mantis_node.mContext)
+            from .node_common import get_socket_value
+            if from_mantis_node: # autogen can fail and we should catch it.
+                from_mantis_node.parameters = {inp.name:get_socket_value(inp)}
+                local_mantis_nodes[signature] = from_mantis_node; all_mantis_nodes[signature] = from_mantis_node
+                from_mantis_node.outputs[inp.name].connect(node=to_mantis_node, socket=to_s, sort_id=0)
             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 ):
-    from .internal_containers import DummyNode
+def gen_mantis_nodes(base_tree, current_tree, tree_path_names, all_mantis_nodes, local_mantis_nodes, dummy_nodes, group_nodes, schema_nodes ):
+    from .internal_nodes 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.
         if hasattr(ui_node, 'initialized'): ui_node.initialized=True
@@ -144,41 +142,41 @@ def gen_node_containers(base_tree, current_tree, tree_path_names, all_nc, local_
             # we only want ONE dummy in/out per tree_path, so use the bl_idname to make a Dummy node
             sig = (None, *tree_path_names, ui_node.bl_idname)
             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 )
-                local_nc[sig] = nc; all_nc[sig] = nc; dummy_nodes[sig] = nc
+            if not local_mantis_nodes.get(sig):
+                mantis_node = DummyNode( signature=sig , base_tree=base_tree, ui_node=ui_node, ui_signature=ui_sig )
+                local_mantis_nodes[sig] = mantis_node; all_mantis_nodes[sig] = mantis_node; dummy_nodes[sig] = mantis_node
                 if ui_node.bl_idname in ["NodeGroupOutput"]:
-                    nc.reroute_links = reroute_links_grpout
+                    mantis_node.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 )
-            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)
+            mantis_node = DummyNode( signature= (sig := (None, *tree_path_names, ui_node.name) ), base_tree=base_tree, ui_node=ui_node )
+            local_mantis_nodes[sig] = mantis_node; all_mantis_nodes[sig] = mantis_node; dummy_nodes[sig] = mantis_node
+            make_connections_to_ng_dummy(base_tree, tree_path_names, local_mantis_nodes, all_mantis_nodes, mantis_node)
             if ui_node.bl_idname == "MantisNodeGroup":
-                group_nodes.append(nc)
-                nc.reroute_links = reroute_links_grp
+                group_nodes.append(mantis_node)
+                mantis_node.reroute_links = reroute_links_grp
             else:
-                group_nodes.append(nc)
-                schema_nodes[sig] = nc
+                group_nodes.append(mantis_node)
+                schema_nodes[sig] = mantis_node
         # if it wasn't the types we ignore or the types we make a Dummy for, use this to catch all non-special cases.
-        elif (nc_cls := ui_node.mantis_class):
+        elif (mantis_class := ui_node.mantis_class):
             sig = (None, *tree_path_names, ui_node.name)
             if ui_node.bl_idname in replace_types:
                 sig = (None, *tree_path_names, ui_node.bl_idname)
-                if local_nc.get(sig):
+                if local_mantis_nodes.get(sig):
                     continue # already made
-            nc = nc_cls( sig , base_tree)
-            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.
+            mantis_node = mantis_class( sig , base_tree)
+            local_mantis_nodes[sig] = mantis_node; all_mantis_nodes[sig] = mantis_node
+            mantis_node.ui_signature = (*mantis_node.ui_signature[:-1], ui_node.name) # just to ensure it points to a real node.
         else:
-            nc = None
-            prRed(f"Can't make nc for.. {ui_node.bl_idname}")
+            mantis_node = None
+            prRed(f"Can't make mantis_node for.. {ui_node.bl_idname}")
         # this should be done at init
-        if nc.signature[0] not in ['MANTIS_AUTOGENERATED'] and nc.node_type not in ['SCHEMA', 'DUMMY', 'DUMMY_SCHEMA']:
-            nc.fill_parameters()
+        if mantis_node.signature[0] not in ['MANTIS_AUTOGENERATED'] and mantis_node.node_type not in ['SCHEMA', 'DUMMY', 'DUMMY_SCHEMA']:
+            mantis_node.fill_parameters()
 
-def data_from_tree(base_tree, tree_path, dummy_nodes, all_nc, all_schema):#
+def data_from_tree(base_tree, tree_path, dummy_nodes, all_mantis_nodes, all_schema):#
     # TODO: it should be relatively easy to make this use a while loop instead of recursion.
-    local_nc, group_nodes = {}, []
+    local_mantis_nodes, group_nodes = {}, []
     tree_path_names = [tree.name for tree in tree_path if hasattr(tree, "name")]
     if tree_path[-1]:
         current_tree = tree_path[-1].node_tree # this may be None.
@@ -188,15 +186,15 @@ 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, all_mantis_nodes, local_mantis_nodes, dummy_nodes, group_nodes, all_schema)
         
-        from .utilities import link_node_containers
+        from .utilities import link_mantis_nodes
         for link in links:
-            link_node_containers((None, *tree_path_names), link, local_nc)
+            link_mantis_nodes((None, *tree_path_names), link, local_mantis_nodes)
         # 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)
-    return dummy_nodes, all_nc, all_schema
+        for mantis_node in group_nodes:
+            data_from_tree(base_tree, tree_path+[mantis_node.ui_node], dummy_nodes, all_mantis_nodes, all_schema)
+    return dummy_nodes, all_mantis_nodes, all_schema
 
 from .utilities import check_and_add_root, init_connections, init_dependencies, init_schema_dependencies
 
@@ -206,29 +204,29 @@ def is_signature_in_other_signature(parent_signature, child_signature):
         return False
     return parent_signature[0:] == child_signature[:len(parent_signature)]
 
-def solve_schema_to_tree(nc, all_nc, roots=[], error_popups=False):
-    from .utilities import get_node_prototype
-    np = get_node_prototype(nc.signature, nc.base_tree)
+def solve_schema_to_tree(mantis_node, all_mantis_nodes, roots=[], error_popups=False):
+    from .utilities import get_ui_node
+    np = get_ui_node(mantis_node.signature, mantis_node.base_tree)
     from .schema_solve import SchemaSolver
-    solver = SchemaSolver(nc, all_nc.copy(), np, error_popups=error_popups)
+    solver = SchemaSolver(mantis_node, all_mantis_nodes.copy(), np, error_popups=error_popups)
     try:
         solved_nodes = solver.solve()
     except Exception as e:
     #     # the schema will run the error cleanup code, we just need to raise or not
         solved_nodes = {}
-        nc.base_tree.hash=''
-        raise execution_error_cleanup(nc, e, show_error=error_popups)
+        mantis_node.base_tree.hash=''
+        raise execution_error_cleanup(mantis_node, e, show_error=error_popups)
 
     # maybe this should be done in schema solver. TODO invesitigate a more efficient way
     del_me = []
-    for k, v in all_nc.items():
-        # delete all the schema's prototype and interface nodes. The links have already been deleted by the solver.
-        if v.signature[0] not in ['MANTIS_AUTOGENERATED'] and is_signature_in_other_signature(nc.signature, k):
+    for k, v in all_mantis_nodes.items():
+        # delete all the schema's ui_node and interface nodes. The links have already been deleted by the solver.
+        if v.signature[0] not in ['MANTIS_AUTOGENERATED'] and is_signature_in_other_signature(mantis_node.signature, k):
             del_me.append(k)
     for k in del_me:
-        del all_nc[k]
+        del all_mantis_nodes[k]
     for k,v in solved_nodes.items():
-        all_nc[k]=v
+        all_mantis_nodes[k]=v
         init_connections(v)
         check_and_add_root(v, roots, include_non_hierarchy=True)
 
@@ -277,7 +275,7 @@ def get_schema_length_dependencies(node, all_nodes={}):
     # this way we can handle Schema and Array Get nodes with one function
     extend_dependencies_from_inputs(node)
     if node.node_type == 'DUMMY_SCHEMA':
-        trees = [(node.prototype.node_tree, node.signature)] # this is UI data
+        trees = [(node.ui_node.node_tree, node.signature)] # this is UI data
         while trees:
             tree, tree_signature = trees.pop()
             for sub_ui_node in tree.nodes:
@@ -289,7 +287,7 @@ def get_schema_length_dependencies(node, all_nodes={}):
                     sub_node = all_nodes[(*tree_signature, sub_ui_node.name)]
                 if sub_node.node_type == 'DUMMY_SCHEMA':
                     extend_dependencies_from_inputs(sub_node)
-                    trees.append((sub_node.prototype.node_tree, sub_node.signature))
+                    trees.append((sub_node.ui_node.node_tree, sub_node.signature))
     return list(filter(deps_filter, deps))
 
 
@@ -306,7 +304,7 @@ def parse_tree(base_tree, error_popups=False):
     #  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={})
+    dummy_nodes, all_mantis_nodes, all_schema =  data_from_tree(base_tree, tree_path = [None], dummy_nodes = {}, all_mantis_nodes = {}, 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)
@@ -323,7 +321,7 @@ def parse_tree(base_tree, error_popups=False):
         # add the Mantis Context here, so that it available during parsing.
         mantis_node.mContext = mContext
         if mantis_node.node_type in ["DUMMY"]: # clean up the groups
-            if mantis_node.prototype.bl_idname in ("MantisNodeGroup", "NodeGroupOutput"):
+            if mantis_node.ui_node.bl_idname in ("MantisNodeGroup", "NodeGroupOutput"):
                 continue
         # Initialize the dependencies and connections (from/to links) for each node.
         # we record & store it because using a getter is much slower (according to profiling)
@@ -409,18 +407,18 @@ def parse_tree(base_tree, error_popups=False):
     # I had a problem with this looping forever. I think it is resolved... but I don't know lol
 
     all_mantis_nodes = list(all_mantis_nodes.values())
-    kept_nc = {}
+    kept_mantis_node = {}
     while (all_mantis_nodes):
-        nc = all_mantis_nodes.pop()
-        if nc in array_nodes:
+        mantis_node = all_mantis_nodes.pop()
+        if mantis_node 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 mantis_node.node_type in ["DUMMY", 'SCHEMA', 'DUMMY_SCHEMA']:
+            continue # screen out the ui_node schema nodes, group in/out, and group placeholders
         # cleanup autogen nodes
-        if nc.signature[0] == "MANTIS_AUTOGENERATED" and len(nc.inputs) == 0 and len(nc.outputs) == 1:
+        if mantis_node.signature[0] == "MANTIS_AUTOGENERATED" and len(mantis_node.inputs) == 0 and len(mantis_node.outputs) == 1:
             from .base_definitions import can_remove_socket_for_autogen
-            output=list(nc.outputs.values())[0]
-            value=list(nc.parameters.values())[0]   # IDEA modify the dependecy get function to exclude these nodes completely
+            output=list(mantis_node.outputs.values())[0]
+            value=list(mantis_node.parameters.values())[0]   # IDEA modify the dependecy get function to exclude these nodes completely
             keep_me = False
             for l in output.links:
                 to_node = l.to_node; to_socket = l.to_socket
@@ -433,17 +431,17 @@ def parse_tree(base_tree, error_popups=False):
                 init_dependencies(to_node) # to remove the autogen node we no longer need.
             if not keep_me:
                 continue
-            init_connections(nc) # because we have removed many connections.
+            init_connections(mantis_node) # because we have removed many connections.
 
-        if (nc.node_type in ['XFORM']) and ("Relationship" in nc.inputs.keys()):
-            if (new_nc := insert_lazy_parents(nc)):
-                kept_nc[new_nc.signature]=new_nc
+        if (mantis_node.node_type in ['XFORM']) and ("Relationship" in mantis_node.inputs.keys()):
+            if (new_mantis_node := insert_lazy_parents(mantis_node)):
+                kept_mantis_node[new_mantis_node.signature]=new_mantis_node
                 # be sure to add the Mantis context.
-                new_nc.mContext =mContext
-        kept_nc[nc.signature]=nc
+                new_mantis_node.mContext =mContext
+        kept_mantis_node[mantis_node.signature]=mantis_node
     prWhite(f"Parsing tree took {time.time()-start_time} seconds.")
-    prWhite("Number of Nodes: %s" % (len(kept_nc)))
-    return kept_nc
+    prWhite("Number of Nodes: %s" % (len(kept_mantis_node)))
+    return kept_mantis_node
 
 from .utilities import switch_mode
 
@@ -487,7 +485,7 @@ def execution_error_cleanup(node, exception, switch_objects = [], show_error=Fal
 def sort_execution(nodes, xForm_pass):
     execution_failed=False
     sorted_nodes = []
-    from .node_container_common import GraphError
+    from .node_common import GraphError
     # check for cycles here by keeping track of the number of times a node has been visited.
     visited={}
     check_max_len=len(nodes)**2 # seems too high but safe. In a well-ordered graph, I guess this number should be less than the number of nodes.
@@ -536,37 +534,45 @@ def sort_execution(nodes, xForm_pass):
                         xForm_pass.appendleft(conn)
     return sorted_nodes, execution_failed
 
-def execute_tree(nodes, base_tree, context, error_popups = False):
+def execute_tree(nodes, base_tree, context, error_popups = False, profile=False):
     assert nodes is not None, "Failed to parse tree."
     assert len(nodes) > 0, "No parsed nodes for execution."\
                            " Mantis probably failed to parse the tree."
     import bpy
     from time import time
-    from .node_container_common import GraphError
+    from .node_common import GraphError
     original_active = context.view_layer.objects.active
     start_execution_time = time()
     mContext = None
     from collections import deque
     xForm_pass = deque()
-    for nc in nodes.values():
+    for mantis_node in nodes.values():
         if not mContext: # just grab one of these. this is a silly way to do this.
-            mContext = nc.mContext
+            mContext = mantis_node.mContext
             mContext.b_objects = {} # clear the objects and recreate them
-        nc.reset_execution()
-        check_and_add_root(nc, xForm_pass)
+        mantis_node.reset_execution()
+        check_and_add_root(mantis_node, xForm_pass)
     mContext.execution_failed = False
 
     switch_me = [] # switch the mode on these objects
     active = None # only need it for switching modes
     select_me = []
+    profiler = None
+    if profile:
+        from .dev_helpers.profile_nodes import NodeProfiler
+        profiler = NodeProfiler()
+        profiler.new_session(mContext.execution_id)
+        sort_key = 'total'
     try:
         sorted_nodes, execution_failed = sort_execution(nodes, xForm_pass)
         for n in sorted_nodes:
             try:
                 if not n.prepared:
-                    n.bPrepare(context)
+                    if profiler: profiler.record(n, "bPrepare", context)
+                    else: n.bPrepare(context)
                 if not n.executed:
-                    n.bTransformPass(context)
+                    if profiler: profiler.record(n, "bTransformPass", context)
+                    else: n.bTransformPass(context)
                 if (n.__class__.__name__ == "xFormArmature" ):
                     ob = n.bGetObject()
                     switch_me.append(ob)
@@ -585,10 +591,11 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
 
         for n in sorted_nodes:
             try:
-                if not n.prepared:
-                    n.bPrepare(context)
+                if profiler: profiler.record(n, "bPrepare", context)
+                else: n.bPrepare(context)
                 if not n.executed:
-                    n.bRelationshipPass(context)
+                    if profiler: profiler.record(n, "bRelationshipPass", context)
+                    else: n.bRelationshipPass(context)
             except Exception as e:
                 e = execution_error_cleanup(n, e, show_error=error_popups)
                 if error_popups == False:
@@ -606,7 +613,8 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
 
         for n in sorted_nodes:
             try:
-                n.bFinalize(context)
+                if profiler: profiler.record(n, "bFinalize", context)
+                else: n.bFinalize(context)
             except Exception as e:
                 e = execution_error_cleanup(n, e, show_error=error_popups)
                 if error_popups == False:
@@ -621,7 +629,8 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
         # finally, apply modifiers and bind stuff
         for n in sorted_nodes:
             try:
-                n.bModifierApply(context)
+                if profiler: profiler.record(n, "bModifierApply", context)
+                else: n.bModifierApply(context)
             except Exception as e:
                 e = execution_error_cleanup(n, e, show_error=error_popups)
                 if error_popups == False:
@@ -634,6 +643,13 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
         tot_time = (time() - start_execution_time)
         if not execution_failed:
             prGreen(f"Executed tree of {len(sorted_nodes)} nodes in {tot_time} seconds")
+        if profiler:
+            from .dev_helpers.profile_nodes import summarize_profile
+            summarize_profile(profiler._current, pass_name='bPrepare', sort_key = sort_key)
+            summarize_profile(profiler._current, pass_name='bTransformPass', sort_key = sort_key)
+            summarize_profile(profiler._current, pass_name='bRelationshipPass', sort_key = sort_key)
+            summarize_profile(profiler._current, pass_name='bFinalize', sort_key = sort_key)
+            summarize_profile(profiler._current, pass_name='bModifierApply', sort_key = sort_key)
         if (original_active):
             context.view_layer.objects.active = original_active
             original_active.select_set(True)

+ 9 - 9
schema_nodes.py

@@ -1,4 +1,4 @@
-from .node_container_common import *
+from .node_common import *
 from math import pi, tau
 from .base_definitions import MantisNode, NodeSocket
 
@@ -15,20 +15,20 @@ def TellClasses():
         SchemaIncomingConnection,
     ]
 
-def schema_init_sockets(nc, is_input = True, in_out='INPUT', category=''):
-    from .utilities import tree_from_nc
-    parent_tree = tree_from_nc(nc.signature, nc.base_tree)
+def schema_init_sockets(mantis_node, is_input = True, in_out='INPUT', category=''):
+    from .utilities import tree_from_mantis_node
+    parent_tree = tree_from_mantis_node(mantis_node.signature, mantis_node.base_tree)
     if is_input:
-        sockets=nc.inputs
+        sockets=mantis_node.inputs
     else:
-        sockets=nc.outputs
+        sockets=mantis_node.outputs
     if category in ['Constant', 'Array', 'Connection']:
         for item in parent_tree.interface.items_tree:
             if item.item_type == 'PANEL': continue
             if item.parent and item.parent.name == category:
                 if item.in_out == in_out:
                     sockets.init_sockets([item.name])
-    nc.init_parameters()
+    mantis_node.init_parameters()
 
 
 class SchemaNode(MantisNode):
@@ -87,7 +87,7 @@ class SchemaConstInput(SchemaNode):
         if parent_schema_node:
             # this allows us to generate the Constant Input from a normal Node Group
             # and treat the node group as a schema
-            parent_tree = parent_schema_node.prototype.node_tree
+            parent_tree = parent_schema_node.ui_node.node_tree
             sockets=self.outputs
             for item in parent_tree.interface.items_tree:
                 if item.item_type == 'PANEL': continue
@@ -109,7 +109,7 @@ class SchemaConstOutput(SchemaNode):
         if parent_schema_node:
             # this allows us to generate the Constant Input from a normal Node Group
             # and treat the node group as a schema
-            parent_tree = parent_schema_node.prototype.node_tree
+            parent_tree = parent_schema_node.ui_node.node_tree
             sockets=self.inputs
             for item in parent_tree.interface.items_tree:
                 if item.item_type == 'PANEL': continue

+ 62 - 62
schema_solve.py

@@ -5,19 +5,19 @@ from .utilities import (prRed, prGreen, prPurple, prWhite,
 from .utilities import init_connections, init_dependencies, get_link_in_out
 from .base_definitions import (SchemaUINode, custom_props_types, \
     MantisNodeGroup, SchemaGroup, replace_types, GraphError)
-from .node_container_common import setup_custom_props_from_np
+from .node_common import setup_custom_props_from_np
 # a class that solves Schema nodes
 from bpy.types import NodeGroupInput, NodeGroupOutput
 
 from .readtree import execution_error_cleanup
 
 class SchemaSolver:
-    def __init__(self, schema_dummy, nodes, prototype, signature=None, error_popups=False):
+    def __init__(self, schema_dummy, nodes, ui_node, signature=None, error_popups=False):
         self.all_nodes = nodes # this is the parsed tree from Mantis
         self.node = schema_dummy
         self.node.solver = self
         self.solved = False
-        self.tree = prototype.node_tree
+        self.tree = ui_node.node_tree
         self.uuid = self.node.uuid
         self.error_popups = error_popups
         if signature:
@@ -38,7 +38,7 @@ class SchemaSolver:
         self.tree_path_names  = [*self.node.signature] # same tree as the schema node
         self.autogen_path_names = ['SCHEMA_AUTOGENERATED', *self.node.signature[1:]]
         self.is_node_group = False
-        if self.node.prototype.bl_idname == "MantisNodeGroup":
+        if self.node.ui_node.bl_idname == "MantisNodeGroup":
             self.is_node_group = True
         if self.node.inputs['Schema Length'].links:
             self.index_link = self.node.inputs['Schema Length'].links[0]
@@ -150,43 +150,43 @@ class SchemaSolver:
         return len(compare_node.signature) < len(queried_node.signature)
 
     def gen_solve_iteration_mantis_nodes(self, frame_mantis_nodes, unprepared):
-        for prototype_ui_node in self.tree.nodes:
-            mantis_node_name = prototype_ui_node.name
+        for ui_node in self.tree.nodes:
+            mantis_node_name = ui_node.name
             index_str = self.index_str()
             mContext=self.node.mContext
-            if isinstance(prototype_ui_node, SchemaUINode):
+            if isinstance(ui_node, SchemaUINode):
                 continue  # IGNORE the schema interface nodes, we already made them in __init__()
                 # they are reused for each iteration.
-            elif prototype_ui_node.bl_idname in ['NodeFrame', 'NodeReroute']:
+            elif ui_node.bl_idname in ['NodeFrame', 'NodeReroute']:
                 continue  # IGNORE stuff that is purely UI - frames, reroutes.
-            elif prototype_ui_node.bl_idname in ['NodeGroupInput', 'NodeGroupOutput']:
+            elif ui_node.bl_idname in ['NodeGroupInput', 'NodeGroupOutput']:
                 continue # we converted these to Schema Nodes because they represent a Group input.
             signature = (*self.autogen_path_names, mantis_node_name+index_str)
             ui_signature=(*self.signature, mantis_node_name)
             prototype_mantis_node = self.all_nodes[ui_signature]
             # the prototype_mantis_node was generated inside the schema when we parsed the tree.
-            # it is the prototype of the mantis node which we make for this iteration
-            # for Schema sub-nodes ... they need a prototype to init.
+            # it is the ui_node of the mantis node which we make for this iteration
+            # for Schema sub-nodes ... they need a ui_node to init.
             if prototype_mantis_node.node_type in ['DUMMY', 'DUMMY_SCHEMA']:
-                # We stored the prototype ui_node when creating the Mantis node.
-                ui_node = prototype_mantis_node.prototype
+                # We stored the ui_node ui_node when creating the Mantis node.
+                ui_node = prototype_mantis_node.ui_node
                 # if prototype_mantis_node is a group or schema: TODO changes are needed elsewhere to make this easier to read. LEGIBILITY
                 if ui_node.bl_idname in ["MantisNodeGroup", "MantisSchemaGroup"]:
                     mantis_node = prototype_mantis_node.__class__(
-                        signature, prototype_mantis_node.base_tree, prototype=ui_node,
+                        signature, prototype_mantis_node.base_tree, ui_node=ui_node,
                         ui_signature =  prototype_mantis_node.signature)
-                    # now let's copy the links from the prototype node
+                    # now let's copy the links from the ui_node node
                     if ui_node.bl_idname in ["MantisNodeGroup"]:
                         mantis_node.prepared = False
                         mantis_node.node_type = 'DUMMY_SCHEMA' # we promote it to a schema for now
                         mantis_node.inputs.init_sockets(['Schema Length']) # add a Schema Length socket
                         mantis_node.parameters['Schema Length'] = 1 # set the length to 1 since it is a single group instance
                         # we'll make the autogenerated nodes for constant inputs. It doesn't matter that there is technically
-                        #  a prototype available for each one  -- these are cheap and I want this to be easy.
+                        #  a ui_node available for each one  -- these are cheap and I want this to be easy.
                         from .readtree import make_connections_to_ng_dummy
                         make_connections_to_ng_dummy(self.node.base_tree, self.autogen_path_names, frame_mantis_nodes, self.all_nodes, mantis_node)
                 else:
-                    mantis_node = prototype_mantis_node.__class__(signature, prototype_mantis_node.base_tree, prototype=ui_node)
+                    mantis_node = prototype_mantis_node.__class__(signature, prototype_mantis_node.base_tree, ui_node=ui_node)
             else:
                 mantis_node = prototype_mantis_node.__class__(signature, prototype_mantis_node.base_tree)
             frame_mantis_nodes[mantis_node.signature] = mantis_node
@@ -195,8 +195,8 @@ class SchemaSolver:
             if mantis_node.prepared == False:
                 unprepared.append(mantis_node)
             if mantis_node.__class__.__name__ in custom_props_types:
-                setup_custom_props_from_np(mantis_node, prototype_ui_node)
-            mantis_node.fill_parameters(prototype_ui_node)
+                setup_custom_props_from_np(mantis_node, ui_node)
+            mantis_node.fill_parameters(ui_node)
             # be sure to pass on the Mantis Context to them
             mantis_node.mContext=mContext
 
@@ -275,7 +275,7 @@ class SchemaSolver:
                 # this connection was forbidden before, right? so this should be a safe assumption.
                 assert to_mantis_node.node_type == 'DUMMY_SCHEMA', "I expected this to be a group/schema"
                 # we need to get the identifier of the named socket now.
-                to_socket_name = to_mantis_node.prototype.inputs[to_socket_name].identifier
+                to_socket_name = to_mantis_node.ui_node.inputs[to_socket_name].identifier
             else:
                 to_mantis_node = frame_mantis_nodes[ to_mantis_node_signature ] # add the index string
 
@@ -341,19 +341,19 @@ class SchemaSolver:
 
     def handle_link_to_array_input_get(self, frame_mantis_nodes, ui_link):
         from_name, to_name = get_link_in_out(ui_link)
-        from_nc = frame_mantis_nodes[(*self.autogen_path_names, from_name+self.index_str())]
-        to_nc = self.schema_nodes[(*self.tree_path_names, to_name)]
+        from_mantis_node = frame_mantis_nodes[(*self.autogen_path_names, from_name+self.index_str())]
+        to_mantis_node = self.schema_nodes[(*self.tree_path_names, to_name)]
         # this only needs to be done once:
         if self.index == 0: # BUG? HACK? TODO find out what is going on here.
             # Kill the link between the schema node group and the node connecting to it
-            old_nc = self.all_nodes[(*self.tree_path_names, from_name)]
+            old_mantis_node = self.all_nodes[(*self.tree_path_names, from_name)]
             # I am not sure about this!
-            existing_link = old_nc.outputs[ui_link.from_socket.name].links[0]
+            existing_link = old_mantis_node.outputs[ui_link.from_socket.name].links[0]
             existing_link.die()
         #
-        connection = from_nc.outputs[ui_link.from_socket.name].connect(node=to_nc, socket=ui_link.to_socket.name)
+        connection = from_mantis_node.outputs[ui_link.from_socket.name].connect(node=to_mantis_node, socket=ui_link.to_socket.name)
         connection.is_hierarchy = True # we have to mark this because links to Schema are not usually hierarchy (?)
-        to_nc.hierarchy_dependencies.append(from_nc); from_nc.hierarchy_connections.append(to_nc)
+        to_mantis_node.hierarchy_dependencies.append(from_mantis_node); from_mantis_node.hierarchy_connections.append(to_mantis_node)
         # TODO: review the wisdom of this default.
 
     def handle_link_from_array_input(self, frame_mantis_nodes, ui_link, index):
@@ -429,7 +429,7 @@ class SchemaSolver:
 
     def handle_link_from_array_type_to_array_out(self, original_ui_link, dummy_link):
         # this is so annoyingly specific lol
-        from_node = dummy_link.nc_from; from_socket_name=dummy_link.from_socket.name
+        from_node = dummy_link.from_mantis_node; from_socket_name=dummy_link.from_socket.name
         for outgoing in self.array_output_connections[original_ui_link.to_socket.identifier]:
             to_node = outgoing.to_node
             connection = from_node.outputs[from_socket_name].connect(node=to_node, socket=outgoing.to_socket)
@@ -472,7 +472,7 @@ class SchemaSolver:
 
             if to_node.node_type == 'DUMMY_SCHEMA' and to_node.prepared:
                 other_stem = ('SCHEMA_AUTOGENERATED', *to_node.signature[1:])
-                other_schema_np = to_node.prototype
+                other_schema_np = to_node.ui_node
                 other_schema_tree = other_schema_np.node_tree
                 for n in other_schema_tree.nodes:
                     if n.bl_idname not in ["SchemaArrayInput", "SchemaArrayInputGet", "SchemaArrayInputAll"]:
@@ -505,7 +505,7 @@ class SchemaSolver:
         for inp in to_node.inputs:
             if inp.name == ui_link.to_socket.name: # the most recent one is from
                 l = inp.links[-1]; break # <----  this index of the outer schema
-        ui_link.from_nc = l.from_node; ui_link.from_socket = l.from_node.outputs[l.from_socket]
+        ui_link.from_mantis_node = l.from_node; ui_link.from_socket = l.from_node.outputs[l.from_socket]
         return ui_link
 
     def test_is_sub_schema(self, other):
@@ -522,32 +522,32 @@ class SchemaSolver:
         e = None
         # forbid some nodes - they aren't necessary to solve the schema & cause problems.
         while unprepared:
-            nc = unprepared.pop()
-            if nc.node_type == 'DUMMY_SCHEMA' and not self.test_is_sub_schema(nc):
-                forbidden.add(nc) # do NOT add this as a dependency.
-            if nc in forbidden: continue # trying to resolve dependencies for.
-            if sum([dep.prepared for dep in nc.hierarchy_dependencies]) == len(nc.hierarchy_dependencies):
+            mantis_node = unprepared.pop()
+            if mantis_node.node_type == 'DUMMY_SCHEMA' and not self.test_is_sub_schema(mantis_node):
+                forbidden.add(mantis_node) # do NOT add this as a dependency.
+            if mantis_node in forbidden: continue # trying to resolve dependencies for.
+            if sum([dep.prepared for dep in mantis_node.hierarchy_dependencies]) == len(mantis_node.hierarchy_dependencies):
                 try:
-                    nc.bPrepare()
-                    if nc.node_type == 'DUMMY_SCHEMA':
-                        self.solve_nested_schema(nc)
+                    mantis_node.bPrepare()
+                    if mantis_node.node_type == 'DUMMY_SCHEMA':
+                        self.solve_nested_schema(mantis_node)
                 except Exception as e:
-                    raise execution_error_cleanup(nc, e, show_error = False)
+                    raise execution_error_cleanup(mantis_node, e, show_error = False)
                     break
-                if not nc.prepared:
-                    raise RuntimeError( f"{nc} has failed to prepare."
+                if not mantis_node.prepared:
+                    raise RuntimeError( f"{mantis_node} has failed to prepare."
                              " Please report this as a bug in mantis." )
             else: # Keeping this for-loop as a fallback, it should never add dependencies though
                 can_add_me = True
-                for dep in nc.hierarchy_dependencies:
+                for dep in mantis_node.hierarchy_dependencies:
                     if not dep.prepared and dep not in unprepared:
                         if dep in forbidden:
                             can_add_me=False
-                            forbidden.add(nc) # forbid the parent, too
+                            forbidden.add(mantis_node) # forbid the parent, too
                             continue
                         unprepared.appendleft(dep)
                 if can_add_me:
-                    unprepared.appendleft(nc) # just rotate them until they are ready.
+                    unprepared.appendleft(mantis_node) # just rotate them until they are ready.
 
 
     def solve_iteration(self):
@@ -570,7 +570,7 @@ class SchemaSolver:
                                         SchemaConstOutput,
                                         SchemaOutgoingConnection,
                                         SchemaIncomingConnection,)
-        from .utilities import clear_reroutes, link_node_containers
+        from .utilities import clear_reroutes, link_mantis_nodes
         from .base_definitions import array_output_types
         self.set_index_strings()
         frame_mantis_nodes = {}
@@ -644,7 +644,7 @@ class SchemaSolver:
                 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,
+            connection = link_mantis_nodes(self.autogen_path_names, ui_link,
                                 frame_mantis_nodes, from_suffix=self.index_str(),
                                 to_suffix=self.index_str())
 
@@ -711,7 +711,7 @@ class SchemaSolver:
                         new_link = DummyLink(
                             from_socket=l.from_node.outputs[l.from_socket],
                             to_socket=l.to_node.inputs[l.to_socket],
-                            nc_from=l.from_node, nc_to=l.to_node,
+                            from_mantis_node=l.from_node, to_mantis_node=l.to_node,
                             multi_input_sort_id=l.multi_input_sort_id
                         )
                         self.handle_link_from_array_type_to_array_out(ui_link, new_link)
@@ -719,29 +719,29 @@ class SchemaSolver:
                     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):
+    def solve_nested_schema(self, schema_mantis_node):
         """ 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().
         """
         solver=None
-        if schema_nc.prepared == False:
+        if schema_mantis_node.prepared == False:
             all_nodes = self.all_nodes.copy()
-            ui_node = schema_nc.prototype
-            length = schema_nc.evaluate_input("Schema Length")
+            ui_node = schema_mantis_node.ui_node
+            length = schema_mantis_node.evaluate_input("Schema Length")
             tree = ui_node.node_tree
-            if schema_nc.prototype.bl_idname == "MantisNodeGroup":
-                prOrange(f"Expanding Node Group {tree.name} in node {schema_nc}.")
+            if schema_mantis_node.ui_node.bl_idname == "MantisNodeGroup":
+                prOrange(f"Expanding Node Group {tree.name} in node {schema_mantis_node}.")
             else:
-                prOrange(f"Expanding schema {tree.name} in node {schema_nc} with length {length}.")
-            solver = SchemaSolver(schema_nc, all_nodes, ui_node, schema_nc.ui_signature, error_popups=self.error_popups)
+                prOrange(f"Expanding schema {tree.name} in node {schema_mantis_node} with length {length}.")
+            solver = SchemaSolver(schema_mantis_node, all_nodes, ui_node, schema_mantis_node.ui_signature, error_popups=self.error_popups)
             solved_nodes = solver.solve()
-            schema_nc.prepared = True
+            schema_mantis_node.prepared = True
             for k,v in solved_nodes.items():
                 self.solved_nodes[k]=v
         return solver
 
 
-    def finalize(self, frame_nc):
+    def finalize(self, mantis_nodes_in_frame):
         from .schema_nodes_ui import (SchemaOutgoingConnection,)
         for i in range(len(self.held_links)):
             link = self.held_links.pop()
@@ -752,7 +752,7 @@ class SchemaSolver:
                     for outgoing in outgoing_links:
                         if outgoing:
                             to_node = outgoing.to_node
-                            from_node =frame_nc[(*self.autogen_path_names, from_np.name+self.index_str()) ]
+                            from_node =mantis_nodes_in_frame[(*self.autogen_path_names, from_np.name+self.index_str()) ]
                             from_socket_name = link.from_socket.name
                             if from_node.node_type in ['DUMMY_SCHEMA']:
                                 from_socket_name = link.from_socket.identifier
@@ -763,8 +763,8 @@ class SchemaSolver:
 
 
         # # solve all unsolved nested schemas...
-        for schema_sig, schema_nc in self.nested_schemas.items():
-                self.solve_nested_schema(schema_nc)
+        for schema_sig, schema_mantis_node in self.nested_schemas.items():
+                self.solve_nested_schema(schema_mantis_node)
 
 
         for n in self.autogenerated_nodes.values():
@@ -806,9 +806,9 @@ class SchemaSolver:
         for index in range(self.solve_length):
             self.index = index
             frame_mantis_nodes = self.solve_iteration()
-            for sig, nc in frame_mantis_nodes.items():
-                if nc.node_type == 'DUMMY_SCHEMA':
-                    self.nested_schemas[sig] = nc
+            for sig, mantis_node in frame_mantis_nodes.items():
+                if mantis_node.node_type == 'DUMMY_SCHEMA':
+                    self.nested_schemas[sig] = mantis_node
         self.finalize(frame_mantis_nodes)
         self.solved = True
         self.node.prepared = True

+ 2 - 2
socket_definitions.py

@@ -353,12 +353,12 @@ def default_update(ui_socket, context, do_execute=True):
         node_tree.update_tree(context, force=True)
     elif hasattr(ui_socket, 'default_value'):
         # we may not have to regenerate the tree; try and update the socket
-        from .utilities import tree_from_nc
+        from .utilities import tree_from_mantis_node
         for mantis_node in node_tree.parsed_tree.values():
             # check to see if the mantis node is in the same ui-tree as this ui_socket
             if mantis_node.ui_signature is None: continue # autogenerated nodes
             if mantis_node.ui_signature[-1] == ui_socket.node.name and \
-                        tree_from_nc(mantis_node.ui_signature, node_tree) == ui_socket.node.id_data:
+                        tree_from_mantis_node(mantis_node.ui_signature, node_tree) == ui_socket.node.id_data:
                 node_updated = True
                 from .misc_nodes import SimpleInputNode
                 if isinstance(mantis_node, SimpleInputNode):

+ 40 - 40
utilities.py

@@ -90,11 +90,11 @@ def clear_reroutes(links):
             kept_links.append(link)
     for start in rerouted_starts:
         from_socket = socket_seek(start, rerouted)
-        new_link = DummyLink(from_socket=from_socket, to_socket=start.to_socket, nc_from=None, nc_to=None, multi_input_sort_id=start.multi_input_sort_id )
+        new_link = DummyLink(from_socket=from_socket, to_socket=start.to_socket, from_mantis_node=None, to_mantis_node=None, multi_input_sort_id=start.multi_input_sort_id )
         kept_links.append(new_link)
     return kept_links
 
-def tree_from_nc(sig, base_tree):
+def tree_from_mantis_node(sig, base_tree):
     if (sig[0] == 'MANTIS_AUTOGENERATED'):
         sig = sig[:-2] # cut off the end part of the signature (because it uses socket.name and socket.identifier)
         # this will lead to totally untraceble bugs in the event of a change in how signatures are assigned
@@ -105,8 +105,8 @@ def tree_from_nc(sig, base_tree):
         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] )
+def get_ui_node(sig, base_tree):
+    return tree_from_mantis_node(sig, base_tree).nodes.get( sig[-1] )
 
 
 ##################################################################################################
@@ -559,31 +559,31 @@ def get_component_library_items(path='ADD_ARMATURE'):
 ##############################
 
 # TODO: refactor the following two functions, they should be one function with arguments.
-def init_connections(nc):
+def init_connections(mantis_node):
     c, hc = [], []
-    for i in nc.outputs.values():
+    for i in mantis_node.outputs.values():
         for l in i.links:
-            # if l.from_node != nc:
+            # if l.from_node != mantis_node:
             #     continue
             if l.is_hierarchy:
                 hc.append(l.to_node)
             c.append(l.to_node)
-    nc.hierarchy_connections = hc
-    nc.connections = c
+    mantis_node.hierarchy_connections = hc
+    mantis_node.connections = c
 
-def init_dependencies(nc):
+def init_dependencies(mantis_node):
     c, hc = [], []
-    for i in nc.inputs.values():
+    for i in mantis_node.inputs.values():
         for l in i.links:
-            # if l.to_node != nc:
+            # if l.to_node != mantis_node:
             #     continue
             if l.is_hierarchy:
                 hc.append(l.from_node)
             c.append(l.from_node)
-    nc.hierarchy_dependencies = hc
-    nc.dependencies = c
+    mantis_node.hierarchy_dependencies = hc
+    mantis_node.dependencies = c
 
-def schema_dependency_handle_item(schema, all_nc, item,):
+def schema_dependency_handle_item(schema, all_mantis_nodes, item,):
     hierarchy = True
     from .base_definitions import from_name_filter, to_name_filter
     if item.in_out == 'INPUT':
@@ -591,8 +591,8 @@ def schema_dependency_handle_item(schema, all_nc, item,):
         hierarchy_dependencies = schema.hierarchy_dependencies
         if item.parent and item.parent.name == 'Array':
             for schema_idname in ['SchemaArrayInput', 'SchemaArrayInputGet', 'SchemaArrayInputAll']:
-                if (nc := all_nc.get( (*schema.signature, schema_idname) )):
-                    for to_link in nc.outputs[item.name].links:
+                if (mantis_node := all_mantis_nodes.get( (*schema.signature, schema_idname) )):
+                    for to_link in mantis_node.outputs[item.name].links:
                         if to_link.to_socket in to_name_filter:
                             # hierarchy_reason='a'
                             hierarchy = False
@@ -605,8 +605,8 @@ def schema_dependency_handle_item(schema, all_nc, item,):
                                 hierarchy_dependencies.append(from_link.from_node)
                             dependencies.append(from_link.from_node)
         if item.parent and item.parent.name == 'Constant':
-            if nc := all_nc.get((*schema.signature, 'SchemaConstInput')):
-                for to_link in nc.outputs[item.name].links:
+            if mantis_node := all_mantis_nodes.get((*schema.signature, 'SchemaConstInput')):
+                for to_link in mantis_node.outputs[item.name].links:
                     if to_link.to_socket in to_name_filter:
                         # hierarchy_reason='dependencies'
                         hierarchy = False
@@ -619,8 +619,8 @@ def schema_dependency_handle_item(schema, all_nc, item,):
                             hierarchy_dependencies.append(from_link.from_node)
                         dependencies.append(from_link.from_node)
         if item.parent and item.parent.name == 'Connection':
-            if nc := all_nc.get((*schema.signature, 'SchemaIncomingConnection')):
-                for to_link in nc.outputs[item.name].links:
+            if mantis_node := all_mantis_nodes.get((*schema.signature, 'SchemaIncomingConnection')):
+                for to_link in mantis_node.outputs[item.name].links:
                     if to_link.to_socket in to_name_filter:
                         # hierarchy_reason='e'
                         hierarchy = False
@@ -633,11 +633,11 @@ def schema_dependency_handle_item(schema, all_nc, item,):
                             hierarchy_dependencies.append(from_link.from_node)
                         dependencies.append(from_link.from_node)
 
-def init_schema_dependencies(schema, all_nc):
+def init_schema_dependencies(schema, all_mantis_nodes):
     """ Initialize the dependencies for Schema, and mark them as hierarchy or non-hierarchy dependencies
         Non-hierarchy dependencies are e.g. drivers and custom transforms.
     """
-    tree = schema.prototype.node_tree
+    tree = schema.ui_node.node_tree
     if tree is None:
         raise RuntimeError(f"Cannot get dependencies for schema {schema}")
     schema.dependencies = []
@@ -648,7 +648,7 @@ def init_schema_dependencies(schema, all_nc):
         for item in tree.interface.items_tree:
             if item.item_type == 'PANEL':
                 continue
-            schema_dependency_handle_item(schema, all_nc, item,)
+            schema_dependency_handle_item(schema, all_mantis_nodes, item,)
 
 def check_and_add_root(n, roots, include_non_hierarchy=False):
     if (include_non_hierarchy * len(n.dependencies)) > 0:
@@ -667,34 +667,34 @@ def get_link_in_out(link):
         to_name = link.to_socket.node.bl_idname
     return from_name, to_name
 
-def link_node_containers(tree_path_names, link, local_nc, from_suffix='', to_suffix=''):
+def link_mantis_nodes(tree_path_names, link, local_mantis_nodes, from_suffix='', to_suffix=''):
     dummy_types = ["DUMMY", "DUMMY_SCHEMA"]
     from_name, to_name = get_link_in_out(link)
-    nc_from = local_nc.get( (*tree_path_names, from_name+from_suffix) )
-    nc_to = local_nc.get( (*tree_path_names, to_name+to_suffix))
-    if (nc_from and nc_to):
+    from_mantis_node = local_mantis_nodes.get( (*tree_path_names, from_name+from_suffix) )
+    to_mantis_node = local_mantis_nodes.get( (*tree_path_names, to_name+to_suffix))
+    if (from_mantis_node and to_mantis_node):
         from_s, to_s = link.from_socket.name, link.to_socket.name
-        if nc_to.node_type in dummy_types: to_s = link.to_socket.identifier
-        if nc_from.node_type in dummy_types: from_s = link.from_socket.identifier
+        if to_mantis_node.node_type in dummy_types: to_s = link.to_socket.identifier
+        if from_mantis_node.node_type in dummy_types: from_s = link.from_socket.identifier
         try:
-            connection = nc_from.outputs[from_s].connect(node=nc_to, socket=to_s, sort_id=link.multi_input_sort_id)
+            connection = from_mantis_node.outputs[from_s].connect(node=to_mantis_node, socket=to_s, sort_id=link.multi_input_sort_id)
             if connection is None:
                 prWhite(f"Already connected: {from_name}:{from_s}->{to_name}:{to_s}")
             return connection
         except KeyError as e:
-            prRed(f"{nc_from}:{from_s} or {nc_to}:{to_s} missing; review the connections printed below:")
-            print (nc_from.outputs.keys())
-            print (nc_to.inputs.keys())
+            prRed(f"{from_mantis_node}:{from_s} or {to_mantis_node}:{to_s} missing; review the connections printed below:")
+            print (from_mantis_node.outputs.keys())
+            print (to_mantis_node.inputs.keys())
             raise e
     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" ))
+        prRed(from_mantis_node, to_mantis_node, (*tree_path_names, from_name+from_suffix), (*tree_path_names, to_name+to_suffix))
+        raise RuntimeError(wrapRed(f"Link not connected: {from_mantis_node} -> {to_mantis_node} in tree" ))
     
-def get_all_dependencies(nc):
+def get_all_dependencies(mantis_node):
     from .base_definitions import GraphError
     """ find all dependencies for a mantis node"""
     nodes = []
-    check_nodes = [nc]
+    check_nodes = [mantis_node]
     nodes_checked = set()
     while (len(check_nodes) > 0):
         node = check_nodes.pop()
@@ -870,10 +870,10 @@ def project_point_to_plane(point, origin, normal):
 ##################################################################################################
 
 # This is really, really stupid way to do this
-def gen_nc_input_for_data(socket):
+def gen_mantis_node_input_for_data(socket):
     # Class List #TODO deduplicate
     from . import xForm_nodes, link_nodes, misc_nodes, primitives_nodes, deformer_nodes, math_nodes, schema_nodes
-    from .internal_containers import NoOpNode
+    from .internal_nodes import NoOpNode
     classes = {}
     for module in [xForm_nodes, link_nodes, misc_nodes, primitives_nodes, deformer_nodes, math_nodes, schema_nodes]:
         for cls in module.TellClasses():

+ 2 - 2
visualize.py

@@ -34,13 +34,13 @@ class MantisVisualizeNode(Node):
         return label
     
     def gen_data(self, mantis_node, mode='DEBUG_CONNECTIONS'):
-        from .utilities import get_node_prototype
+        from .utilities import get_ui_node
         if mantis_node.node_type in ['SCHEMA', 'DUMMY']:
             np=None
         elif mantis_node.ui_signature is None:
             np=None
         else:
-            np=get_node_prototype(mantis_node.ui_signature, mantis_node.base_tree)
+            np=get_ui_node(mantis_node.ui_signature, mantis_node.base_tree)
         self.use_custom_color = True
         match mantis_node.node_type:
             case 'XFORM':        self.color = (1.0 ,0.5, 0.0)

+ 104 - 59
xForm_nodes.py

@@ -1,6 +1,7 @@
-from .node_container_common import *
+from .node_common import *
 from .base_definitions import MantisNode, NodeSocket
 from .xForm_socket_templates import *
+from .mantis_dataclasses import xForm_info
 
 def TellClasses():
 
@@ -23,11 +24,11 @@ def reset_object_data(ob):
     ob.animation_data_clear() # this is a little dangerous. TODO find a better solution since this can wipe animation the user wants to keep
     ob.modifiers.clear() # I would also like a way to copy modifiers and their settings, or bake them down. oh well
 
-def get_parent_node(node_container, type = 'XFORM'):
+def get_parent_node(mantis_node, type = 'XFORM'):
     # type variable for selecting whether to get either
     #   the parent xForm  or the inheritance node
-    node_line, socket = trace_single_line(node_container, "Relationship")
-    parent_nc = None
+    node_line, socket = trace_single_line(mantis_node, "Relationship")
+    parent_mantis_node = None
     for i in range(len(node_line)):
         # check each of the possible parent types.
         if ( (node_line[ i ].__class__.__name__ == 'LinkInherit') ):
@@ -48,29 +49,21 @@ def get_matrix(node):
     return matrix
 
 def set_object_parent(node):
-        parent_nc = get_parent_node(node, type='LINK')
-        if (parent_nc):
-            parent = None
-            if node.inputs["Relationship"].is_linked:
-                trace = trace_single_line(node, "Relationship")
-                for other_node in trace[0]:
-                    if other_node is node: continue # lol
-                    if (other_node.node_type == 'XFORM'):
-                        parent = other_node; break
-                if parent is None:
-                    prWhite(f"INFO: no parent set for {node}.")
-                    return
-                prWhite(f"INFO: setting parent of {node} to {other_node}.")
-
-                if (parent.bObject) is None:
-                    raise GraphError(f"Could not get parent object from node {parent} for {node}")
-                if isinstance(parent, xFormBone):
-                    armOb= parent.bGetParentArmature()
-                    node.bObject.parent = armOb
-                    node.bObject.parent_type = 'BONE'
-                    node.bObject.parent_bone = parent.bObject
-                else:
-                    node.bObject.parent = parent.bGetObject()
+    from bpy import data
+    parent_xForm_info = get_parent_xForm_info(node)
+    if parent_xForm_info.object_type  == '':
+        return # no parent
+    elif parent_xForm_info.object_type in ['armature', 'object']:
+        parent = data.objects.get(parent_xForm_info.parent_edit_name)
+        if parent_xForm_info.parent_edit_name and parent is None:
+            raise GraphError(f"Could not get parent object for node {node}.")
+        node.bGetObject().parent = parent
+    else: # it is a bone
+        armOb = data.objects.get(parent_xForm_info.root_armature)
+        node_ob = node.bGetObject()
+        node_ob.parent = armOb; node_ob.parent_type = 'BONE'
+        # this one expects a string.
+        node_ob.parent_bone = parent_xForm_info.self_edit_name
 
 class xFormNode(MantisNode):
     def __init__(self, signature, base_tree, socket_templates=[]):
@@ -96,7 +89,7 @@ class xFormArmature(xFormNode):
         self.prepared = True
 
     def bTransformPass(self, bContext = None,):
-        # from .utilities import get_node_prototype
+        # from .utilities import get_ui_node
 
         import bpy
         if (not isinstance(bContext, bpy.types.Context)):
@@ -130,8 +123,6 @@ class xFormArmature(xFormNode):
                 ob.data.collections.remove(bc)
             del collections
             # end ugly/bad
-
-
         else:
             # Create the Object
             ob = bpy.data.objects.new(name, bpy.data.armatures.new(name)) #create ob
@@ -171,10 +162,23 @@ class xFormArmature(xFormNode):
             ob.data.edit_bones.remove(ob.data.edit_bones[0])
         # bContext.view_layer.objects.active = prevAct
 
+        # This will OVERWRITE the root armature since bones have to trace back to this one.
+        root_armature = ob.name
+        parent_xForm_info = get_parent_xForm_info(self)
+        my_info = xForm_info(
+            object_type='armature',
+            root_armature= ob.name,
+            parent_pose_name=parent_xForm_info.self_pose_name,
+            parent_edit_name=parent_xForm_info.self_edit_name,
+            self_pose_name=ob.name,
+            self_edit_name=ob.name,
+        )
+        self.parameters['xForm Out'] = my_info
+
         self.executed = True
 
     def bGetObject(self, mode = ''):
-        import bpy; return bpy.data.objects[self.bObject]
+        import bpy; return bpy.data.objects[self.parameters['xForm Out'].self_pose_name]
 
 bone_inputs= [
          "Name",
@@ -258,32 +262,22 @@ class xFormBone(xFormNode):
         self.set_traverse([("Relationship", "xForm Out")])
 
     def bGetParentArmature(self):
-        if (trace := trace_single_line(self, "Relationship")[0] ) :
-            for i in range(len(trace)):
-                # have to look in reverse, actually TODO
-                if ( isinstance(trace[ i ], xFormArmature ) ):
-                    return trace[ i ].bGetObject()
-        return None
-        #should do the trick...
+        parent_xForm_info = get_parent_xForm_info(self)
+        from bpy import data
+        return data.objects.get(parent_xForm_info.root_armature)
 
     def bSetParent(self, eb):
-        # print (self.bObject)
-        from bpy.types import EditBone
-        parent_nc = get_parent_node(self, type='LINK')
-        # print (self, parent_nc.inputs['Parent'].from_node)
-        parent=None
-        if parent_nc.inputs['Parent'].links[0].from_node.node_type == 'XFORM':
-            parent = parent_nc.inputs['Parent'].links[0].from_node.bGetObject(mode = 'EDIT')
-        else:
-            raise RuntimeError(wrapRed(f"Cannot set parent for node {self}"))
-
-        if isinstance(parent, EditBone):
-            eb.parent = parent
-
-        eb.use_connect = parent_nc.evaluate_input("Connected")
-        eb.use_inherit_rotation = parent_nc.evaluate_input("Inherit Rotation")
-        eb.inherit_scale = parent_nc.evaluate_input("Inherit Scale")
-        # otherwise, no need to do anything.
+        from bpy import data
+        parent_xForm_info = get_parent_xForm_info(self)
+        parent_armature = data.objects.get( parent_xForm_info.root_armature)
+        if parent_xForm_info.self_edit_name != parent_xForm_info.root_armature:
+            parent_bone = parent_armature.data.edit_bones.get( parent_xForm_info.self_edit_name)
+            eb.parent = parent_bone
+            parent_mantis_node = get_parent_node(self, type = 'LINK') # get the link node.
+            # TODO probably need to send the parenting info or at least the signatures of intervening nodes.
+            eb.use_connect = parent_mantis_node.evaluate_input("Connected")
+            eb.use_inherit_rotation = parent_mantis_node.evaluate_input("Inherit Rotation")
+            eb.inherit_scale = parent_mantis_node.evaluate_input("Inherit Scale")
 
     def bPrepare(self, bContext=None):
         self.parameters['Matrix'] = get_matrix(self)
@@ -362,6 +356,18 @@ class xFormBone(xFormNode):
         eb.tail_radius           = self.evaluate_input("Envelope Tail Radius")
 
         print( wrapGreen("Created Bone: ") + wrapOrange(eb.name) + wrapGreen(" in ") + wrapWhite(self.bGetParentArmature().name))
+
+        parent_xForm_info = get_parent_xForm_info(self)
+        my_info = xForm_info(
+            object_type='bone',
+            root_armature= xF.name,
+            parent_pose_name=parent_xForm_info.self_pose_name,
+            parent_edit_name=parent_xForm_info.self_edit_name,
+            self_pose_name=eb.name,
+            self_edit_name=eb.name,
+        )
+        self.parameters['xForm Out'] = my_info
+
         self.executed = True
 
     def set_bone_color(self, b, inherit_color, bContext):
@@ -475,8 +481,8 @@ class xFormBone(xFormNode):
         pb.rotation_mode = rotation_mode
         pb.id_properties_clear()
         # these are kept around unless explicitly deleted.
-        # from .utilities import get_node_prototype
-        # np = get_node_prototype(self.signature, self.base_tree)
+        # from .utilities import get_ui_node
+        # np = get_ui_node(self.signature, self.base_tree)
         driver = None
         do_prints=False
 
@@ -648,10 +654,10 @@ class xFormBone(xFormNode):
             prRed ("Cannot get bone for %s" % self)
             raise e
 
-    def fill_parameters(self, prototype=None):
+    def fill_parameters(self, ui_node=None):
         # this is the fill_parameters that is run if it isn't a schema
         setup_custom_props(self)
-        super().fill_parameters(prototype)
+        super().fill_parameters(ui_node)
         # otherwise we will do this from the schema
         # LEGIBILITY TODO - why? explain this?
 
@@ -723,6 +729,19 @@ class xFormGeometryObject(xFormNode):
         except: # I guess it isn't ready yet. we'll do it later
             pass # (This can happen when solving schema.)
         self.bObject.matrix_world = matrix
+
+        parent_xForm_info = get_parent_xForm_info(self)
+        root_armature = parent_xForm_info.root_armature
+        my_info = xForm_info(
+            object_type='object',
+            root_armature= root_armature,
+            parent_pose_name=parent_xForm_info.self_edit_name,
+            parent_edit_name=parent_xForm_info.self_pose_name,
+            self_pose_name=self.bObject.name,
+            self_edit_name=self.bObject.name,
+        )
+        self.parameters['xForm Out'] = my_info
+
         self.prepared = True
 
     def bTransformPass(self, bContext = None,):
@@ -795,6 +814,19 @@ class xFormObjectInstance(xFormNode):
         self.parameters['Matrix'] = matrix
         set_object_parent(self)
         self.bObject.matrix_world = matrix
+
+        parent_xForm_info = get_parent_xForm_info(self)
+        root_armature = parent_xForm_info.root_armature
+        my_info = xForm_info(
+            object_type='object',
+            root_armature= root_armature,
+            parent_pose_name=parent_xForm_info.self_edit_name,
+            parent_edit_name=parent_xForm_info.self_pose_name,
+            self_pose_name=self.bObject.name,
+            self_edit_name=self.bObject.name,
+        )
+        self.parameters['xForm Out'] = my_info
+
         self.prepared = True
 
     def bTransformPass(self, bContext = None,):
@@ -944,6 +976,19 @@ class xFormCurvePin(xFormNode):
         self.parameters['Matrix'] = ob.matrix_world.copy()
         ob.parent=curve
         print( wrapGreen("Created Curve Pin: ") + wrapOrange(self.bObject.name) )
+
+        parent_xForm_info = get_parent_xForm_info(self)
+        root_armature = parent_xForm_info.root_armature
+        my_info = xForm_info(
+            object_type='object',
+            root_armature= root_armature,
+            parent_pose_name=curve.name,
+            parent_edit_name=curve.name,
+            self_pose_name=self.bObject.name,
+            self_edit_name=self.bObject.name,
+        )
+        self.parameters['xForm Out'] = my_info
+
         self.prepared = True; self.executed = True
 
     def bFinalize(self, bContext = None):

+ 22 - 22
xForm_nodes_ui.py

@@ -43,15 +43,15 @@ def default_traverse(self, socket):
 
 def check_if_connected(start, end, line):
     started=False
-    for path_nc in line:
-        prWhite("    ", path_nc.signature)
-        if path_nc.signature == start.signature:
+    for path_mantis_node in line:
+        prWhite("    ", path_mantis_node.signature)
+        if path_mantis_node.signature == start.signature:
             started = True
-        elif path_nc.signature == end.signature:
+        elif path_mantis_node.signature == end.signature:
             break
         if started:
-            if path_nc.inputs.get("Connected"):
-                if path_nc.evaluate_input("Connected") == False:
+            if path_mantis_node.inputs.get("Connected"):
+                if path_mantis_node.evaluate_input("Connected") == False:
                     return False
     else:
         return False
@@ -241,18 +241,18 @@ class xFormBoneNode(Node, xFormNode):
         
     def display_update(self, parsed_tree, context):
         if context.space_data:
-            nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
+            mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
             self.display_ik_settings = False
-            if nc and (pb := nc.bGetObject(mode='POSE')):
+            if mantis_node and (pb := mantis_node.bGetObject(mode='POSE')):
                 self.display_ik_settings = pb.is_in_ik_chain
             
             self.inputs['Name'].display_text = ""
-            if nc:
+            if mantis_node:
                 try:
-                    self.inputs['Name'].display_text = nc.evaluate_input("Name")
-                    self.display_vp_settings = nc.inputs["Custom Object"].is_connected
-                    self.display_def_settings = nc.evaluate_input("Deform")
-                    self.display_bb_settings = nc.evaluate_input("BBone Segments") > 1
+                    self.inputs['Name'].display_text = mantis_node.evaluate_input("Name")
+                    self.display_vp_settings = mantis_node.inputs["Custom Object"].is_connected
+                    self.display_def_settings = mantis_node.evaluate_input("Deform")
+                    self.display_bb_settings = mantis_node.evaluate_input("BBone Segments") > 1
                 except KeyError:
                     return # the tree isn't ready yet.
             
@@ -290,10 +290,10 @@ class xFormArmatureNode(Node, xFormNode):
     
     def display_update(self, parsed_tree, context):
         if context.space_data:
-            nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
+            mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
             self.inputs['Name'].display_text = ""
-            if nc:
-                self.inputs['Name'].display_text = nc.evaluate_input("Name")
+            if mantis_node:
+                self.inputs['Name'].display_text = mantis_node.evaluate_input("Name")
 
 class xFormGeometryObjectNode(Node, xFormNode):
     """Represents a curve or mesh object."""
@@ -314,10 +314,10 @@ class xFormGeometryObjectNode(Node, xFormNode):
     
     def display_update(self, parsed_tree, context):
         if context.space_data:
-            nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
+            mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
             self.inputs['Name'].display_text = ""
-            if nc:
-                self.inputs['Name'].display_text = nc.evaluate_input("Name")
+            if mantis_node:
+                self.inputs['Name'].display_text = mantis_node.evaluate_input("Name")
 
 class xFormObjectInstance(Node, xFormNode):
     """Represents an instance of an existing geometry object."""
@@ -338,10 +338,10 @@ class xFormObjectInstance(Node, xFormNode):
     
     def display_update(self, parsed_tree, context):
         if context.space_data:
-            nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
+            mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
             self.inputs['Name'].display_text = ""
-            if nc:
-                self.inputs['Name'].display_text = nc.evaluate_input("Name")
+            if mantis_node:
+                self.inputs['Name'].display_text = mantis_node.evaluate_input("Name")
 
 from .xForm_nodes import xFormCurvePinSockets
 class xFormCurvePin(Node, xFormNode):