misc_containers.py 67 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820
  1. from .node_container_common import *
  2. # The fact that I need this means that some of these classes should
  3. # probably be moved to link_containers.py
  4. from .xForm_containers import xFormRoot, xFormArmature, xFormBone
  5. from math import pi, tau
  6. def TellClasses():
  7. return [
  8. # utility
  9. InputFloat,
  10. InputIntNode,
  11. InputVector,
  12. InputBoolean,
  13. InputBooleanThreeTuple,
  14. InputRotationOrder,
  15. InputTransformSpace,
  16. InputString,
  17. InputQuaternion,
  18. InputQuaternionAA,
  19. InputMatrix,
  20. # InputLayerMask,
  21. # InputGeometry,
  22. InputExistingGeometryObject,
  23. InputExistingGeometryData,
  24. UtilityPointFromCurve,
  25. UtilityMatrixFromCurve,
  26. UtilityMatricesFromCurve,
  27. UtilityMetaRig,
  28. UtilityBoneProperties,
  29. UtilityDriverVariable,
  30. UtilityDriver,
  31. UtilityFCurve,
  32. UtilityKeyframe,
  33. UtilitySwitch,
  34. UtilityCombineThreeBool,
  35. UtilityCombineVector,
  36. UtilityCatStrings,
  37. UtilityGetBoneLength,
  38. UtilityPointFromBoneMatrix,
  39. UtilitySetBoneLength,
  40. UtilityMatrixSetLocation,
  41. UtilityMatrixGetLocation,
  42. UtilityMatrixFromXForm,
  43. UtilityAxesFromMatrix,
  44. UtilityBoneMatrixHeadTailFlip,
  45. UtilityMatrixTransform,
  46. UtilityTransformationMatrix,
  47. UtilityIntToString,
  48. UtilityArrayGet,
  49. UtilitySetBoneMatrixTail,
  50. #
  51. UtilityCompare,
  52. UtilityChoose,
  53. #
  54. UtilityPrint,
  55. ]
  56. def matrix_from_head_tail(head, tail):
  57. from mathutils import Vector, Quaternion, Matrix
  58. rotation = Vector((0,1,0)).rotation_difference((tail-head).normalized()).to_matrix()
  59. m= Matrix.LocRotScale(head, rotation, None)
  60. m[3][3] = (tail-head).length
  61. return m
  62. #*#-------------------------------#++#-------------------------------#*#
  63. # G E N E R I C N O D E S
  64. #*#-------------------------------#++#-------------------------------#*#
  65. # in reality, none of these inputs have names
  66. # so I am using the socket name for now
  67. # I suppose I could use any name :3
  68. # TODO: the inputs that do not have names should have an empty string
  69. # TODO after that: make this work with identifiers instead, stupid.
  70. class InputFloat:
  71. '''A node representing float input'''
  72. def __init__(self, signature, base_tree):
  73. self.base_tree=base_tree
  74. self.signature = signature
  75. self.inputs = {}
  76. self.outputs = {"Float Input" : NodeSocket(name = "Float Input", node=self) }
  77. self.parameters = {"Float Input":None, "Mute":None}
  78. self.node_type = 'UTILITY'
  79. self.hierarchy_connections = []
  80. self.connections = []
  81. self.hierarchy_dependencies = []
  82. self.dependencies = []
  83. self.prepared = True
  84. self.executed = True
  85. def evaluate_input(self, input_name):
  86. return self.parameters["Float Input"]
  87. class InputIntNode:
  88. '''A node representing integer input'''
  89. def __init__(self, signature, base_tree):
  90. self.base_tree=base_tree
  91. self.signature = signature
  92. self.inputs = {}
  93. self.outputs = {"Integer" : NodeSocket(name = "Integer", node=self) }
  94. self.parameters = {"Integer":None, "Mute":None}
  95. self.node_type = 'UTILITY'
  96. self.hierarchy_connections = []
  97. self.connections = []
  98. self.hierarchy_dependencies = []
  99. self.dependencies = []
  100. self.prepared = True
  101. self.executed = True
  102. def evaluate_input(self, input_name):
  103. return self.parameters["Integer"]
  104. class InputVector:
  105. '''A node representing vector input'''
  106. def __init__(self, signature, base_tree):
  107. self.base_tree=base_tree
  108. self.signature = signature
  109. self.inputs = {}
  110. self.outputs = {"" : NodeSocket(name = '', node=self) }
  111. self.parameters = {'':None, "Mute":None}
  112. self.node_type = 'UTILITY'
  113. self.hierarchy_connections = []
  114. self.connections = []
  115. self.hierarchy_dependencies = []
  116. self.dependencies = []
  117. self.prepared = True
  118. self.executed = True
  119. def evaluate_input(self, input_name):
  120. return self.parameters[""]
  121. class InputBoolean:
  122. '''A node representing boolean input'''
  123. def __init__(self, signature, base_tree):
  124. self.base_tree=base_tree
  125. self.signature = signature
  126. self.inputs = {}
  127. self.outputs = {"" : NodeSocket(name = '', node=self) }
  128. self.parameters = {'':None, "Mute":None}
  129. self.node_type = 'UTILITY'
  130. self.hierarchy_connections = []
  131. self.connections = []
  132. self.hierarchy_dependencies = []
  133. self.dependencies = []
  134. self.prepared = True
  135. self.executed = True
  136. def evaluate_input(self, input_name):
  137. return self.parameters[""]
  138. class InputBooleanThreeTuple:
  139. '''A node representing inheritance'''
  140. def __init__(self, signature, base_tree):
  141. self.base_tree=base_tree
  142. self.signature = signature
  143. self.inputs = {}
  144. self.outputs = {"" : NodeSocket(name = '', node=self) }
  145. self.parameters = {'':None, "Mute":None}
  146. self.node_type = 'UTILITY'
  147. self.hierarchy_connections = []
  148. self.connections = []
  149. self.hierarchy_dependencies = []
  150. self.dependencies = []
  151. self.prepared = True
  152. self.executed = True
  153. def evaluate_input(self, input_name):
  154. return self.parameters[""]
  155. class InputRotationOrder:
  156. '''A node representing string input for rotation order'''
  157. def __init__(self, signature, base_tree):
  158. self.base_tree=base_tree
  159. self.signature = signature
  160. self.inputs = {}
  161. self.outputs = {"" : NodeSocket(name = '', node=self) }
  162. self.parameters = {'':None, "Mute":None}
  163. self.node_type = 'UTILITY'
  164. self.hierarchy_connections = []
  165. self.connections = []
  166. self.hierarchy_dependencies = []
  167. self.dependencies = []
  168. self.prepared = True
  169. self.executed = True
  170. def evaluate_input(self, input_name):
  171. return self.parameters[""]
  172. class InputTransformSpace:
  173. '''A node representing string input for transform space'''
  174. def __init__(self, signature, base_tree):
  175. self.base_tree=base_tree
  176. self.signature = signature
  177. self.inputs = {}
  178. self.outputs = {"" : NodeSocket(name = '', node=self) }
  179. self.parameters = {'':None, "Mute":None}
  180. self.node_type = 'UTILITY'
  181. self.hierarchy_connections = []
  182. self.connections = []
  183. self.hierarchy_dependencies = []
  184. self.dependencies = []
  185. self.prepared = True
  186. self.executed = True
  187. def evaluate_input(self, input_name):
  188. return self.parameters[""]
  189. class InputString:
  190. '''A node representing string input'''
  191. def __init__(self, signature, base_tree):
  192. self.base_tree=base_tree
  193. self.signature = signature
  194. self.inputs = {}
  195. self.outputs = {"" : NodeSocket(name = '', node=self) }
  196. self.parameters = {'':None, "Mute":None}
  197. self.node_type = 'UTILITY'
  198. self.hierarchy_connections = []
  199. self.connections = []
  200. self.hierarchy_dependencies = []
  201. self.dependencies = []
  202. self.prepared = True
  203. self.executed = True
  204. def evaluate_input(self, input_name):
  205. return self.parameters[""]
  206. class InputQuaternion:
  207. '''A node representing quaternion input'''
  208. def __init__(self, signature, base_tree):
  209. self.base_tree=base_tree
  210. self.signature = signature
  211. self.inputs = {}
  212. self.outputs = {"" : NodeSocket(name = 'QuaternionSocket', node=self) }
  213. self.parameters = {'':None, "Mute":None}
  214. self.node_type = 'UTILITY'
  215. self.hierarchy_connections = []
  216. self.connections = []
  217. self.hierarchy_dependencies = []
  218. self.dependencies = []
  219. self.prepared = True
  220. self.executed = True
  221. def evaluate_input(self, input_name):
  222. return self.parameters[""]
  223. class InputQuaternionAA:
  224. '''A node representing axis-angle quaternion input'''
  225. def __init__(self, signature, base_tree):
  226. self.base_tree=base_tree
  227. self.signature = signature
  228. self.inputs = {}
  229. self.outputs = {"" : NodeSocket(name = 'QuaternionSocketAA', node=self) }
  230. self.parameters = {'':None, "Mute":None}
  231. self.node_type = 'UTILITY'
  232. self.hierarchy_connections = []
  233. self.connections = []
  234. self.hierarchy_dependencies = []
  235. self.dependencies = []
  236. self.prepared = True
  237. self.executed = True
  238. def evaluate_input(self, input_name):
  239. return self.parameters[""]
  240. class InputMatrix:
  241. '''A node representing axis-angle quaternion input'''
  242. def __init__(self, signature, base_tree):
  243. self.base_tree=base_tree
  244. self.signature = signature
  245. self.inputs = {}
  246. self.outputs = {"Matrix" : NodeSocket(name = 'Matrix', node=self) }
  247. self.parameters = {'Matrix':None, "Mute":None}
  248. self.node_type = 'UTILITY'
  249. self.hierarchy_connections = []
  250. self.connections = []
  251. self.hierarchy_dependencies = []
  252. self.dependencies = []
  253. self.prepared = True
  254. self.executed = True
  255. def evaluate_input(self, input_name):
  256. return self.parameters["Matrix"]
  257. def fill_parameters(self):
  258. # this node is peculiar for how its data is input
  259. # It uses node properties that are not addressable as sockets.
  260. from mathutils import Matrix
  261. from .utilities import get_node_prototype
  262. node_prototype = get_node_prototype(self.signature, self.base_tree)
  263. matrix = ( node_prototype.first_row[ 0], node_prototype.first_row[ 1], node_prototype.first_row[ 2], node_prototype.first_row[ 3],
  264. node_prototype.second_row[0], node_prototype.second_row[1], node_prototype.second_row[2], node_prototype.second_row[3],
  265. node_prototype.third_row[ 0], node_prototype.third_row[ 1], node_prototype.third_row[ 2], node_prototype.third_row[ 3],
  266. node_prototype.fourth_row[0], node_prototype.fourth_row[1], node_prototype.fourth_row[2], node_prototype.fourth_row[3], )
  267. self.parameters["Matrix"] = Matrix([matrix[0:4], matrix[4:8], matrix[8:12], matrix[12:16]])
  268. # print (self.parameters["Matrix"])
  269. class UtilityMatrixFromCurve:
  270. '''Get a matrix from a curve'''
  271. def __init__(self, signature, base_tree):
  272. self.base_tree=base_tree
  273. self.executed = False
  274. self.signature = signature
  275. self.inputs = {
  276. "Curve" : NodeSocket(is_input = True, name = "Curve", node=self),
  277. "Total Divisions" : NodeSocket(is_input = True, name = "Total Divisions", node=self),
  278. "Matrix Index" : NodeSocket(is_input = True, name = "Matrix Index", node=self),
  279. }
  280. self.outputs = {
  281. "Matrix" : NodeSocket(name = "Matrix", node=self),
  282. }
  283. self.parameters = {
  284. "Curve" : None,
  285. "Total Divisions" : None,
  286. "Matrix Index" : None,
  287. "Matrix" : None,
  288. }
  289. self.node_type = "UTILITY"
  290. self.hierarchy_connections, self.connections = [], []
  291. self.hierarchy_dependencies, self.dependencies = [], []
  292. self.prepared, self.executed = False, False
  293. def bPrepare(self, bContext = None,):
  294. from mathutils import Matrix
  295. import bpy
  296. m = Matrix.Identity(4)
  297. curve = bpy.data.objects.get(self.evaluate_input("Curve"))
  298. if not curve:
  299. prRed(f"No curve found for {self}. Using an Identity matrix instead.")
  300. m[3][3] = 1.0
  301. else:
  302. from .utilities import mesh_from_curve, data_from_ribbon_mesh
  303. if not bContext:
  304. # TODO find out if this is bad or a HACK or if it is OK
  305. bContext = bpy.context
  306. # IMPORTANT TODO: I need to be able to reuse this m
  307. # First, try to get the one we made before
  308. m_name = curve.name+'.'+self.base_tree.execution_id
  309. if not (m := bpy.data.meshes.get(m_name)):
  310. m = mesh_from_curve(curve, bContext)
  311. m.name = m_name
  312. #
  313. num_divisions = self.evaluate_input("Total Divisions")
  314. m_index = self.evaluate_input("Matrix Index")
  315. factors = [1/num_divisions*m_index, 1/num_divisions*(m_index+1)]
  316. data = data_from_ribbon_mesh(m, [factors], curve.matrix_world)
  317. # print(data)
  318. # this is in world space... let's just convert it back
  319. m = matrix_from_head_tail(data[0][0][0], data[0][0][1])
  320. m.translation -= curve.location
  321. # TODO HACK TODO
  322. # all the nodes should work in world-space, and it should be the responsibility
  323. # of the xForm node to convert!
  324. self.parameters["Matrix"] = m
  325. self.prepared = True
  326. self.executed = True
  327. def bFinalize(self, bContext=None):
  328. import bpy
  329. curve_name = self.evaluate_input("Curve")
  330. curve = bpy.data.objects.get(curve_name)
  331. m_name = curve.name+'.'+self.base_tree.execution_id
  332. if (mesh := bpy.data.meshes.get(m_name)):
  333. bpy.data.meshes.remove(mesh)
  334. def fill_parameters(self):
  335. fill_parameters(self)
  336. class UtilityPointFromCurve:
  337. '''Get a point from a curve'''
  338. def __init__(self, signature, base_tree):
  339. self.base_tree=base_tree
  340. self.executed = False
  341. self.signature = signature
  342. self.inputs = {
  343. "Curve" : NodeSocket(is_input = True, name = "Curve", node=self),
  344. "Factor" : NodeSocket(is_input = True, name = "Factor", node=self),
  345. }
  346. self.outputs = {
  347. "Point" : NodeSocket(name = "Point", node=self),
  348. }
  349. self.parameters = {
  350. "Curve" : None,
  351. "Factor" : None,
  352. "Point" : None,
  353. }
  354. self.node_type = "UTILITY"
  355. self.hierarchy_connections, self.connections = [], []
  356. self.hierarchy_dependencies, self.dependencies = [], []
  357. self.prepared, self.executed = False, False
  358. def bPrepare(self, bContext = None,):
  359. from mathutils import Matrix
  360. import bpy
  361. curve = bpy.data.objects.get(self.evaluate_input("Curve"))
  362. if not curve:
  363. raise RuntimeError(f"No curve found for {self}.")
  364. else:
  365. from .utilities import mesh_from_curve, data_from_ribbon_mesh
  366. if not bContext:
  367. # TODO find out if this is bad or a HACK or if it is OK
  368. bContext = bpy.context
  369. # IMPORTANT TODO: I need to be able to reuse this m
  370. # First, try to get the one we made before
  371. m_name = curve.name+'.'+self.base_tree.execution_id
  372. if not (m := bpy.data.meshes.get(m_name)):
  373. m = mesh_from_curve(curve, bContext)
  374. m.name = m_name
  375. #
  376. num_divisions = 1
  377. factors = [self.evaluate_input("Factor")]
  378. data = data_from_ribbon_mesh(m, [factors], curve.matrix_world)
  379. p = data[0][0][0] - curve.location
  380. self.parameters["Point"] = p
  381. self.prepared, self.executed = True, True
  382. def bFinalize(self, bContext=None):
  383. import bpy
  384. curve_name = self.evaluate_input("Curve")
  385. curve = bpy.data.objects.get(curve_name)
  386. m_name = curve.name+'.'+self.base_tree.execution_id
  387. if (mesh := bpy.data.meshes.get(m_name)):
  388. bpy.data.meshes.remove(mesh)
  389. def fill_parameters(self):
  390. fill_parameters(self)
  391. class UtilityMatricesFromCurve:
  392. '''Get matrices from a curve'''
  393. def __init__(self, signature, base_tree):
  394. self.base_tree=base_tree
  395. self.executed = False
  396. self.signature = signature
  397. self.inputs = {
  398. "Curve" : NodeSocket(is_input = True, name = "Curve", node=self),
  399. "Total Divisions" : NodeSocket(is_input = True, name = "Total Divisions", node=self),
  400. # "Matrix Index" : NodeSocket(is_input = True, name = "Matrix Index", node=self),
  401. }
  402. self.outputs = {
  403. "Matrices" : NodeSocket(name = "Matrices", node=self),
  404. }
  405. self.parameters = {
  406. "Curve" : None,
  407. "Total Divisions" : None,
  408. # "Matrix Index" : None,
  409. "Matrices" : None,
  410. }
  411. self.node_type = "UTILITY"
  412. self.hierarchy_connections = []
  413. self.connections = []
  414. self.hierarchy_dependencies = []
  415. self.dependencies = []
  416. self.prepared = False
  417. self.executed = False
  418. def bPrepare(self, bContext = None,):
  419. import time
  420. # start_time = time.time()
  421. #
  422. from mathutils import Matrix
  423. import bpy
  424. m = Matrix.Identity(4)
  425. curve_name = self.evaluate_input("Curve")
  426. curve = bpy.data.objects.get(curve_name)
  427. if not curve:
  428. prRed(f"No curve found for {self}. Using an Identity matrix instead.")
  429. m[3][3] = 1.0
  430. else:
  431. from .utilities import mesh_from_curve, data_from_ribbon_mesh
  432. if not bContext:
  433. bContext = bpy.context
  434. m_name = curve.name+'.'+self.base_tree.execution_id
  435. if not (mesh := bpy.data.meshes.get(m_name)):
  436. mesh = mesh_from_curve(curve, bContext)
  437. mesh.name = m_name
  438. num_divisions = self.evaluate_input("Total Divisions")
  439. factors = [0.0] + [(1/num_divisions*(i+1)) for i in range(num_divisions)]
  440. data = data_from_ribbon_mesh(mesh, [factors], curve.matrix_world)
  441. # 0 is the spline index. 0 selects points as opposed to normals or whatever.
  442. matrices = [matrix_from_head_tail(data[0][0][i], data[0][0][i+1]) for i in range(num_divisions)]
  443. for link in self.outputs["Matrices"].links:
  444. for i, m in enumerate(matrices):
  445. name = "Matrix"+str(i).zfill(4)
  446. if not (out := self.outputs.get(name)): # reuse them if there are multiple links.
  447. out = self.outputs[name] = NodeSocket(name = name, node=self)
  448. c = out.connect(link.to_node, link.to_socket)
  449. # prOrange(c)
  450. self.parameters[name] = m
  451. # print (mesh)
  452. link.die()
  453. self.prepared = True
  454. self.executed = True
  455. # prGreen(f"Matrices from curves took {time.time() - start_time} seconds.")
  456. def bFinalize(self, bContext=None):
  457. import bpy
  458. curve_name = self.evaluate_input("Curve")
  459. curve = bpy.data.objects.get(curve_name)
  460. m_name = curve.name+'.'+self.base_tree.execution_id
  461. if (mesh := bpy.data.meshes.get(m_name)):
  462. prGreen(f"Freeing mesh data {m_name}...")
  463. bpy.data.meshes.remove(mesh)
  464. def fill_parameters(self):
  465. fill_parameters(self)
  466. class UtilityMetaRig:
  467. '''A node representing an armature object'''
  468. def __init__(self, signature, base_tree):
  469. self.base_tree=base_tree
  470. self.executed = False
  471. self.signature = signature
  472. self.inputs = {
  473. "Meta-Armature" : NodeSocket(is_input = True, name = "Meta-Armature", node=self),
  474. "Meta-Bone" : NodeSocket(is_input = True, name = "Meta-Bone", node=self),
  475. }
  476. self.outputs = {
  477. "Matrix" : NodeSocket(name = "Matrix", node=self),
  478. }
  479. self.parameters = {
  480. "Meta-Armature" : None,
  481. "Meta-Bone" : None,
  482. }
  483. self.node_type = "UTILITY"
  484. self.hierarchy_connections = []
  485. self.connections = []
  486. self.hierarchy_dependencies = []
  487. self.dependencies = []
  488. self.prepared = False
  489. self.executed = False
  490. def bPrepare(self, bContext = None,):
  491. #kinda clumsy, whatever
  492. import bpy
  493. from mathutils import Matrix
  494. m = Matrix.Identity(4)
  495. meta_rig = self.evaluate_input("Meta-Armature")
  496. meta_bone = self.evaluate_input("Meta-Bone")
  497. if meta_rig:
  498. if ( armOb := bpy.data.objects.get(meta_rig) ):
  499. m = armOb.matrix_world
  500. if ( b := armOb.data.bones.get(meta_bone)):
  501. # calculate the correct object-space matrix
  502. m = Matrix.Identity(3)
  503. bones = [] # from the last ancestor, mult the matrices until we get to b
  504. while (b): bones.append(b); b = b.parent
  505. while (bones): b = bones.pop(); m = m @ b.matrix
  506. m = Matrix.Translation(b.head_local) @ m.to_4x4()
  507. #
  508. m[3][3] = b.length # this is where I arbitrarily decided to store length
  509. # else:
  510. # prRed("no bone for MetaRig node ", self)
  511. else:
  512. raise RuntimeError(wrapRed(f"No meta-rig input for MetaRig node {self}"))
  513. self.parameters["Matrix"] = m
  514. self.prepared = True
  515. self.executed = True
  516. def fill_parameters(self):
  517. fill_parameters(self)
  518. # self.parameters["Matrix"] = None # why in the
  519. class UtilityBoneProperties:
  520. '''A node representing an armature object'''
  521. def __init__(self, signature, base_tree):
  522. self.base_tree=base_tree
  523. self.executed = False
  524. self.signature = signature
  525. self.inputs = {}
  526. self.outputs = {
  527. "matrix" : NodeSocket(name = "matrix", node=self),
  528. "matrix_local" : NodeSocket(name = "matrix_local", node=self),
  529. "matrix_basis" : NodeSocket(name = "matrix_basis", node=self),
  530. "head" : NodeSocket(name = "head", node=self),
  531. "tail" : NodeSocket(name = "tail", node=self),
  532. "length" : NodeSocket(name = "length", node=self),
  533. "rotation" : NodeSocket(name = "rotation", node=self),
  534. "location" : NodeSocket(name = "location", node=self),
  535. "scale" : NodeSocket(name = "scale", node=self),
  536. }
  537. self.parameters = {
  538. "matrix":None,
  539. "matrix_local":None,
  540. "matrix_basis":None,
  541. "head":None,
  542. "tail":None,
  543. "length":None,
  544. "rotation":None,
  545. "location":None,
  546. "scale":None,
  547. }
  548. self.node_type = "UTILITY"
  549. #
  550. self.hierarchy_connections = []
  551. self.connections = []
  552. self.hierarchy_dependencies = []
  553. self.dependencies = []
  554. self.prepared = True
  555. self.executed = True
  556. def fill_parameters(self):
  557. pass#fill_parameters(self)
  558. # TODO this should probably be moved to Links
  559. class UtilityDriverVariable:
  560. '''A node representing an armature object'''
  561. def __init__(self, signature, base_tree):
  562. self.base_tree=base_tree
  563. self.executed = False
  564. self.signature = signature
  565. self.inputs = {
  566. "Variable Type" : NodeSocket(is_input = True, name = "Variable Type", node = self),
  567. "Property" : NodeSocket(is_input = True, name = "Property", node = self),
  568. "Property Index" : NodeSocket(is_input = True, name = "Property Index", node = self),
  569. "Evaluation Space" : NodeSocket(is_input = True, name = "Evaluation Space", node = self),
  570. "Rotation Mode" : NodeSocket(is_input = True, name = "Rotation Mode", node = self),
  571. "xForm 1" : NodeSocket(is_input = True, name = "xForm 1", node = self),
  572. "xForm 2" : NodeSocket(is_input = True, name = "xForm 2", node = self),
  573. }
  574. self.outputs = {
  575. "Driver Variable" : NodeSocket(name = "Driver Variable", node=self),
  576. }
  577. self.parameters = {
  578. "Variable Type":None,
  579. "Property":None,
  580. "Property Index":None,
  581. "Evaluation Space":None,
  582. "Rotation Mode":None,
  583. "xForm 1":None,
  584. "xForm 2":None,
  585. }
  586. self.node_type = "DRIVER" # MUST be run in Pose mode
  587. #
  588. self.hierarchy_connections = []
  589. self.connections = []
  590. self.hierarchy_dependencies = []
  591. self.dependencies = []
  592. self.prepared = True
  593. self.executed = False
  594. def evaluate_input(self, input_name):
  595. if input_name == 'Property':
  596. if self.inputs['Property'].is_linked:
  597. # get the name instead...
  598. trace = trace_single_line(self, input_name)
  599. return trace[1].name # the name of the socket
  600. return self.parameters["Property"]
  601. return evaluate_input(self, input_name)
  602. def GetxForm(self, index=1):
  603. trace = trace_single_line(self, "xForm 1" if index == 1 else "xForm 2")
  604. for node in trace[0]:
  605. if (node.__class__ in [xFormRoot, xFormArmature, xFormBone]):
  606. return node #this will fetch the first one, that's good!
  607. return None
  608. def bExecute(self, bContext = None,):
  609. prepare_parameters(self)
  610. #prPurple ("Executing Driver Variable Node")
  611. xForm1 = self.GetxForm()
  612. xForm2 = self.GetxForm(index=2)
  613. # kinda clumsy
  614. if xForm1 : xForm1 = xForm1.bGetObject()
  615. if xForm2 : xForm2 = xForm2.bGetObject()
  616. v_type = self.evaluate_input("Variable Type")
  617. i = self.evaluate_input("Property Index"); dVarChannel = ""
  618. if (i >= 0): #negative values will use the vector property.
  619. if self.evaluate_input("Property") == 'location':
  620. if i == 0: dVarChannel = "LOC_X"
  621. elif i == 1: dVarChannel = "LOC_Y"
  622. elif i == 2: dVarChannel = "LOC_Z"
  623. else: raise RuntimeError("Invalid property index for %s" % self)
  624. if self.evaluate_input("Property") == 'rotation':
  625. if i == 0: dVarChannel = "ROT_X"
  626. elif i == 1: dVarChannel = "ROT_Y"
  627. elif i == 2: dVarChannel = "ROT_Z"
  628. elif i == 3: dVarChannel = "ROT_W"
  629. else: raise RuntimeError("Invalid property index for %s" % self)
  630. if self.evaluate_input("Property") == 'scale':
  631. if i == 0: dVarChannel = "SCALE_X"
  632. elif i == 1: dVarChannel = "SCALE_Y"
  633. elif i == 2: dVarChannel = "SCALE_Z"
  634. elif i == 3: dVarChannel = "SCALE_AVG"
  635. else: raise RuntimeError("Invalid property index for %s" % self)
  636. if self.evaluate_input("Property") == 'scale_average':
  637. dVarChannel = "SCALE_AVG"
  638. if dVarChannel: v_type = "TRANSFORMS"
  639. my_var = {
  640. "owner" : xForm1, # will be filled in by Driver
  641. "prop" : self.evaluate_input("Property"), # will be filled in by Driver
  642. "type" : v_type,
  643. "space" : self.evaluate_input("Evaluation Space"),
  644. "rotation_mode" : self.evaluate_input("Rotation Mode"),
  645. "xForm 1" : xForm1,#self.GetxForm(index = 1),
  646. "xForm 2" : xForm2,#self.GetxForm(index = 2),
  647. "channel" : dVarChannel,}
  648. # Push parameter to downstream connected node.connected:
  649. if (out := self.outputs["Driver Variable"]).is_linked:
  650. self.parameters[out.name] = my_var
  651. for link in out.links:
  652. link.to_node.parameters[link.to_socket] = my_var
  653. self.executed = True
  654. class UtilityKeyframe:
  655. '''A node representing a keyframe for a F-Curve'''
  656. def __init__(self, signature, base_tree):
  657. self.base_tree=base_tree
  658. self.executed = False
  659. self.signature = signature
  660. self.inputs = {
  661. "Frame" : NodeSocket(is_input = True, name = "Frame", node = self),
  662. "Value" : NodeSocket(is_input = True, name = "Value", node = self),
  663. }
  664. self.outputs = {
  665. "Keyframe" : NodeSocket(name = "Keyframe", node=self),
  666. }
  667. self.parameters = {
  668. "Frame":None,
  669. "Value":None,
  670. "Keyframe":{}, # for some reason I have to initialize this and then use the dict... why? TODO find out
  671. }
  672. self.node_type = "DRIVER" # MUST be run in Pose mode
  673. setup_custom_props(self)
  674. self.hierarchy_connections = []
  675. self.connections = []
  676. self.hierarchy_dependencies = []
  677. self.dependencies = []
  678. self.prepared = False
  679. self.executed = False
  680. def bPrepare(self, bContext = None,):
  681. key = self.parameters["Keyframe"]
  682. from mathutils import Vector
  683. key["co"]= Vector( (self.evaluate_input("Frame"), self.evaluate_input("Value"),))
  684. # eventually this will have the right data, TODO
  685. # self.parameters["Keyframe"] = key
  686. self.prepared = True
  687. self.executed = True
  688. class UtilityFCurve:
  689. '''A node representing an armature object'''
  690. def __init__(self, signature, base_tree):
  691. self.base_tree=base_tree
  692. self.executed = False
  693. self.signature = signature
  694. self.inputs = {}
  695. self.outputs = {
  696. "fCurve" : NodeSocket(name = "fCurve", node=self),
  697. }
  698. self.parameters = {
  699. "Use_KeyFrame_Nodes": True, # HACK
  700. "fCurve":None,
  701. }
  702. self.node_type = "UTILITY"
  703. setup_custom_props(self)
  704. self.hierarchy_connections = []
  705. self.connections = []
  706. self.hierarchy_dependencies = []
  707. self.dependencies = []
  708. self.prepared = False
  709. self.executed = False
  710. def evaluate_input(self, input_name):
  711. return evaluate_input(self, input_name)
  712. def bExecute(self, bContext = None,):
  713. prepare_parameters(self)
  714. from .utilities import get_node_prototype
  715. np = get_node_prototype(self.signature, self.base_tree)
  716. keys = []
  717. #['amplitude', 'back', 'bl_rna', 'co', 'co_ui', 'easing', 'handle_left', 'handle_left_type', 'handle_right', 'handle_right_type',
  718. # 'interpolation', 'period', 'rna_type', 'select_control_point', 'select_left_handle', 'select_right_handle', 'type']
  719. if True: #np.use_kf_nodes:
  720. for k in self.inputs.keys():
  721. # print (self.inputs[k])
  722. key = self.evaluate_input(k)
  723. key["type"]="GENERATED"
  724. key["interpolation"] = "LINEAR"
  725. keys.append(key)
  726. else:
  727. raise NotImplementedError("Getting the keys from an fCurve isn't really working anymore lol need to fix")
  728. fc_ob = np.fake_fcurve_ob
  729. fc = fc_ob.animation_data.action.fcurves[0]
  730. for k in fc.keyframe_points:
  731. key = {}
  732. for prop in dir(k):
  733. if ("__" in prop) or ("bl_" in prop): continue
  734. #it's __name__ or bl_rna or something
  735. key[prop] = getattr(k, prop)
  736. keys.append(key)
  737. # Push parameter to downstream connected node.connected:
  738. # TODO: find out if this is necesary, even
  739. if (out := self.outputs["fCurve"]).is_linked:
  740. self.parameters[out.name] = keys
  741. for link in out.links:
  742. link.to_node.parameters[link.to_socket] = keys
  743. # the above was a HACK. I don't want nodes modiying their descenedents.
  744. # If the above was necessary, I want to get an error from it so I can fix it in the descendent's class
  745. self.prepared = True
  746. self.executed = True
  747. class UtilityDriver:
  748. '''A node representing an armature object'''
  749. def __init__(self, signature, base_tree):
  750. self.base_tree=base_tree
  751. self.executed = False
  752. self.signature = signature
  753. self.inputs = {
  754. "Driver Type" : NodeSocket(is_input = True, name = "Driver Type", node = self),
  755. "Expression" : NodeSocket(is_input = True, name = "Expression", node = self),
  756. "fCurve" : NodeSocket(is_input = True, name = "fCurve", node = self),
  757. }
  758. self.outputs = {
  759. "Driver" : NodeSocket(name = "Driver", node=self),
  760. }
  761. from .drivers import MantisDriver
  762. self.parameters = {
  763. "Driver Type":None,
  764. "Expression":None,
  765. "fCurve":None,
  766. "Driver":MantisDriver(),
  767. }
  768. self.node_type = "DRIVER" # MUST be run in Pose mode
  769. setup_custom_props(self)
  770. self.hierarchy_connections = []
  771. self.connections = []
  772. self.hierarchy_dependencies = []
  773. self.dependencies = []
  774. self.prepared = True
  775. self.executed = True
  776. def bExecute(self, bContext = None,):
  777. prepare_parameters(self)
  778. from .drivers import MantisDriver
  779. #prPurple("Executing Driver Node")
  780. my_vars = []
  781. for inp in list(self.inputs.keys() )[3:]:
  782. if (new_var := self.evaluate_input(inp)):
  783. new_var["name"] = inp
  784. my_vars.append(new_var)
  785. else:
  786. raise RuntimeError("Failed to initialize Driver variable")
  787. my_driver ={ "owner" : None,
  788. "prop" : None, # will be filled out in the node that uses the driver
  789. "expression" : self.evaluate_input("Expression"),
  790. "ind" : -1, # same here
  791. "type" : self.evaluate_input("Driver Type"),
  792. "vars" : my_vars,
  793. "keys" : self.evaluate_input("fCurve"), }
  794. my_driver = MantisDriver(my_driver)
  795. self.parameters["Driver"].update(my_driver)
  796. print("Initializing driver %s " % (wrapPurple(self.__repr__())) )
  797. self.executed = True
  798. class UtilitySwitch:
  799. '''A node representing an armature object'''
  800. def __init__(self, signature, base_tree):
  801. self.base_tree=base_tree
  802. self.executed = False
  803. self.signature = signature
  804. self.inputs = {
  805. # "xForm" : NodeSocket(is_input = True, name = "xForm", node = self),
  806. "Parameter" : NodeSocket(is_input = True, name = "Parameter", node = self),
  807. "Parameter Index" : NodeSocket(is_input = True, name = "Parameter Index", node = self),
  808. "Invert Switch" : NodeSocket(is_input = True, name = "Invert Switch", node = self),
  809. }
  810. self.outputs = {
  811. "Driver" : NodeSocket(name = "Driver", node=self),
  812. }
  813. from .drivers import MantisDriver
  814. self.parameters = {
  815. # "xForm":None,
  816. "Parameter":None,
  817. "Parameter Index":None,
  818. "Invert Switch":None,
  819. "Driver":MantisDriver(), # empty for now
  820. }
  821. self.node_type = "DRIVER" # MUST be run in Pose mode
  822. self.hierarchy_connections = []
  823. self.connections = []
  824. self.hierarchy_dependencies = []
  825. self.dependencies = []
  826. self.prepared = True
  827. self.executed = False
  828. def evaluate_input(self, input_name):
  829. if input_name == 'Parameter':
  830. if self.inputs['Parameter'].is_connected:
  831. trace = trace_single_line(self, input_name)
  832. return trace[1].name # the name of the socket
  833. return self.parameters["Parameter"]
  834. return evaluate_input(self, input_name)
  835. def GetxForm(self,):
  836. trace = trace_single_line(self, "Parameter" )
  837. for node in trace[0]:
  838. if (node.__class__ in [xFormRoot, xFormArmature, xFormBone]):
  839. return node #this will fetch the first one, that's good!
  840. return None
  841. def bExecute(self, bContext = None,):
  842. #prepare_parameters(self)
  843. #prPurple ("Executing Switch Node")
  844. xForm = self.GetxForm()
  845. if xForm : xForm = xForm.bGetObject()
  846. if not xForm:
  847. raise RuntimeError("Could not evaluate xForm for %s" % self)
  848. from .drivers import MantisDriver
  849. my_driver ={ "owner" : None,
  850. "prop" : None, # will be filled out in the node that uses the driver
  851. "ind" : -1, # same here
  852. "type" : "SCRIPTED",
  853. "vars" : [ { "owner" : xForm,
  854. "prop" : self.evaluate_input("Parameter"),
  855. "name" : "a",
  856. "type" : "SINGLE_PROP", } ],
  857. "keys" : [ { "co":(0,0),
  858. "interpolation": "LINEAR",
  859. "type":"KEYFRAME",}, #display type
  860. { "co":(1,1),
  861. "interpolation": "LINEAR",
  862. "type":"KEYFRAME",},], }
  863. my_driver ["expression"] = "a"
  864. my_driver = MantisDriver(my_driver)
  865. # this makes it so I can check for type later!
  866. if self.evaluate_input("Invert Switch") == True:
  867. my_driver ["expression"] = "1 - a"
  868. # this way, regardless of what order things are handled, the
  869. # driver is sent to the next node.
  870. # In the case of some drivers, the parameter may be sent out
  871. # before it's filled in (because there is a circular dependency)
  872. # I want to support this behaviour because Blender supports it,
  873. # but I also do not want to support it because it makes things
  874. # more complex and IMO it's bad practice.
  875. # We do not make a copy. We update the driver, so that
  876. # the same instance is filled out.
  877. self.parameters["Driver"].update(my_driver)
  878. print("Initializing driver %s " % (wrapPurple(self.__repr__())) )
  879. self.executed = True
  880. class UtilityCombineThreeBool:
  881. '''A node for combining three booleans into a boolean three-tuple'''
  882. def __init__(self, signature, base_tree):
  883. self.base_tree=base_tree
  884. self.executed = False
  885. self.signature = signature
  886. self.inputs = {
  887. "X" : NodeSocket(is_input = True, name = "X", node = self),
  888. "Y" : NodeSocket(is_input = True, name = "Y", node = self),
  889. "Z" : NodeSocket(is_input = True, name = "Z", node = self),
  890. }
  891. self.outputs = {
  892. "Three-Bool" : NodeSocket(name = "Three-Bool", node=self),
  893. }
  894. self.parameters = {
  895. "X":None,
  896. "Y":None,
  897. "Z":None, }
  898. self.node_type = "UTILITY"
  899. self.hierarchy_connections = []
  900. self.connections = []
  901. self.hierarchy_dependencies = []
  902. self.dependencies = []
  903. self.prepared = False
  904. self.executed = False
  905. def bPrepare(self, bContext = None,):
  906. #prPurple("Executing CombineThreeBool Node")
  907. #prepare_parameters(self)
  908. self.parameters["Three-Bool"] = (
  909. self.evaluate_input("X"),
  910. self.evaluate_input("Y"),
  911. self.evaluate_input("Z"), )
  912. # DO:
  913. # figure out how to get the driver at execute-time
  914. # because Blender allows circular dependencies in drivers
  915. # (sort of), I need to adopt a more convoluted way of doing
  916. # things here or elsewhere.
  917. self.prepared = True
  918. self.executed = True
  919. # Note this is a copy of the above. This needs to be de-duplicated into
  920. # a simpler CombineVector node_container.
  921. # TODO
  922. class UtilityCombineVector:
  923. '''A node for combining three floats into a vector'''
  924. def __init__(self, signature, base_tree):
  925. self.base_tree=base_tree
  926. self.executed = False
  927. self.signature = signature
  928. self.inputs = {
  929. "X" : NodeSocket(is_input = True, name = "X", node = self),
  930. "Y" : NodeSocket(is_input = True, name = "Y", node = self),
  931. "Z" : NodeSocket(is_input = True, name = "Z", node = self),
  932. }
  933. self.outputs = {
  934. "Vector" : NodeSocket(name = "Vector", node=self),
  935. }
  936. self.parameters = {
  937. "X":None,
  938. "Y":None,
  939. "Z":None, }
  940. self.node_type = "UTILITY"
  941. self.hierarchy_connections = []
  942. self.connections = []
  943. self.hierarchy_dependencies = []
  944. self.dependencies = []
  945. self.prepared = False
  946. self.executed = False
  947. def bPrepare(self, bContext = None,):
  948. #prPurple("Executing CombineVector Node")
  949. prepare_parameters(self)
  950. self.parameters["Vector"] = (
  951. self.evaluate_input("X"),
  952. self.evaluate_input("Y"),
  953. self.evaluate_input("Z"), )
  954. self.prepared = True
  955. self.executed = True
  956. class UtilityCatStrings:
  957. '''A node representing an armature object'''
  958. def __init__(self, signature, base_tree):
  959. self.base_tree=base_tree
  960. self.executed = False
  961. self.signature = signature
  962. self.inputs = {
  963. "String_1" : NodeSocket(is_input = True, name = "String_1", node = self),
  964. "String_2" : NodeSocket(is_input = True, name = "String_2", node = self),
  965. }
  966. self.outputs = {
  967. "OutputString" : NodeSocket(name = "OutputString", node=self),
  968. }
  969. self.parameters = {
  970. "String_1":None,
  971. "String_2":None,
  972. }
  973. self.node_type = "UTILITY"
  974. self.hierarchy_connections = []
  975. self.connections = []
  976. self.hierarchy_dependencies = []
  977. self.dependencies = []
  978. self.prepared = False
  979. self.executed = False
  980. def bPrepare(self, bContext = None,):
  981. self.parameters["OutputString"] = self.evaluate_input("String_1")+self.evaluate_input("String_2")
  982. self.prepared = True
  983. self.executed = True
  984. class InputLayerMask:
  985. '''A node representing an armature object'''
  986. def __init__(self, signature, base_tree):
  987. self.base_tree=base_tree
  988. self.executed = False
  989. self.signature = signature
  990. self.inputs = {
  991. }
  992. self.outputs = {
  993. "Layer Mask" : NodeSocket(is_input = True, name = "Layer Mask", node = self),
  994. }
  995. self.parameters = {
  996. "Layer Mask":None,
  997. }
  998. self.node_type = "UTILITY"
  999. self.hierarchy_connections = []
  1000. self.connections = []
  1001. self.hierarchy_dependencies = []
  1002. self.dependencies = []
  1003. self.prepared = True
  1004. self.executed = True
  1005. class InputExistingGeometryObject:
  1006. '''A node representing an existing object'''
  1007. def __init__(self, signature, base_tree):
  1008. self.base_tree=base_tree
  1009. self.executed = False
  1010. self.signature = signature
  1011. self.inputs = {
  1012. "Name" : NodeSocket(is_input = True, name = "Name", node = self),
  1013. }
  1014. self.outputs = {
  1015. "Object" : NodeSocket(is_input = False, name = "Object", node=self),
  1016. }
  1017. self.parameters = {
  1018. "Name":None,
  1019. "Object":None,
  1020. }
  1021. self.node_type = "XFORM"
  1022. self.hierarchy_connections = []
  1023. self.connections = []
  1024. self.hierarchy_dependencies = []
  1025. self.dependencies = []
  1026. self.prepared = False
  1027. self.executed = False
  1028. def bPrepare(self, bContext=None):
  1029. from bpy import data
  1030. self.bObject = data.objects.get( self.evaluate_input("Name") )
  1031. if self is None and (name := self.evaluate_input("Name")):
  1032. prRed(f"No object found with name {name} in {self}")
  1033. self.prepared = True; self.executed = True
  1034. def bGetObject(self, mode=''):
  1035. return self.bObject
  1036. class InputExistingGeometryData:
  1037. '''A node representing existing object data'''
  1038. def __init__(self, signature, base_tree):
  1039. self.base_tree=base_tree
  1040. self.executed = False
  1041. self.signature = signature
  1042. self.inputs = {
  1043. "Name" : NodeSocket(is_input = True, name = "Name", node = self),
  1044. }
  1045. self.outputs = {
  1046. "Geometry" : NodeSocket(is_input = False, name = "Geometry", node=self),
  1047. }
  1048. self.parameters = {
  1049. "Name":None,
  1050. "Geometry":None,
  1051. }
  1052. self.node_type = "UTILITY"
  1053. self.hierarchy_connections = []
  1054. self.connections = []
  1055. self.hierarchy_dependencies = []
  1056. self.dependencies = []
  1057. self.prepared = True
  1058. self.executed = True
  1059. # mode for interface consistency
  1060. def bGetObject(self, mode=''):
  1061. from bpy import data
  1062. # first try Curve, then try Mesh
  1063. bObject = data.curves.get(self.evaluate_input("Name"))
  1064. if not bObject:
  1065. bObject = data.meshes.get(self.evaluate_input("Name"))
  1066. if bObject is None:
  1067. raise RuntimeError(f"Could not find a mesh or curve datablock named \"{self.evaluate_input('Name')}\" for node {self}")
  1068. return bObject
  1069. class UtilityGetBoneLength:
  1070. '''A node to get the length of a bone matrix'''
  1071. def __init__(self, signature, base_tree):
  1072. self.base_tree=base_tree
  1073. self.executed = False
  1074. self.signature = signature
  1075. self.inputs = {
  1076. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1077. }
  1078. self.outputs = {
  1079. "Bone Length" : NodeSocket(name = "Bone Length", node = self),
  1080. }
  1081. self.parameters = {
  1082. "Bone Length":None,
  1083. }
  1084. self.node_type = "UTILITY"
  1085. self.hierarchy_connections = []
  1086. self.connections = []
  1087. self.hierarchy_dependencies = []
  1088. self.dependencies = []
  1089. self.prepared = False
  1090. self.executed = False
  1091. def bPrepare(self, bContext = None,):
  1092. # print (self.inputs['Bone Matrix'].links)
  1093. if l := self.evaluate_input("Bone Matrix"):
  1094. self.parameters["Bone Length"] = l[3][3]
  1095. self.prepared = True
  1096. self.executed = True
  1097. class UtilityPointFromBoneMatrix:
  1098. '''A node representing an armature object'''
  1099. def __init__(self, signature, base_tree):
  1100. self.base_tree=base_tree
  1101. self.executed = False
  1102. self.signature = signature
  1103. self.inputs = {
  1104. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1105. "Head/Tail" : NodeSocket(is_input = True, name = "Head/Tail", node = self),
  1106. }
  1107. self.outputs = {
  1108. "Point" : NodeSocket(name = "Point", node = self),
  1109. }
  1110. self.parameters = {
  1111. "Bone Matrix":None,
  1112. "Head/Tail":None,
  1113. "Point":None,
  1114. }
  1115. self.node_type = "UTILITY"
  1116. self.hierarchy_connections = []
  1117. self.connections = []
  1118. self.hierarchy_dependencies = []
  1119. self.dependencies = []
  1120. self.prepared = False
  1121. self.executed = False
  1122. # TODO: find out why this is sometimes not ready at bPrepare phase
  1123. def bPrepare(self, bContext = None,):
  1124. from mathutils import Vector
  1125. matrix = self.evaluate_input("Bone Matrix")
  1126. head, rotation, _scale = matrix.copy().decompose()
  1127. tail = head.copy() + (rotation @ Vector((0,1,0)))*matrix[3][3]
  1128. self.parameters["Point"] = head.lerp(tail, self.evaluate_input("Head/Tail"))
  1129. self.prepared = True
  1130. self.executed = True
  1131. class UtilitySetBoneLength:
  1132. '''Sets the length of a Bone's matrix'''
  1133. def __init__(self, signature, base_tree):
  1134. self.base_tree=base_tree
  1135. self.executed = False
  1136. self.signature = signature
  1137. self.inputs = {
  1138. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1139. "Length" : NodeSocket(is_input = True, name = "Length", node = self),
  1140. }
  1141. self.outputs = {
  1142. "Bone Matrix" : NodeSocket(name = "Bone Matrix", node = self),
  1143. }
  1144. self.parameters = {
  1145. "Bone Matrix":None,
  1146. "Length":None,
  1147. }
  1148. self.node_type = "UTILITY"
  1149. self.hierarchy_connections = []
  1150. self.connections = []
  1151. self.hierarchy_dependencies = []
  1152. self.dependencies = []
  1153. self.prepared = False
  1154. self.executed = False
  1155. def bPrepare(self, bContext = None,):
  1156. from mathutils import Vector
  1157. if matrix := self.evaluate_input("Bone Matrix"):
  1158. matrix = matrix.copy()
  1159. # print (self.inputs["Length"].links)
  1160. matrix[3][3] = self.evaluate_input("Length")
  1161. self.parameters["Length"] = self.evaluate_input("Length")
  1162. self.parameters["Bone Matrix"] = matrix
  1163. self.prepared = True
  1164. self.executed = True
  1165. class UtilityMatrixSetLocation:
  1166. '''Sets the location of a matrix'''
  1167. def __init__(self, signature, base_tree):
  1168. self.base_tree=base_tree
  1169. self.executed = False
  1170. self.signature = signature
  1171. self.inputs = {
  1172. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1173. "Location" : NodeSocket(is_input = True, name = "Location", node = self),
  1174. }
  1175. self.outputs = {
  1176. "Matrix" : NodeSocket(name = "Matrix", node = self),
  1177. }
  1178. self.parameters = {
  1179. "Matrix" : None,
  1180. "Location" : None,
  1181. }
  1182. self.node_type = "UTILITY"
  1183. self.connections, self.hierarchy_connections = [], []
  1184. self.dependencies, self.hierarchy_dependencies = [], []
  1185. self.prepared, self.executed = False,False
  1186. def bPrepare(self, bContext = None,):
  1187. from mathutils import Vector
  1188. if matrix := self.evaluate_input("Matrix"):
  1189. matrix = matrix.copy()
  1190. # print (self.inputs["Length"].links)
  1191. loc = self.evaluate_input("Location")
  1192. matrix[0][3] = loc[0]; matrix[1][3] = loc[1]; matrix[2][3] = loc[2]
  1193. self.parameters["Matrix"] = matrix
  1194. self.prepared = True
  1195. self.executed = True
  1196. class UtilityMatrixGetLocation:
  1197. '''Gets the location of a matrix'''
  1198. def __init__(self, signature, base_tree):
  1199. self.base_tree=base_tree
  1200. self.signature = signature
  1201. self.inputs = {
  1202. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1203. }
  1204. self.outputs = {
  1205. "Location" : NodeSocket(name = "Location", node = self),
  1206. }
  1207. self.parameters = {
  1208. "Matrix" : None,
  1209. "Location" : None,
  1210. }
  1211. self.node_type = "UTILITY"
  1212. self.connections, self.hierarchy_connections = [], []
  1213. self.dependencies, self.hierarchy_dependencies = [], []
  1214. self.prepared, self.executed = False,False
  1215. def bPrepare(self, bContext = None,):
  1216. from mathutils import Vector
  1217. if matrix := self.evaluate_input("Matrix"):
  1218. self.parameters["Location"] = matrix.to_translation()
  1219. self.prepared = True; self.executed = True
  1220. class UtilityMatrixFromXForm:
  1221. """Returns the matrix of the given xForm node."""
  1222. def __init__(self, signature, base_tree):
  1223. self.base_tree=base_tree
  1224. self.signature = signature
  1225. self.inputs = {
  1226. "xForm" : NodeSocket(is_input = True, name = "xForm", node = self),
  1227. }
  1228. self.outputs = {
  1229. "Matrix" : NodeSocket(name = "Matrix", node = self),
  1230. }
  1231. self.parameters = {
  1232. "Matrix" : None,
  1233. }
  1234. self.node_type = "UTILITY"
  1235. self.connections, self.hierarchy_connections = [], []
  1236. self.dependencies, self.hierarchy_dependencies = [], []
  1237. self.prepared, self.executed = False,False
  1238. def GetxForm(self):
  1239. trace = trace_single_line(self, "xForm")
  1240. for node in trace[0]:
  1241. if (node.node_type == 'XFORM'):
  1242. return node
  1243. raise GraphError("%s is not connected to an xForm" % self)
  1244. def bPrepare(self, bContext = None,):
  1245. from mathutils import Vector, Matrix
  1246. self.parameters["Matrix"] = Matrix.Identity(4)
  1247. if matrix := self.GetxForm().parameters.get("Matrix"):
  1248. self.parameters["Matrix"] = matrix.copy()
  1249. elif hasattr(self.GetxForm().bObject, "matrix"):
  1250. self.parameters["Matrix"] = self.GetxForm().bObject.matrix.copy()
  1251. elif hasattr(self.GetxForm().bObject, "matrix_world"):
  1252. self.parameters["Matrix"] = self.GetxForm().bObject.matrix_world.copy()
  1253. else:
  1254. prRed(f"Could not find matrix for {self} - check if the referenced object exists.")
  1255. self.prepared = True; self.executed = True
  1256. class UtilityAxesFromMatrix:
  1257. """Returns the axes of the given matrix."""
  1258. def __init__(self, signature, base_tree):
  1259. self.base_tree=base_tree
  1260. self.signature = signature
  1261. self.inputs = {
  1262. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1263. }
  1264. self.outputs = {
  1265. "X Axis" : NodeSocket(name = "X Axis", node = self),
  1266. "Y Axis" : NodeSocket(name = "Y Axis", node = self),
  1267. "Z Axis" : NodeSocket(name = "Z Axis", node = self),
  1268. }
  1269. self.parameters = {
  1270. "Matrix" : None,
  1271. "X Axis" : None,
  1272. "Y Axis" : None,
  1273. "Z Axis" : None,
  1274. }
  1275. self.node_type = "UTILITY"
  1276. self.connections, self.hierarchy_connections = [], []
  1277. self.dependencies, self.hierarchy_dependencies = [], []
  1278. self.prepared, self.executed = False,False
  1279. def bPrepare(self, bContext = None,):
  1280. from mathutils import Vector
  1281. if matrix := self.evaluate_input("Matrix"):
  1282. matrix= matrix.copy().to_3x3()
  1283. self.parameters['X Axis'] = matrix @ Vector((1,0,0))
  1284. self.parameters['Y Axis'] = matrix @ Vector((0,1,0))
  1285. self.parameters['Z Axis'] = matrix @ Vector((0,0,1))
  1286. self.prepared = True; self.executed = True
  1287. class UtilityBoneMatrixHeadTailFlip:
  1288. def __init__(self, signature, base_tree):
  1289. self.base_tree=base_tree
  1290. self.executed = False
  1291. self.signature = signature
  1292. self.inputs = {
  1293. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1294. }
  1295. self.outputs = {
  1296. "Bone Matrix" : NodeSocket(name = "Bone Matrix", node = self),
  1297. }
  1298. self.parameters = {
  1299. "Bone Matrix":None,
  1300. }
  1301. self.node_type = "UTILITY"
  1302. self.hierarchy_connections = []
  1303. self.connections = []
  1304. self.hierarchy_dependencies = []
  1305. self.dependencies = []
  1306. self.prepared = False
  1307. self.executed = False
  1308. def bPrepare(self, bContext = None,):
  1309. from mathutils import Vector, Matrix, Quaternion
  1310. from bpy.types import Bone
  1311. if matrix := self.evaluate_input("Bone Matrix"):
  1312. axis, roll = Bone.AxisRollFromMatrix(matrix.to_3x3())
  1313. new_mat = Bone.MatrixFromAxisRoll(-1*axis, roll)
  1314. length = matrix[3][3]
  1315. new_mat.resize_4x4() # last column contains
  1316. new_mat[0][3] = matrix[0][3] + axis[0]*length # x location
  1317. new_mat[1][3] = matrix[1][3] + axis[1]*length # y location
  1318. new_mat[2][3] = matrix[2][3] + axis[2]*length # z location
  1319. new_mat[3][3] = length # length
  1320. self.parameters["Bone Matrix"] = new_mat
  1321. self.prepared, self.executed = True, True
  1322. class UtilityMatrixTransform:
  1323. def __init__(self, signature, base_tree):
  1324. self.base_tree=base_tree
  1325. self.executed = False
  1326. self.signature = signature
  1327. self.inputs = {
  1328. "Matrix 1" : NodeSocket(is_input = True, name = "Matrix 1", node = self),
  1329. "Matrix 2" : NodeSocket(is_input = True, name = "Matrix 2", node = self),
  1330. }
  1331. self.outputs = {
  1332. "Out Matrix" : NodeSocket(name = "Out Matrix", node = self),
  1333. }
  1334. self.parameters = {
  1335. "Matrix 1" : None,
  1336. "Matrix 2" : None,
  1337. "Out Matrix" : None,
  1338. }
  1339. self.node_type = "UTILITY"
  1340. self.hierarchy_connections = []
  1341. self.connections = []
  1342. self.hierarchy_dependencies = []
  1343. self.dependencies = []
  1344. self.prepared = False
  1345. self.executed = False
  1346. def bPrepare(self, bContext = None,):
  1347. from mathutils import Vector
  1348. mat1 = self.evaluate_input("Matrix 1"); mat2 = self.evaluate_input("Matrix 2")
  1349. if mat1 and mat2:
  1350. mat1copy = mat1.copy()
  1351. self.parameters["Out Matrix"] = mat2 @ mat1copy
  1352. self.parameters["Out Matrix"].translation = mat1copy.to_translation()+ mat2.to_translation()
  1353. else:
  1354. raise RuntimeError(wrapRed(f"Node {self} did not receive all matrix inputs... found input 1? {mat1 is not None}, 2? {mat2 is not None}"))
  1355. self.prepared = True
  1356. self.executed = True
  1357. class UtilityTransformationMatrix:
  1358. def __init__(self, signature, base_tree):
  1359. self.base_tree=base_tree
  1360. self.executed = False
  1361. self.signature = signature
  1362. self.inputs = {
  1363. "Operation" : NodeSocket(is_input = True, name = "Operation", node = self),
  1364. "Vector" : NodeSocket(is_input = True, name = "Vector", node = self),
  1365. "W" : NodeSocket(is_input = True, name = "W", node = self),
  1366. }
  1367. self.outputs = {
  1368. "Matrix" : NodeSocket(name = "Matrix", node = self),
  1369. }
  1370. self.parameters = {
  1371. "Operation" : None,
  1372. "Origin" : None,
  1373. "Vector" : None,
  1374. "W" : None,
  1375. "Matrix" : None,
  1376. }
  1377. self.node_type = "UTILITY"
  1378. self.hierarchy_connections = []
  1379. self.connections = []
  1380. self.hierarchy_dependencies = []
  1381. self.dependencies = []
  1382. self.prepared = False
  1383. self.executed = False
  1384. # enumMatrixTransform = (('TRANSLATE', 'Translate', 'Translate'),
  1385. # ('ROTATE_AXIS_ANGLE', "Rotate (Vector-angle)", "Rotates a number of radians around an axis"),
  1386. # ('ROTATE_EULER', "Rotate (Euler)", "Euler Rotation"),
  1387. # ('ROTATE_QUATERNION', "Rotate (Quaternion)", "Quaternion Rotation"),
  1388. # ('SCALE', "Scale", "Scale"),)
  1389. def bPrepare(self, bContext = None,):
  1390. from mathutils import Matrix, Vector
  1391. if (operation := self.evaluate_input("Operation")) == 'ROTATE_AXIS_ANGLE':
  1392. # this can, will, and should fail if the axis is 0,0,0
  1393. self.parameters["Matrix"] = rotMat = Matrix.Rotation(self.evaluate_input("W"), 4, Vector(self.evaluate_input("Vector")).normalized())
  1394. elif (operation := self.evaluate_input("Operation")) == 'TRANSLATE':
  1395. m = Matrix.Identity(4)
  1396. if axis := self.evaluate_input("Vector"):
  1397. m[0][3]=axis[0];m[1][3]=axis[1];m[2][3]=axis[2]
  1398. self.parameters['Matrix'] = m
  1399. else:
  1400. raise NotImplementedError(self.evaluate_input("Operation").__repr__()+ " Operation not yet implemented.")
  1401. self.prepared = True
  1402. self.executed = True
  1403. class UtilityIntToString:
  1404. def __init__(self, signature, base_tree):
  1405. self.base_tree=base_tree
  1406. self.executed = False
  1407. self.signature = signature
  1408. self.inputs = {
  1409. "Number" : NodeSocket(is_input = True, name = "Number", node = self),
  1410. "Zero Padding" : NodeSocket(is_input = True, name = "Zero Padding", node = self),
  1411. }
  1412. self.outputs = {
  1413. "String" : NodeSocket(name = "String", node = self),
  1414. }
  1415. self.parameters = {
  1416. "Number" : None,
  1417. "Zero Padding" : None,
  1418. "String" : None,
  1419. }
  1420. self.node_type = "UTILITY"
  1421. self.hierarchy_connections = []
  1422. self.connections = []
  1423. self.hierarchy_dependencies = []
  1424. self.dependencies = []
  1425. self.prepared = False
  1426. self.executed = False
  1427. def bPrepare(self, bContext = None,):
  1428. number = self.evaluate_input("Number")
  1429. zeroes = self.evaluate_input("Zero Padding")
  1430. # I'm casting to int because I want to support any number, even though the node asks for int.
  1431. self.parameters["String"] = str(int(number)).zfill(int(zeroes))
  1432. self.prepared = True
  1433. self.executed = True
  1434. class UtilityArrayGet:
  1435. def __init__(self, signature, base_tree):
  1436. self.base_tree=base_tree
  1437. self.executed = False
  1438. self.signature = signature
  1439. self.inputs = {
  1440. "Index" : NodeSocket(is_input = True, name = "Index", node = self),
  1441. "OoB Behaviour" : NodeSocket(is_input = True, name = "OoB Behaviour", node = self),
  1442. "Array" : NodeSocket(is_input = True, name = "Array", node = self),
  1443. }
  1444. self.outputs = {
  1445. "Output" : NodeSocket(name = "Output", node = self),
  1446. }
  1447. self.parameters = {
  1448. "Index" : None,
  1449. "OoB Behaviour" : None,
  1450. "Array" : None,
  1451. "Output" : None,
  1452. }
  1453. self.node_type = "UTILITY"
  1454. self.hierarchy_connections = []
  1455. self.connections = []
  1456. self.hierarchy_dependencies = []
  1457. self.dependencies = []
  1458. self.prepared = False
  1459. self.executed = False
  1460. def bPrepare(self, bContext = None,):
  1461. if self.prepared == False:
  1462. # sort the array entries
  1463. for inp in self.inputs.values():
  1464. inp.links.sort(key=lambda a : -a.multi_input_sort_id)
  1465. oob = self.evaluate_input("OoB Behaviour")
  1466. index = self.evaluate_input("Index")
  1467. from .utilities import cap, wrap
  1468. # we must assume that the array has sent the correct number of links
  1469. if oob == 'WRAP':
  1470. index = index % len(self.inputs['Array'].links)
  1471. if oob == 'HOLD':
  1472. index = cap(index, len(self.inputs['Array'].links)-1)
  1473. # relink the connections and then kill all the links to and from the array
  1474. from .utilities import init_connections, init_dependencies
  1475. l = self.inputs["Array"].links[index]
  1476. for link in self.outputs["Output"].links:
  1477. to_node = link.to_node
  1478. l.from_node.outputs[l.from_socket].connect(to_node, link.to_socket)
  1479. link.die()
  1480. init_dependencies(to_node)
  1481. from_node=l.from_node
  1482. for inp in self.inputs.values():
  1483. for l in inp.links:
  1484. l.die()
  1485. init_connections(from_node)
  1486. if self in from_node.hierarchy_connections:
  1487. raise RuntimeError()
  1488. # this is intentional because the Array Get is kind of a weird hybrid between a Utility and a Schema
  1489. # so it should be removed from the tree when it is done. it has already dealt with the actual links.
  1490. # however I think this is redundant. Check.
  1491. self.hierarchy_connections, self.connections = [], []
  1492. self.hierarchy_dependencies, self.dependencies = [], []
  1493. self.prepared = True
  1494. self.executed = True
  1495. class UtilitySetBoneMatrixTail:
  1496. def __init__(self, signature, base_tree):
  1497. self.base_tree=base_tree
  1498. self.executed = False
  1499. self.signature = signature
  1500. self.inputs = {
  1501. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1502. "Tail Location" : NodeSocket(is_input = True, name = "Tail Location", node = self),
  1503. }
  1504. self.outputs = {
  1505. "Result" : NodeSocket(name = "Result", node = self),
  1506. }
  1507. self.parameters = {
  1508. "Matrix" : None,
  1509. "Tail Location" : None,
  1510. "Result" : None,
  1511. }
  1512. self.node_type = "UTILITY"
  1513. self.hierarchy_connections, self.connections = [], []
  1514. self.hierarchy_dependencies, self.dependencies = [], []
  1515. self.prepared, self.executed = False, False
  1516. def bPrepare(self, bContext = None,):
  1517. from mathutils import Matrix
  1518. matrix = self.evaluate_input("Matrix")
  1519. if matrix is None: matrix = Matrix.Identity(4)
  1520. #just do this for now lol
  1521. self.parameters["Result"] = matrix_from_head_tail(matrix.translation, self.evaluate_input("Tail Location"))
  1522. class UtilityPrint:
  1523. def __init__(self, signature, base_tree):
  1524. self.base_tree=base_tree
  1525. self.executed = False
  1526. self.signature = signature
  1527. self.inputs = {
  1528. "Input" : NodeSocket(is_input = True, name = "Input", node = self),
  1529. }
  1530. self.outputs = {}
  1531. self.parameters = {
  1532. "Input" : None,
  1533. }
  1534. self.node_type = "UTILITY"
  1535. self.hierarchy_connections = []
  1536. self.connections = []
  1537. self.hierarchy_dependencies = []
  1538. self.dependencies = []
  1539. self.prepared = False
  1540. self.executed = False
  1541. def bPrepare(self, bContext = None,):
  1542. if my_input := self.evaluate_input("Input"):
  1543. print("Preparation phase: ", wrapWhite(self), wrapGreen(my_input))
  1544. # else:
  1545. # prRed("No input to print.")
  1546. self.prepared = True
  1547. def bExecute(self, bContext = None,):
  1548. if my_input := self.evaluate_input("Input"):
  1549. print("Execution phase: ", wrapWhite(self), wrapGreen(my_input))
  1550. # else:
  1551. # prRed("No input to print.")
  1552. self.executed = True
  1553. class UtilityCompare:
  1554. def __init__(self, signature, base_tree):
  1555. self.base_tree=base_tree
  1556. self.executed = False
  1557. self.signature = signature
  1558. self.inputs = {
  1559. "A" : NodeSocket(is_input = True, name = "A", node = self),
  1560. "B" : NodeSocket(is_input = True, name = "B", node = self),
  1561. }
  1562. self.outputs = {
  1563. "Result" : NodeSocket(name = "Result", node = self),
  1564. }
  1565. self.parameters = {
  1566. "A" : None,
  1567. "B" : None,
  1568. "Result" : None,
  1569. }
  1570. self.node_type = "UTILITY"
  1571. self.hierarchy_connections, self.connections = [], []
  1572. self.hierarchy_dependencies, self.dependencies = [], []
  1573. self.prepared, self.executed = False, False
  1574. def bPrepare(self, bContext = None,):
  1575. self.parameters["Result"] = self.evaluate_input("A") == self.evaluate_input("B")
  1576. self.prepared = True; self.executed = True
  1577. class UtilityChoose:
  1578. def __init__(self, signature, base_tree):
  1579. self.base_tree=base_tree
  1580. self.executed = False
  1581. self.signature = signature
  1582. self.inputs = {
  1583. "Condition" : NodeSocket(is_input = True, name = "Condition", node = self),
  1584. "A" : NodeSocket(is_input = True, name = "A", node = self),
  1585. "B" : NodeSocket(is_input = True, name = "B", node = self),
  1586. }
  1587. self.outputs = {
  1588. "Result" : NodeSocket(name = "Result", node = self),
  1589. }
  1590. self.parameters = {
  1591. "Condition" : None,
  1592. "A" : None,
  1593. "B" : None,
  1594. "Result" : None,
  1595. }
  1596. self.node_type = "UTILITY"
  1597. self.hierarchy_connections, self.connections = [], []
  1598. self.hierarchy_dependencies, self.dependencies = [], []
  1599. self.prepared, self.executed = False, False
  1600. def bPrepare(self, bContext = None,):
  1601. condition = self.evaluate_input("Condition")
  1602. if condition:
  1603. self.parameters["Result"] = self.evaluate_input("B")
  1604. else:
  1605. self.parameters["Result"] = self.evaluate_input("A")
  1606. self.prepared = True
  1607. self.executed = True
  1608. for c in TellClasses():
  1609. setup_container(c)