| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438 | 
							- from .node_container_common import *
 
- from .base_definitions import MantisNode, NodeSocket
 
- from .xForm_containers import xFormArmature, xFormBone
 
- from math import pi, tau
 
- def TellClasses():
 
-     return [
 
-              # utility
 
-              InputFloat,
 
-              InputIntNode,
 
-              InputVector,
 
-              InputBoolean,
 
-              InputBooleanThreeTuple,
 
-              InputRotationOrder,
 
-              InputTransformSpace,
 
-              InputString,
 
-              InputMatrix,
 
-              InputExistingGeometryObject,
 
-              InputExistingGeometryData,
 
-              UtilityGeometryOfXForm,
 
-              UtilityNameOfXForm,
 
-              UtilityPointFromCurve,
 
-              UtilityMatrixFromCurve,
 
-              UtilityMatricesFromCurve,
 
-              UtilityMetaRig,
 
-              UtilityBoneProperties,
 
-              UtilityDriverVariable,
 
-              UtilityDriver,
 
-              UtilityFCurve,
 
-              UtilityKeyframe,
 
-              UtilitySwitch,
 
-              UtilityCombineThreeBool,
 
-              UtilityCombineVector,
 
-              UtilitySeparateVector,
 
-              UtilityCatStrings,
 
-              UtilityGetBoneLength,
 
-              UtilityPointFromBoneMatrix,
 
-              UtilitySetBoneLength,
 
-              UtilityMatrixSetLocation,
 
-              UtilityMatrixGetLocation,
 
-              UtilityMatrixFromXForm,
 
-              UtilityAxesFromMatrix,
 
-              UtilityBoneMatrixHeadTailFlip,
 
-              UtilityMatrixTransform,
 
-              UtilityTransformationMatrix,
 
-              UtilityIntToString,
 
-              UtilityArrayGet,
 
-              UtilitySetBoneMatrixTail,
 
-              # Control flow switches
 
-              UtilityCompare,
 
-              UtilityChoose,
 
-              # useful NoOp:
 
-              UtilityPrint,
 
-             ]
 
- def matrix_from_head_tail(head, tail):
 
-     from mathutils import Vector, Quaternion, Matrix
 
-     rotation = Vector((0,1,0)).rotation_difference((tail-head).normalized()).to_matrix()
 
-     m= Matrix.LocRotScale(head, rotation, None)
 
-     m[3][3] = (tail-head).length
 
-     return m
 
- #*#-------------------------------#++#-------------------------------#*#
 
- # U T I L I T Y   N O D E S
 
- #*#-------------------------------#++#-------------------------------#*#
 
- class InputFloat(MantisNode):
 
-     '''A node representing float input'''
 
-     
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         outputs = ["Float Input"]
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = 'UTILITY'
 
-         self.prepared = True
 
-         self.executed = True
 
- class InputIntNode(MantisNode):
 
-     '''A node representing integer input'''
 
-     
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         outputs = ["Integer"]
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = 'UTILITY'
 
-         self.prepared = True
 
-         self.executed = True
 
-     
 
- class InputVector(MantisNode):
 
-     '''A node representing vector input'''
 
-     
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         outputs = [""]
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = 'UTILITY'
 
-         self.prepared = True
 
-         self.executed = True
 
- class InputBoolean(MantisNode):
 
-     '''A node representing boolean input'''
 
-     
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         outputs = [""]
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = 'UTILITY'
 
-         self.prepared = True
 
-         self.executed = True
 
- class InputBooleanThreeTuple(MantisNode):
 
-     '''A node representing a tuple of three booleans'''
 
-         
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         outputs = [""]
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = 'UTILITY'
 
-         self.prepared = True
 
-         self.executed = True
 
-     
 
- class InputRotationOrder(MantisNode):
 
-     '''A node representing string input for rotation order'''
 
-         
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         outputs = [""]
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = 'UTILITY'
 
-         self.prepared = True
 
-         self.executed = True
 
-     
 
- class InputTransformSpace(MantisNode):
 
-     '''A node representing string input for transform space'''
 
-         
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         outputs = [""]
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = 'UTILITY'
 
-         self.prepared = True
 
-         self.executed = True
 
-         
 
-     def evaluate_input(self, input_name):
 
-         return self.parameters[""]
 
-     
 
- class InputString(MantisNode):
 
-     '''A node representing string input'''
 
-         
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         outputs = [""]
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = 'UTILITY'
 
-         self.prepared = True
 
-         self.executed = True
 
-     
 
- class InputMatrix(MantisNode):
 
-     '''A node representing axis-angle quaternion input'''
 
-         
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         outputs  = ["Matrix",]
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = 'UTILITY'
 
-         self.prepared = True
 
-         self.executed = True
 
-         #
 
-         from mathutils import Matrix
 
-         from .utilities import get_node_prototype
 
-         # TODO: this should probably be in fill_parameters
 
-         node_prototype = get_node_prototype(self.signature, self.base_tree)
 
-         
 
-         matrix = ( node_prototype.first_row[ 0], node_prototype.first_row[ 1], node_prototype.first_row[ 2], node_prototype.first_row[ 3],
 
-                    node_prototype.second_row[0], node_prototype.second_row[1], node_prototype.second_row[2], node_prototype.second_row[3],
 
-                    node_prototype.third_row[ 0], node_prototype.third_row[ 1], node_prototype.third_row[ 2], node_prototype.third_row[ 3],
 
-                    node_prototype.fourth_row[0], node_prototype.fourth_row[1], node_prototype.fourth_row[2], node_prototype.fourth_row[3], )
 
-         self.parameters["Matrix"] = Matrix([matrix[0:4], matrix[4:8], matrix[8:12], matrix[12:16]])
 
