7 Commits 965c6c48d6 ... b4ad5bfeff

Tác giả SHA1 Thông báo Ngày
  Joseph Brandenburg b4ad5bfeff Add profiler to help optimize nodes 3 tháng trước cách đây
  Joseph Brandenburg b2507f4983 Refactor: xForm_info data class for setting parents 3 tháng trước cách đây
  Joseph Brandenburg ead2ed726a add utility function to clear traverse targets 3 tháng trước cách đây
  Joseph Brandenburg d2a048a2b7 Fix Vector Division 3 tháng trước cách đây
  Joseph Brandenburg 18522fe086 lots of renamed variables, functions, etc 3 tháng trước cách đây
  Joseph Brandenburg 8860af32ad rename node_container_common to node_common 3 tháng trước cách đây
  Joseph Brandenburg 364dd262c1 rename internal_containers to internal_nodes 3 tháng trước cách đây

+ 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):