-     def fill_parameters(self, prototype=None):
 
-         return
 
- class UtilityMatrixFromCurve(MantisNode):
 
-     '''Get a matrix from a curve'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Curve"            ,
 
-           "Total Divisions"  ,
 
-           "Matrix Index"     ,
 
-         ]
 
-         outputs = [
 
-           "Matrix" ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         from mathutils import Matrix
 
-         import bpy
 
-         m = Matrix.Identity(4)
 
-         curve = bpy.data.objects.get(self.evaluate_input("Curve"))
 
-         if not curve:
 
-             prRed(f"No curve found for {self}. Using an Identity matrix instead.")
 
-             m[3][3] = 1.0
 
-         else:
 
-             from .utilities import mesh_from_curve, data_from_ribbon_mesh
 
-             if not bContext:
 
-                 # TODO find out if this is bad or a HACK or if it is OK
 
-                 bContext = bpy.context
 
-             # IMPORTANT TODO: I need to be able to reuse this m
 
-             # First, try to get the one we made before
 
-             m_name = curve.name+'.'+self.base_tree.execution_id
 
-             if not (m := bpy.data.meshes.get(m_name)):
 
-                 m = mesh_from_curve(curve, bContext)
 
-                 m.name = m_name
 
-             #
 
-             num_divisions = self.evaluate_input("Total Divisions")
 
-             m_index = self.evaluate_input("Matrix Index")
 
-             factors = [1/num_divisions*m_index, 1/num_divisions*(m_index+1)]
 
-             data = data_from_ribbon_mesh(m, [factors], curve.matrix_world)
 
-             # print(data)
 
-             # this is in world space... let's just convert it back
 
-             m = matrix_from_head_tail(data[0][0][0], data[0][0][1])
 
-             m.translation -= curve.location
 
-             # TODO HACK TODO
 
-             # all the nodes should work in world-space, and it should be the responsibility
 
-             # of the xForm node to convert!
 
-         self.parameters["Matrix"] = m
 
-         self.prepared = True
 
-         self.executed = True
 
-     
 
-     def bFinalize(self, bContext=None):
 
-         import bpy
 
-         curve_name = self.evaluate_input("Curve")
 
-         curve = bpy.data.objects.get(curve_name)
 
-         m_name = curve.name+'.'+self.base_tree.execution_id
 
-         if (mesh := bpy.data.meshes.get(m_name)):
 
-             bpy.data.meshes.remove(mesh)
 
- class UtilityPointFromCurve(MantisNode):
 
-     '''Get a point from a curve'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Curve"       ,
 
-           "Factor"      ,
 
-         ]
 
-         outputs = [
 
-           "Point" ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         from mathutils import Matrix
 
-         import bpy
 
-         curve = bpy.data.objects.get(self.evaluate_input("Curve"))
 
-         if not curve:
 
-             raise RuntimeError(f"No curve found for {self}.")
 
-         else:
 
-             from .utilities import mesh_from_curve, data_from_ribbon_mesh
 
-             if not bContext:
 
-                 # TODO find out if this is bad or a HACK or if it is OK
 
-                 bContext = bpy.context
 
-             # IMPORTANT TODO: I need to be able to reuse this m
 
-             # First, try to get the one we made before
 
-             m_name = curve.name+'.'+self.base_tree.execution_id
 
-             if not (m := bpy.data.meshes.get(m_name)):
 
-                 m = mesh_from_curve(curve, bContext)
 
-                 m.name = m_name
 
-             #
 
-             num_divisions = 1
 
-             factors = [self.evaluate_input("Factor")]
 
-             data = data_from_ribbon_mesh(m, [factors], curve.matrix_world)
 
-             p = data[0][0][0] - curve.location
 
-         self.parameters["Point"] = p
 
-         self.prepared, self.executed = True, True
 
-     
 
-     def bFinalize(self, bContext=None):
 
-         import bpy
 
-         curve_name = self.evaluate_input("Curve")
 
-         curve = bpy.data.objects.get(curve_name)
 
-         m_name = curve.name+'.'+self.base_tree.execution_id
 
-         if (mesh := bpy.data.meshes.get(m_name)):
 
-             bpy.data.meshes.remove(mesh)
 
- class UtilityMatricesFromCurve(MantisNode):
 
-     '''Get matrices from a curve'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Curve"            ,
 
-           "Total Divisions"  ,
 
-         ]
 
-         self.outputs = [
 
-           "Matrices" ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         import time
 
-         # start_time = time.time()
 
-         #
 
-         from mathutils import Matrix
 
-         import bpy
 
-         m = Matrix.Identity(4)
 
-         curve_name = self.evaluate_input("Curve")
 
-         curve = bpy.data.objects.get(curve_name)
 
-         if not curve:
 
-             prRed(f"No curve found for {self}. Using an Identity matrix instead.")
 
-             m[3][3] = 1.0
 
-         else:
 
-             from .utilities import mesh_from_curve, data_from_ribbon_mesh
 
-             if not bContext:
 
-                 bContext = bpy.context
 
-             m_name = curve.name+'.'+self.base_tree.execution_id
 
-             if not (mesh := bpy.data.meshes.get(m_name)):
 
-                 mesh = mesh_from_curve(curve, bContext)
 
-                 mesh.name = m_name
 
-             num_divisions = self.evaluate_input("Total Divisions")
 
-             factors = [0.0] + [(1/num_divisions*(i+1)) for i in range(num_divisions)]
 
-             data = data_from_ribbon_mesh(mesh, [factors], curve.matrix_world)
 
-             
 
-             # 0 is the spline index. 0 selects points as opposed to normals or whatever.
 
-             matrices = [matrix_from_head_tail(data[0][0][i], data[0][0][i+1]) for i in range(num_divisions)]
 
-         
 
-         for link in self.outputs["Matrices"].links:
 
-             for i, m in enumerate(matrices):
 
-                 name = "Matrix"+str(i).zfill(4)
 
-                 if not (out := self.outputs.get(name)): # reuse them if there are multiple links.
 
-                     out = self.outputs[name] = NodeSocket(name = name, node=self)
 
-                 c = out.connect(link.to_node, link.to_socket)
 
-                 # prOrange(c)
 
-                 self.parameters[name] = m
 
-                 # print (mesh)
 
-             link.die()
 
-         self.prepared = True
 
-         self.executed = True
 
-         # prGreen(f"Matrices from curves took {time.time() - start_time} seconds.")
 
-     
 
-     def bFinalize(self, bContext=None):
 
-         import bpy
 
-         curve_name = self.evaluate_input("Curve")
 
-         curve = bpy.data.objects.get(curve_name)
 
-         m_name = curve.name+'.'+self.base_tree.execution_id
 
-         if (mesh := bpy.data.meshes.get(m_name)):
 
-             prGreen(f"Freeing mesh data {m_name}...")
 
-             bpy.data.meshes.remove(mesh)
 
- class UtilityMetaRig(MantisNode):
 
-     '''A node representing an armature object'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Meta-Armature" ,
 
-           "Meta-Bone"     ,
 
-         ]
 
-         outputs = [
 
-           "Matrix" ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         #kinda clumsy, whatever
 
-         import bpy
 
-         from mathutils import Matrix
 
-         m = Matrix.Identity(4)
 
-         
 
-         meta_rig  = self.evaluate_input("Meta-Armature")
 
-         meta_bone = self.evaluate_input("Meta-Bone")
 
-         
 
-         if meta_rig:
 
-             if ( armOb := bpy.data.objects.get(meta_rig) ):
 
-                 m = armOb.matrix_world
 
-                 if ( b := armOb.data.bones.get(meta_bone)):
 
-                     # calculate the correct object-space matrix
 
-                     m = Matrix.Identity(3)
 
-                     bones = [] # from the last ancestor, mult the matrices until we get to b
 
-                     while (b): bones.append(b); b = b.parent
 
-                     while (bones): b = bones.pop(); m = m @ b.matrix
 
-                     m = Matrix.Translation(b.head_local) @ m.to_4x4()
 
-                     #
 
-                     m[3][3] = b.length # this is where I arbitrarily decided to store length
 
-                 # else:
 
-                 #     prRed("no bone for MetaRig node ", self)
 
-         else:
 
-             raise RuntimeError(wrapRed(f"No meta-rig input for MetaRig node {self}"))
 
-         
 
-         self.parameters["Matrix"] = m
 
-         self.prepared = True
 
-         self.executed = True
 
- class UtilityBoneProperties(MantisNode):
 
-     '''A node representing a bone's gettable properties'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         outputs = [
 
-             "matrix"        ,
 
-             "matrix_local"  ,
 
-             "matrix_basis"  ,
 
-             "head"          ,
 
-             "tail"          ,
 
-             "length"        ,
 
-             "rotation"      ,
 
-             "location"      ,
 
-             "scale"         ,
 
-         ]
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-         self.prepared = True
 
-         self.executed = True
 
-     def fill_parameters(self, prototype=None):
 
-         return
 
-         
 
- # TODO this should probably be moved to Links
 
- class UtilityDriverVariable(MantisNode):
 
-     '''A node representing an armature object'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-             "Variable Type"   ,
 
-             "Property"       ,
 
-             "Property Index" ,
 
-             "Evaluation Space",
 
-             "Rotation Mode"   ,
 
-             "xForm 1"         ,
 
-             "xForm 2"         ,
 
-         ]
 
-         outputs = [
 
-           "Driver Variable",
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "DRIVER" # MUST be run in Pose mode
 
-         self.prepared = True
 
-         
 
-     def evaluate_input(self, input_name):
 
-         if input_name == 'Property':
 
-             if self.inputs.get('Property'):
 
-                 if self.inputs['Property'].is_linked:
 
-                 # get the name instead...
 
-                     trace = trace_single_line(self, input_name)
 
-                     return trace[1].name # the name of the socket
 
-             return self.parameters["Property"]
 
-         return super().evaluate_input(input_name)
 
-         
 
-     def GetxForm(self, index=1):
 
-         trace = trace_single_line(self, "xForm 1" if index == 1 else "xForm 2")
 
-         for node in trace[0]:
 
-             if (node.__class__ in [xFormArmature, xFormBone]):
 
-                 return node #this will fetch the first one, that's good!
 
-         return None
 
-     def bExecute(self, bContext = None,):
 
-         prepare_parameters(self)
 
-         #prPurple ("Executing Driver Variable Node")
 
-         xForm1 = self.GetxForm()
 
-         xForm2 = self.GetxForm(index=2)
 
-         # kinda clumsy
 
-         if xForm1 : xForm1 = xForm1.bGetObject()
 
-         if xForm2 : xForm2 = xForm2.bGetObject()
 
-         
 
-         v_type = self.evaluate_input("Variable Type")
 
-         i = self.evaluate_input("Property Index"); dVarChannel = ""
 
-         if (i >= 0): #negative values will use the vector property.
 
-             if self.evaluate_input("Property") == 'location':
 
-                 if   i == 0: dVarChannel = "LOC_X"
 
-                 elif i == 1: dVarChannel = "LOC_Y"
 
-                 elif i == 2: dVarChannel = "LOC_Z"
 
-                 else: raise RuntimeError("Invalid property index for %s" % self)
 
-             if self.evaluate_input("Property") == 'rotation':
 
-                 if   i == 0: dVarChannel = "ROT_X"
 
-                 elif i == 1: dVarChannel = "ROT_Y"
 
-                 elif i == 2: dVarChannel = "ROT_Z"
 
-                 elif i == 3: dVarChannel = "ROT_W"
 
-                 else: raise RuntimeError("Invalid property index for %s" % self)
 
-             if self.evaluate_input("Property") == 'scale':
 
-                 if   i == 0: dVarChannel = "SCALE_X"
 
-                 elif i == 1: dVarChannel = "SCALE_Y"
 
-                 elif i == 2: dVarChannel = "SCALE_Z"
 
-                 elif i == 3: dVarChannel = "SCALE_AVG"
 
-                 else: raise RuntimeError("Invalid property index for %s" % self)
 
-             if self.evaluate_input("Property") == 'scale_average':
 
-                 dVarChannel = "SCALE_AVG"
 
-         if dVarChannel: v_type = "TRANSFORMS"
 
-         
 
-         my_var = {
 
-             "owner"         : xForm1, # will be filled in by Driver
 
-             "prop"          : self.evaluate_input("Property"), # will be filled in by Driver
 
-             "type"          : v_type,
 
-             "space"         : self.evaluate_input("Evaluation Space"),
 
-             "rotation_mode" : self.evaluate_input("Rotation Mode"),
 
-             "xForm 1"       : xForm1,#self.GetxForm(index = 1),
 
-             "xForm 2"       : xForm2,#self.GetxForm(index = 2),
 
-             "channel"       : dVarChannel,}
 
-         
 
-         # Push parameter to downstream connected node.connected:
 
-         if (out := self.outputs["Driver Variable"]).is_linked:
 
-             self.parameters[out.name] = my_var
 
-             for link in out.links:
 
-                 link.to_node.parameters[link.to_socket] = my_var
 
-         self.executed = True
 
-             
 
- class UtilityKeyframe(MantisNode):
 
-     '''A node representing a keyframe for a F-Curve'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Frame"   ,
 
-           "Value"   ,
 
-         ]
 
-         outputs = [
 
-           "Keyframe" ,
 
-         ]
 
-         additional_parameters = {"Keyframe":{}}
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters( additional_parameters=additional_parameters)
 
-         self.node_type = "DRIVER" # MUST be run in Pose mode
 
-         setup_custom_props(self)
 
-     def bPrepare(self, bContext = None,):
 
-         key = self.parameters["Keyframe"]
 
-         from mathutils import Vector
 
-         key["co"]= Vector( (self.evaluate_input("Frame"), self.evaluate_input("Value"),))
 
-         key["type"]="GENERATED"
 
-         key["interpolation"] = "LINEAR"
 
-         # eventually this will have the right data, TODO
 
-         # self.parameters["Keyframe"] = key
 
-         self.prepared = True
 
-         self.executed = True
 
- class UtilityFCurve(MantisNode):
 
-     '''A node representing an armature object'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-             "Extrapolation Mode",
 
-         ]
 
-         outputs = [
 
-           "fCurve",
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-         setup_custom_props(self)
 
-         self.prepared = True
 
-     def evaluate_input(self, input_name):
 
-         return super().evaluate_input(input_name)
 
-     def bExecute(self, bContext = None,):
 
-         prepare_parameters(self)
 
-         from .utilities import get_node_prototype
 
-         np = get_node_prototype(self.signature, self.base_tree)
 
-         extrap_mode = self.evaluate_input("Extrapolation Mode")
 
-         keys = [] # ugly but whatever
 
-         #['amplitude', 'back', 'bl_rna', 'co', 'co_ui', 'easing', 'handle_left', 'handle_left_type', 'handle_right', 'handle_right_type',
 
-         # 'interpolation', 'period', 'rna_type', 'select_control_point', 'select_left_handle', 'select_right_handle', 'type']
 
-         for k in self.inputs.keys():
 
-             if k == 'Extrapolation Mode' : continue
 
-             # print (self.inputs[k])
 
-             if (key := self.evaluate_input(k)) is None:
 
-                 prOrange(f"WARN: No keyframe connected to {self}:{k}. Skipping Link.")
 
-             else:
 
-                 keys.append(key)
 
-         if len(keys) <1:
 
-             prOrange(f"WARN: no keys in fCurve {self}.")
 
-         keys.append(extrap_mode)
 
-         self.parameters["fCurve"] = keys
 
-         self.executed = True
 
- #TODO make the fCurve data a data class instead of a dict 
 
- class UtilityDriver(MantisNode):
 
-     '''A node representing an armature object'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Driver Type"   ,
 
-           "Expression"    ,
 
-           "fCurve"        ,
 
-         ]
 
-         outputs = [
 
-           "Driver",
 
-         ]
 
-         from .drivers import MantisDriver
 
-         additional_parameters = {
 
-           "Driver":MantisDriver(), 
 
-         }
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters(additional_parameters=additional_parameters)
 
-         self.node_type = "DRIVER" # MUST be run in Pose mode
 
-         setup_custom_props(self)
 
-         self.prepared = True
 
-     def bExecute(self, bContext = None,):
 
-         prepare_parameters(self)
 
-         from .drivers import MantisDriver
 
-         #prPurple("Executing Driver Node")
 
-         my_vars = []
 
-         keys = self.evaluate_input("fCurve")
 
-         if keys is None or len(keys) <2:
 
-             prWhite(f"INFO: no fCurve connected to {self}; using default fCurve.")
 
-             from mathutils import Vector
 
-             keys = [
 
-                 {"co":Vector( (0, 0,)), "type":"GENERATED", "interpolation":"LINEAR" },
 
-                 {"co":Vector( (1, 1,)), "type":"GENERATED", "interpolation":"LINEAR" },
 
-                 "CONSTANT",]
 
-         for inp in list(self.inputs.keys() )[3:]:
 
-             if (new_var := self.evaluate_input(inp)):
 
-                 new_var["name"] = inp
 
-                 my_vars.append(new_var)
 
-             else:
 
-                 raise RuntimeError(f"Failed to initialize Driver variable for {self}")
 
-         my_driver ={ "owner"         :  None,
 
-                      "prop"          :  None, # will be filled out in the node that uses the driver
 
-                      "expression"    :  self.evaluate_input("Expression"),
 
-                      "ind"           :  -1, # same here
 
-                      "type"          :  self.evaluate_input("Driver Type"),
 
-                      "vars"          :  my_vars,
 
-                      "keys"          :  keys[:-1],
 
-                      "extrapolation" :  keys[-1] }
 
-         
 
-         my_driver = MantisDriver(my_driver)
 
-         
 
-         self.parameters["Driver"].update(my_driver)
 
-         print("Initializing driver %s " % (wrapPurple(self.__repr__())) )
 
-         self.executed = True
 
- class UtilitySwitch(MantisNode):
 
-     '''A node representing an armature object'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = {
 
-           "Parameter"            ,
 
-           "Parameter Index"      ,
 
-           "Invert Switch"        ,
 
-         }
 
-         outputs = [
 
-           "Driver",
 
-         ]
 
-         from .drivers import MantisDriver
 
-         additional_parameters = {
 
-           "Driver":MantisDriver(), 
 
-         }
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters(additional_parameters=additional_parameters)
 
-         self.node_type = "DRIVER" # MUST be run in Pose mode
 
-         self.prepared = True
 
-     def evaluate_input(self, input_name):
 
-         if input_name == 'Parameter':
 
-             if self.inputs['Parameter'].is_connected:
 
-                 trace = trace_single_line(self, input_name)
 
-                 return trace[1].name # the name of the socket
 
-             return self.parameters["Parameter"]
 
-         return super().evaluate_input(input_name)
 
-     def GetxForm(self,):
 
-         trace = trace_single_line(self, "Parameter" )
 
-         for node in trace[0]:
 
-             if (node.__class__ in [xFormArmature, xFormBone]):
 
-                 return node #this will fetch the first one, that's good!
 
-         return None
 
-     def bExecute(self, bContext = None,):
 
-         #prepare_parameters(self)
 
-         #prPurple ("Executing Switch Node")
 
-         xForm = self.GetxForm()
 
-         if xForm : xForm = xForm.bGetObject() 
 
-         if not xForm:
 
-             raise RuntimeError("Could not evaluate xForm for %s" % self)
 
-         from .drivers import MantisDriver
 
-         my_driver ={ "owner" : None,
 
-                      "prop"  : None, # will be filled out in the node that uses the driver 
 
-                      "ind"   : -1, # same here
 
-                      "type"  : "SCRIPTED",
 
-                      "vars"  : [ { "owner" : xForm,
 
-                                    "prop"  : self.evaluate_input("Parameter"),
 
-                                    "name"  : "a",
 
-                                    "type"  : "SINGLE_PROP", } ],
 
-                      "keys"  : [ { "co":(0,0),
 
-                                    "interpolation": "LINEAR",
 
-                                    "type":"KEYFRAME",}, #display type
 
-                                  { "co":(1,1),
 
-                                    "interpolation": "LINEAR",
 
-                                    "type":"KEYFRAME",},],
 
-                       "extrapolation": 'CONSTANT', }
 
-         my_driver   ["expression"] = "a"
 
-         
 
-         my_driver = MantisDriver(my_driver)
 
-     # this makes it so I can check for type later!
 
-         
 
-         if self.evaluate_input("Invert Switch") == True:
 
-             my_driver   ["expression"] = "1 - a"
 
-         
 
-         # this way, regardless of what order things are handled, the
 
-         #  driver is sent to the next node.
 
-         # In the case of some drivers, the parameter may be sent out
 
-         #  before it's filled in (because there is a circular dependency)
 
-         # I want to support this behaviour because Blender supports it.
 
-         # We do not make a copy. We update the driver, so that
 
-         #  the same instance is filled out.
 
-         self.parameters["Driver"].update(my_driver)
 
-         print("Initializing driver %s " % (wrapPurple(self.__repr__())) )
 
-         self.executed = True
 
- class UtilityCombineThreeBool(MantisNode):
 
-     '''A node for combining three booleans into a boolean three-tuple'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "X"   ,
 
-           "Y"   ,
 
-           "Z"   ,
 
-         ]
 
-         outputs = [
 
-           "Three-Bool",
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         self.parameters["Three-Bool"] = (
 
-           self.evaluate_input("X"),
 
-           self.evaluate_input("Y"),
 
-           self.evaluate_input("Z"), )
 
-         self.prepared = True
 
-         self.executed = True
 
- # Note this is a copy of the above. This needs to be de-duplicated.
 
- class UtilityCombineVector(MantisNode):
 
-     '''A node for combining three floats into a vector'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "X"   ,
 
-           "Y"   ,
 
-           "Z"   ,
 
-         ]
 
-         outputs = [
 
-           "Vector",
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         #prPurple("Executing CombineVector Node")
 
-         prepare_parameters(self)
 
-         self.parameters["Vector"] = (
 
-           self.evaluate_input("X"),
 
-           self.evaluate_input("Y"),
 
-           self.evaluate_input("Z"), )
 
-         self.prepared = True
 
-         self.executed = True
 
-   
 
- class UtilitySeparateVector(MantisNode):
 
-     '''A node for separating a vector into three floats'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Vector"
 
-         ]
 
-         outputs = [
 
-           "X"   ,
 
-           "Y"   ,
 
-           "Z"   ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         self.parameters["X"] = self.evaluate_input("Vector")[0]
 
-         self.parameters["Y"] = self.evaluate_input("Vector")[1]
 
-         self.parameters["Z"] = self.evaluate_input("Vector")[2]
 
-         self.prepared = True
 
-         self.executed = True
 
- class UtilityCatStrings(MantisNode):
 
-     '''A node representing an armature object'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "String_1"   ,
 
-           "String_2"   ,
 
-         ]
 
-         outputs = [
 
-           "OutputString" ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     
 
-     def bPrepare(self, bContext = None,):
 
-         self.parameters["OutputString"] = self.evaluate_input("String_1")+self.evaluate_input("String_2")
 
-         self.prepared = True
 
-         self.executed = True
 
- class InputExistingGeometryObject(MantisNode):
 
-     '''A node representing an existing object'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Name"  ,
 
-         ]
 
-         outputs = [
 
-           "Object" ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "XFORM"
 
-     
 
-     def bPrepare(self, bContext=None):
 
-         from bpy import data
 
-         name = self.evaluate_input("Name")
 
-         if name:
 
-           self.bObject = data.objects.get( name  )
 
-         else:
 
-           self.bObject = None
 
-         if self is None and (name := self.evaluate_input("Name")):
 
-           prRed(f"No object found with name {name} in {self}")
 
-         self.prepared = True; self.executed = True
 
-     def bGetObject(self, mode=''):
 
-         return self.bObject
 
- class InputExistingGeometryData(MantisNode):
 
-     '''A node representing existing object data'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Name"  ,
 
-         ]
 
-         outputs = [
 
-           "Geometry" ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-         self.prepared = True
 
-         self.executed = True
 
-     # the mode argument is only for interface consistency
 
-     def bGetObject(self, mode=''):
 
-         from bpy import data
 
-         # first try Curve, then try Mesh
 
-         bObject = data.curves.get(self.evaluate_input("Name"))
 
-         if not bObject:
 
-             bObject = data.meshes.get(self.evaluate_input("Name"))
 
-         if bObject is None:
 
-             raise RuntimeError(f"Could not find a mesh or curve datablock named \"{self.evaluate_input('Name')}\" for node {self}")
 
-         return bObject
 
- class UtilityGeometryOfXForm(MantisNode):
 
-     '''A node representing existing object data'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "xForm"  ,
 
-         ]
 
-         outputs = [
 
-           "Geometry" ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-         self.prepared = True
 
-         self.executed = True
 
-     # mode for interface consistency
 
-     def bGetObject(self, mode=''):
 
-         if not (self.inputs.get('xForm') and self.inputs['xForm'].links):
 
-             prOrange(f"WARN: Cannot retrieve data from {self}, there is no xForm node connected.")
 
-             return None
 
-         xf = self.inputs["xForm"].links[0].from_node
 
-         if xf.node_type == 'XFORM':
 
-             xf_ob = xf.bGetObject()
 
-             if xf_ob.type in ['MESH', 'CURVE']:
 
-                 return xf_ob.data
 
-         prOrange(f"WARN: Cannot retrieve data from {self}, the connected xForm is not a mesh or curve.")
 
-         return None
 
- class UtilityNameOfXForm(MantisNode):
 
-     '''A node representing existing object data'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "xForm"  ,
 
-         ]
 
-         outputs = [
 
-           "Name" ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     # mode for interface consistency
 
-     def bPrepare(self, bContext = None,):
 
-         if not (self.inputs.get('xForm') and self.inputs['xForm'].links):
 
-             prOrange(f"WARN: Cannot retrieve data from {self}, there is no xForm node connected.")
 
-             return ''
 
-         xf = self.inputs["xForm"].links[0].from_node
 
-         self.parameters["Name"] = xf.evaluate_input('Name')
 
-         self.prepared, self.executed = True, True
 
- class UtilityGetBoneLength(MantisNode):
 
-     '''A node to get the length of a bone matrix'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Bone Matrix" ,
 
-         ]
 
-         outputs = [
 
-           "Bone Length" ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         if l := self.evaluate_input("Bone Matrix"):
 
-             self.parameters["Bone Length"] = l[3][3]
 
-         self.prepared = True
 
-         self.executed = True
 
- class UtilityPointFromBoneMatrix(MantisNode):
 
-     '''A node representing an armature object'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Bone Matrix"   ,
 
-           "Head/Tail"     ,
 
-         ]
 
-         outputs = [
 
-           "Point"     ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     # TODO: find out why this is sometimes not ready at bPrepare phase
 
-     def bPrepare(self, bContext = None,):
 
-         from mathutils import Vector
 
-         matrix = self.evaluate_input("Bone Matrix")
 
-         head, rotation, _scale = matrix.copy().decompose()
 
-         tail = head.copy() + (rotation @ Vector((0,1,0)))*matrix[3][3]
 
-         self.parameters["Point"] = head.lerp(tail, self.evaluate_input("Head/Tail"))
 
-         self.prepared = True
 
-         self.executed = True
 
- class UtilitySetBoneLength(MantisNode):
 
-     '''Sets the length of a Bone's matrix'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Bone Matrix"   ,
 
-           "Length"        ,
 
-         ]
 
-         outputs = [
 
-           "Bone Matrix"   ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     
 
-     def bPrepare(self, bContext = None,):
 
-         from mathutils import Vector
 
-         if matrix := self.evaluate_input("Bone Matrix"):
 
-             matrix = matrix.copy()
 
-             # print (self.inputs["Length"].links)
 
-             matrix[3][3] = self.evaluate_input("Length")
 
-             self.parameters["Length"] = self.evaluate_input("Length")
 
-             self.parameters["Bone Matrix"] = matrix
 
-         self.prepared = True
 
-         self.executed = True
 
-   
 
- class UtilityMatrixSetLocation(MantisNode):
 
-     '''Sets the location of a matrix'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Matrix"        ,
 
-           "Location"      ,
 
-         ]
 
-         outputs = [
 
-           "Matrix"        ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     
 
-     def bPrepare(self, bContext = None,):
 
-         from mathutils import Vector
 
-         if matrix := self.evaluate_input("Matrix"):
 
-             matrix = matrix.copy()
 
-             # print (self.inputs["Length"].links)
 
-             loc = self.evaluate_input("Location")
 
-             matrix[0][3] = loc[0]; matrix[1][3] = loc[1]; matrix[2][3] = loc[2]
 
-             self.parameters["Matrix"] = matrix
 
-         self.prepared = True
 
-         self.executed = True
 
- class UtilityMatrixGetLocation(MantisNode):
 
-     '''Gets the location of a matrix'''
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Matrix"        ,
 
-         ]
 
-         outputs = [
 
-           "Location"    ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     
 
-     def bPrepare(self, bContext = None,):
 
-         from mathutils import Vector
 
-         if matrix := self.evaluate_input("Matrix"):
 
-             self.parameters["Location"] = matrix.to_translation()
 
-         self.prepared = True; self.executed = True
 
- class UtilityMatrixFromXForm(MantisNode):
 
-     """Returns the matrix of the given xForm node."""
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "xForm"        ,
 
-         ]
 
-         outputs = [
 
-           "Matrix"       ,
 
-         ]
 
-         self.node_type = "UTILITY"
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-     
 
-     def GetxForm(self):
 
-         trace = trace_single_line(self, "xForm")
 
-         for node in trace[0]:
 
-             if (node.node_type == 'XFORM'):
 
-                 return node
 
-         raise GraphError("%s is not connected to an xForm" % self)
 
-         
 
-     def bPrepare(self, bContext = None,):
 
-         from mathutils import Vector, Matrix
 
-         self.parameters["Matrix"] = Matrix.Identity(4)
 
-         if matrix := self.GetxForm().parameters.get("Matrix"):
 
-             self.parameters["Matrix"] = matrix.copy()
 
-         elif hasattr(self.GetxForm().bObject, "matrix"):
 
-             self.parameters["Matrix"] = self.GetxForm().bObject.matrix.copy()
 
-         elif hasattr(self.GetxForm().bObject, "matrix_world"):
 
-             self.parameters["Matrix"] = self.GetxForm().bObject.matrix_world.copy()
 
-         else:
 
-           prRed(f"Could not find matrix for {self} - check if the referenced object exists.")
 
-         self.prepared = True; self.executed = True
 
- class UtilityAxesFromMatrix(MantisNode):
 
-     """Returns the axes of the given matrix."""
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Matrix"       ,
 
-         ]
 
-         outputs = [
 
-           "X Axis"      ,
 
-           "Y Axis"      ,
 
-           "Z Axis"      ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-         
 
-     def bPrepare(self, bContext = None,):
 
-         from mathutils import Vector
 
-         if matrix := self.evaluate_input("Matrix"):
 
-             matrix= matrix.copy().to_3x3()
 
-             self.parameters['X Axis'] = matrix @ Vector((1,0,0))
 
-             self.parameters['Y Axis'] = matrix @ Vector((0,1,0))
 
-             self.parameters['Z Axis'] = matrix @ Vector((0,0,1))
 
-         self.prepared = True; self.executed = True
 
- class UtilityBoneMatrixHeadTailFlip(MantisNode):
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Bone Matrix"     ,
 
-         ]
 
-         outputs = [
 
-           "Bone Matrix"     ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         from mathutils import Vector, Matrix, Quaternion
 
-         from bpy.types import Bone
 
-         if matrix := self.evaluate_input("Bone Matrix"):
 
-             axis, roll = Bone.AxisRollFromMatrix(matrix.to_3x3())
 
-             new_mat = Bone.MatrixFromAxisRoll(-1*axis, roll)
 
-             length = matrix[3][3]
 
-             new_mat.resize_4x4()         # last column contains
 
-             new_mat[0][3] = matrix[0][3] + axis[0]*length # x location
 
-             new_mat[1][3] = matrix[1][3] + axis[1]*length # y location
 
-             new_mat[2][3] = matrix[2][3] + axis[2]*length # z location
 
-             new_mat[3][3] = length                           # length
 
-             self.parameters["Bone Matrix"] = new_mat
 
-         self.prepared, self.executed = True, True
 
- class UtilityMatrixTransform(MantisNode):
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Matrix 1"   ,
 
-           "Matrix 2"   ,
 
-         ]
 
-         outputs = [
 
-           "Out Matrix"    ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         from mathutils import Vector
 
-         mat1 = self.evaluate_input("Matrix 1"); mat2 = self.evaluate_input("Matrix 2")
 
-         if mat1 and mat2:
 
-             mat1copy = mat1.copy()
 
-             self.parameters["Out Matrix"] = mat2 @ mat1copy
 
-             self.parameters["Out Matrix"].translation = mat1copy.to_translation()+ mat2.to_translation()
 
-         else:
 
-             raise RuntimeError(wrapRed(f"Node {self} did not receive all matrix inputs... found input 1? {mat1 is not None}, 2? {mat2 is not None}"))
 
-         self.prepared = True
 
-         self.executed = True
 
- class UtilityTransformationMatrix(MantisNode):
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Operation"   ,
 
-           "Vector"      ,
 
-           "W"           ,
 
-         ]
 
-         outputs = [
 
-           "Matrix"    ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         from mathutils import Matrix, Vector
 
-         if (operation := self.evaluate_input("Operation")) == 'ROTATE_AXIS_ANGLE':
 
-             # this can, will, and should fail if the axis is 0,0,0
 
-             self.parameters["Matrix"] = rotMat = Matrix.Rotation(self.evaluate_input("W"), 4, Vector(self.evaluate_input("Vector")).normalized())
 
-         elif (operation := self.evaluate_input("Operation")) == 'TRANSLATE':
 
-             m = Matrix.Identity(4)
 
-             if axis := self.evaluate_input("Vector"):
 
-                 m[0][3]=axis[0];m[1][3]=axis[1];m[2][3]=axis[2]
 
-             self.parameters['Matrix'] = m
 
-         elif (operation := self.evaluate_input("Operation")) == 'SCALE':
 
-             self.parameters["Matrix"] = Matrix.Scale(self.evaluate_input("W"), 4, Vector(self.evaluate_input("Vector")).normalized())
 
-         else:
 
-             raise NotImplementedError(self.evaluate_input("Operation").__repr__()+ "  Operation not yet implemented.")
 
-         self.prepared = True
 
-         self.executed = True
 
- class UtilityIntToString(MantisNode):
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Number"         ,
 
-           "Zero Padding"   ,
 
-         ]
 
-         outputs = [
 
-           "String"         ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         number = self.evaluate_input("Number")
 
-         zeroes = self.evaluate_input("Zero Padding")
 
-         # I'm casting to int because I want to support any number, even though the node asks for int.
 
-         self.parameters["String"] = str(int(number)).zfill(int(zeroes))
 
-         self.prepared = True
 
-         self.executed = True
 
- class UtilityArrayGet(MantisNode):
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Index"           ,
 
-           "OoB Behaviour"   ,
 
-           "Array"           ,
 
-         ]
 
-         outputs = [
 
-           "Output"          ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-       if self.prepared == False:
 
-         # sort the array entries
 
-         for inp in self.inputs.values():
 
-             inp.links.sort(key=lambda a : -a.multi_input_sort_id)
 
-         oob   = self.evaluate_input("OoB Behaviour")
 
-         index = self.evaluate_input("Index")
 
-         from .utilities import cap, wrap
 
-         
 
-         # we must assume that the array has sent the correct number of links
 
-         if oob == 'WRAP':
 
-             index = index % len(self.inputs['Array'].links)
 
-         if oob == 'HOLD':
 
-             index = cap(index, len(self.inputs['Array'].links)-1)
 
-         # relink the connections and then kill all the links to and from the array
 
-         from .utilities import init_connections, init_dependencies
 
-         l = self.inputs["Array"].links[index]
 
-         for link in self.outputs["Output"].links:
 
-             to_node = link.to_node
 
-             l.from_node.outputs[l.from_socket].connect(to_node, link.to_socket)
 
-             link.die()
 
-             init_dependencies(to_node)
 
-         from_node=l.from_node
 
-         for inp in self.inputs.values():
 
-             for l in inp.links:
 
-               l.die()
 
-         init_connections(from_node)
 
-         if self in from_node.hierarchy_connections:
 
-           raise RuntimeError()
 
-         # this is intentional because the Array Get is kind of a weird hybrid between a Utility and a Schema
 
-         # so it should be removed from the tree when it is done. it has already dealt with the actual links.
 
-         # however I think this is redundant. Check.
 
-         self.hierarchy_connections, self.connections = [], []
 
-         self.hierarchy_dependencies, self.dependencies = [], []
 
-         self.prepared = True
 
-         self.executed = True
 
- class UtilitySetBoneMatrixTail(MantisNode):
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = {
 
-           "Matrix"        ,
 
-           "Tail Location" ,
 
-         }
 
-         outputs = [
 
-           "Result"       ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-       from mathutils import Matrix
 
-       matrix = self.evaluate_input("Matrix")
 
-       if matrix is None: matrix = Matrix.Identity(4)
 
-       #just do this for now lol
 
-       self.parameters["Result"] = matrix_from_head_tail(matrix.translation, self.evaluate_input("Tail Location"))
 
- class UtilityPrint(MantisNode):
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Input"         ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         if my_input := self.evaluate_input("Input"):
 
-             print("Preparation phase: ", wrapWhite(self), wrapGreen(my_input))
 
-         # else:
 
-         #     prRed("No input to print.")
 
-         self.prepared = True
 
-     def bExecute(self, bContext = None,):
 
-         if my_input := self.evaluate_input("Input"):
 
-             print("Execution phase: ", wrapWhite(self), wrapGreen(my_input))
 
-         # else:
 
-         #     prRed("No input to print.")
 
-         self.executed = True
 
- class UtilityCompare(MantisNode):
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "A"           ,
 
-           "B"           ,
 
-         ]
 
-         outputs = [
 
-           "Result"      ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         self.parameters["Result"] = self.evaluate_input("A") == self.evaluate_input("B")
 
-         self.prepared = True; self.executed = True
 
- class UtilityChoose(MantisNode):
 
-     def __init__(self, signature, base_tree):
 
-         super().__init__(signature, base_tree)
 
-         inputs = [
 
-           "Condition"   ,
 
-           "A"           ,
 
-           "B"           ,
 
-         ]
 
-         outputs = [
 
-           "Result"      ,
 
-         ]
 
-         self.inputs.init_sockets(inputs)
 
-         self.outputs.init_sockets(outputs)
 
-         self.init_parameters()
 
-         self.node_type = "UTILITY"
 
-     def bPrepare(self, bContext = None,):
 
-         condition = self.evaluate_input("Condition")
 
-         if condition:
 
-             self.parameters["Result"] = self.evaluate_input("B")
 
-         else:
 
-             self.parameters["Result"] = self.evaluate_input("A")
 
-         self.prepared = True
 
-         self.executed = True
 
 
  |