add_node.sh 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. #!/bin/bash
  2. # A tool for adding the boilerplate code for adding a new node to the tree.
  3. parentclass=""
  4. nodefile=""
  5. cnodefile=""
  6. classfile=""
  7. icon=""
  8. # title: name menu: name, H W menu size pairs of tag,description, trick to swap stdout and stderr
  9. nodetype=$(whiptail --title "add_node.sh" --menu "Node Type" 25 78 16 "xForm" "" "Link" "" "Utility" "" "Math" "" 3>&2 2>&1 1>&3)
  10. if [[ $nodetype == "" ]]; then
  11. echo "Cancelled."
  12. else
  13. case $nodetype in
  14. Link)
  15. nodefile="link_definitions.py"
  16. parentclass="LinkNode"
  17. cnodefile="link_containers.py"
  18. icon="CONSTRAINT_BONE"
  19. ;;
  20. xForm)
  21. nodefile="xForm_definitions.py"
  22. cnodefile="xForm_containers.py"
  23. parentclass="xFormNode"
  24. icon="EMPTY_AXIS"
  25. ;;
  26. Utility)
  27. nodefile="nodes_generic.py"
  28. cnodefile="misc_containers.py"
  29. parentclass="MantisNode"
  30. icon="NODE"
  31. ;;
  32. Math)
  33. nodefile="math_definitions.py"
  34. cnodefile="math_containers.py"
  35. parentclass="MantisNode"
  36. icon="NODE"
  37. ;;
  38. esac
  39. cancelled=0
  40. check_cancelled() {
  41. if [[ $cancelled == "1" ]]; then
  42. echo "Cancelled."
  43. exit
  44. fi
  45. }
  46. #read class_name
  47. class_name=$(whiptail --inputbox "Name of new node?" 8 39 "" --title "add_node.sh" 3>&1 1>&2 2>&3)
  48. cancelled=$?; check_cancelled
  49. whiptail --title "add_node.sh" --msgbox \
  50. "Node class will be named $nodetype$class_name$n_text with bl_idname $nodetype$class_name" 8 78
  51. bl_label=$(whiptail --title "add_node.sh" --inputbox "UI Label new node class?" 8 39 "" 3>&1 1>&2 2>&3)
  52. cancelled=$?; check_cancelled
  53. docstring=$(whiptail --title "add_node.sh" --inputbox "Docstring for new node class?" 8 39 "" 3>&1 1>&2 2>&3)
  54. cancelled=$?; check_cancelled
  55. # TODO: I would like to be able to define the number of inputs and their names
  56. # and the script will add them to the file
  57. # it should also give me the option to choose socket type and such
  58. # Utility nodes would also give the option of adding outputs.
  59. declare -i num_inputs="0"
  60. declare -i num_outputs="0"
  61. if [[ $nodetype == 'Link' || $nodetype == 'Utility' ]]; then
  62. choice=$(whiptail --title "add_node.sh"\
  63. --inputbox "Number of inputs? Note: This number is in addition to the default inputs." 8 39 "0" 3>&1 1>&2 2>&3)
  64. if [[ "$choice" -eq "$choice" ]]; then
  65. num_inputs=$choice
  66. else
  67. echo "Error, must be a number"
  68. cancelled=1
  69. fi
  70. cancelled=$?; check_cancelled
  71. elif [[ $nodetype == 'Utility' ]]; then
  72. choice=$(whiptail --title "add_node.sh"\
  73. --inputbox "Number of outputs?" 8 39 "0" 3>&1 1>&2 2>&3)
  74. if [[ "$choice" -eq "$choice" ]]; then
  75. num_outputs=$choice
  76. else
  77. echo "Error, must be a number"
  78. cancelled=1
  79. fi
  80. cancelled=$?; check_cancelled
  81. elif [[ $nodetype == 'Math' ]]; then
  82. choice=$(whiptail --title "add_node.sh"\
  83. --inputbox "Number of inputs?" 8 39 "0" 3>&1 1>&2 2>&3)
  84. if [[ "$choice" -eq "$choice" ]]; then
  85. num_inputs=$choice
  86. else
  87. echo "Error, must be a number"
  88. cancelled=1
  89. fi
  90. cancelled=$?; check_cancelled
  91. choice=$(whiptail --title "add_node.sh"\
  92. --inputbox "Number of outputs?" 8 39 "0" 3>&1 1>&2 2>&3)
  93. if [[ "$choice" -eq "$choice" ]]; then
  94. num_outputs=$choice
  95. else
  96. echo "Error, must be a number"
  97. cancelled=1
  98. fi
  99. cancelled=$?; check_cancelled
  100. else
  101. num_inputs=0
  102. fi
  103. if [[ $nodetype == 'Utility' && $num_inputs == "0" && $num_outputs == "0" ]]; then
  104. echo "Error. The node must have at least one socket."
  105. exit
  106. fi
  107. # now, set a flag if this is a utility and has no inputs but has outputs
  108. isinput=0
  109. if [[ $nodetype == 'Utility' && $num_inputs == "0" && $num_outputs -gt 0 ]]; then
  110. isinput=1
  111. fi
  112. n_text=Node # surely there is a better way to do this...
  113. classheader=\
  114. "\nclass $nodetype$class_name$n_text(Node, $parentclass):
  115. \"\"\"$docstring\"\"\"
  116. bl_idname = \"$nodetype$class_name\"
  117. bl_label = \"$bl_label\"
  118. bl_icon = \"$icon\"
  119. def init(self, context):\n"
  120. printf "$(echo "$classheader")">classheader.txt
  121. #now do the cnode:
  122. echo "class $nodetype$class_name:" > cnode_def # this is the bl_idname!! important!
  123. echo " '''A node representing an armature object'''" >> cnode_def
  124. echo >> cnode_def
  125. if [[ $nodetype == 'xForm' ]]; then
  126. echo " bObject = None echo " >> cnode_def
  127. echo >> cnode_def
  128. fi
  129. echo " def __init__(self, signature, base_tree):" >> cnode_def
  130. echo " self.base_tree=base_tree" >> cnode_def
  131. echo " self.executed = False" >> cnode_def
  132. echo " self.signature = signature" >> cnode_def
  133. echo " self.inputs = {" >> cnode_def
  134. echo > parameters
  135. if [[ $nodetype == 'xForm' ]]; then
  136. #node
  137. echo " self.inputs.new('StringSocket', \"Name\")" >> classheader.txt
  138. echo " self.inputs.new('MatrixSocket', \"Matrix\")" >> classheader.txt
  139. echo " self.inputs.new('RelationshipSocket', \"Relationship\")" >> classheader.txt
  140. #cnode
  141. echo " \"Name\" : NodeSocket(is_input = True, name = \"Name\", node = self)," >> cnode_def
  142. echo " \"Rotation Order\" : NodeSocket(is_input = True, name = \"Rotation Order\", node = self)," >> cnode_def
  143. echo " \"Matrix\" : NodeSocket(is_input = True, name = \"Matrix\", node = self)," >> cnode_def
  144. echo " \"Relationship\" : NodeSocket(is_input = True, name = \"Relationship\", node = self)," >> cnode_def
  145. #parameters; should be identical to cnode inputs
  146. echo " \"Name\":None, " >> parameters
  147. echo " \"Rotation Order\":None, " >> parameters
  148. echo " \"Matrix\":None, " >> parameters
  149. echo " \"Relationship\":None, " >> parameters
  150. elif [[ $nodetype == 'Link' ]]; then
  151. #node
  152. echo " self.inputs.new (\"RelationshipSocket\", \"Input Relationship\")" >> classheader.txt
  153. #cnode
  154. echo " \"Input Relationship\" : NodeSocket(is_input = True, name = \"Input Relationship\", node = self,)," >> cnode_def
  155. #parameters; should be identical to cnode inputs
  156. echo " \"Input Relationship\":None, " >> parameters
  157. fi
  158. # New Inputs
  159. until [[ $num_inputs == "0" ]]; do
  160. sockettype=$(whiptail --title "add_node.sh" --menu "Input Socket Type" 25 78 16\
  161. "RelationshipSocket" ""\
  162. "MatrixSocket" "" \
  163. "xFormSocket" ""\
  164. "GenericRotationSocket" ""\
  165. "RelationshipSocket" ""\
  166. "xFormParameterSocket" ""\
  167. "DriverSocket" ""\
  168. "DriverVariableSocket" ""\
  169. "TransformSpaceSocket" ""\
  170. "BooleanSocket" ""\
  171. "BooleanThreeTupleSocket" ""\
  172. "RotationOrderSocket" ""\
  173. "QuaternionSocket" ""\
  174. "QuaternionSocketAA" ""\
  175. "IntSocket" ""\
  176. "GeometrySocket" ""\
  177. "StringSocket" ""\
  178. "BoneCollectionSocket" ""\
  179. "BoolUpdateParentNode" ""\
  180. "LabelSocket" ""\
  181. "IKChainLengthSocket" ""\
  182. "EnumInheritScale" ""\
  183. "EnumRotationMix" ""\
  184. "EnumRotationMixCopyTransforms" ""\
  185. "EnumMaintainVolumeStretchTo" ""\
  186. "EnumRotationStretchTo" ""\
  187. "EnumTrackAxis" ""\
  188. "EnumUpAxis" ""\
  189. "EnumLockAxis" ""\
  190. "EnumLimitMode" ""\
  191. "EnumDriverVariableType" ""\
  192. "EnumDriverVariableEvaluationSpace" ""\
  193. "EnumDriverRotationMode" ""\
  194. "FloatSocket" ""\
  195. "FloatFactorSocket" ""\
  196. "FloatAngleSocket" ""\
  197. "VectorSocket" ""\
  198. "VectorEulerSocket" ""\
  199. "VectorTranslationSocket" ""\
  200. "VectorScaleSocket" ""\
  201. 3>&2 2>&1 1>&3)
  202. socketname=$(whiptail --title "add_node.sh"\
  203. --inputbox "Input Socket Name" 8 39 "" 3>&1 1>&2 2>&3)
  204. cancelled=$?; check_cancelled
  205. ((num_inputs = num_inputs-1))
  206. #node
  207. echo " self.inputs.new(\"$sockettype\", \"$socketname\")" >> classheader.txt
  208. #cnode
  209. echo " \"$socketname\" : NodeSocket(is_input = True, name = \"$socketname\", node = self)," >> cnode_def
  210. #parameters; should be identical to cnode inputs
  211. echo " \"$socketname\":None, " >> parameters
  212. done
  213. echo " }" >> cnode_def
  214. echo " self.outputs = {" >> cnode_def
  215. # add the defaults for xForm, Link:
  216. if [[ $nodetype == 'xForm' ]]; then
  217. #node
  218. echo " self.outputs.new('xFormSocket', \"xForm Out\")" >> classheader.txt
  219. #cnode
  220. echo " \"xForm Out\" : NodeSocket(name=\"xForm Out\", node = self), }" >> cnode_def
  221. elif [[ $nodetype == 'Link' ]]; then
  222. #node
  223. echo " self.outputs.new(\"RelationshipSocket\", \"Output Relationship\")" >> classheader.txt
  224. #cnode
  225. echo " \"Output Relationship\" : NodeSocket(name = \"Output Relationship\", node=self), }" >> cnode_def
  226. # New Outputs
  227. elif [[ $nodetype == 'Utility' || $nodetype == 'Math' ]]; then
  228. until [[ $num_outputs == "0" ]]; do
  229. sockettype=$(whiptail --title "add_node.sh" --menu "Output Socket Type" 25 78 16\
  230. "RelationshipSocket" ""\
  231. "MatrixSocket" "" \
  232. "xFormSocket" ""\
  233. "GenericRotationSocket" ""\
  234. "RelationshipSocket" ""\
  235. "xFormParameterSocket" ""\
  236. "DriverSocket" ""\
  237. "DriverVariableSocket" ""\
  238. "TransformSpaceSocket" ""\
  239. "BooleanSocket" ""\
  240. "BooleanThreeTupleSocket" ""\
  241. "RotationOrderSocket" ""\
  242. "QuaternionSocket" ""\
  243. "QuaternionSocketAA" ""\
  244. "IntSocket" ""\
  245. "GeometrySocket" ""\
  246. "StringSocket" ""\
  247. "BoneCollectionSocket" ""\
  248. "BoolUpdateParentNode" ""\
  249. "LabelSocket" ""\
  250. "IKChainLengthSocket" ""\
  251. "EnumInheritScale" ""\
  252. "EnumRotationMix" ""\
  253. "EnumRotationMixCopyTransforms" ""\
  254. "EnumMaintainVolumeStretchTo" ""\
  255. "EnumRotationStretchTo" ""\
  256. "EnumTrackAxis" ""\
  257. "EnumUpAxis" ""\
  258. "EnumLockAxis" ""\
  259. "EnumLimitMode" ""\
  260. "EnumDriverVariableType" ""\
  261. "EnumDriverVariableEvaluationSpace" ""\
  262. "EnumDriverRotationMode" ""\
  263. "FloatSocket" ""\
  264. "FloatFactorSocket" ""\
  265. "FloatAngleSocket" ""\
  266. "VectorSocket" ""\
  267. "VectorEulerSocket" ""\
  268. "VectorTranslationSocket" ""\
  269. "VectorScaleSocket" ""\
  270. 3>&2 2>&1 1>&3)
  271. socketname=$(whiptail --title "add_node.sh"\
  272. --inputbox "Output Socket Name" 8 39 "" 3>&1 1>&2 2>&3)
  273. cancelled=$?; check_cancelled
  274. ((num_outputs = num_outputs-1))
  275. #node
  276. echo " self.outputs.new(\"$sockettype\", \"$socketname\")" >> classheader.txt
  277. #cnode
  278. echo " \"$socketname\" : NodeSocket(name = \"$socketname\", node=self)," >> cnode_def
  279. #parameters , this time it should by the cnode outputs!
  280. echo " \"$socketname\":None, " >> parameters
  281. done
  282. echo " }" >> cnode_def
  283. fi
  284. #cnode
  285. echo " self.parameters = {" >> cnode_def
  286. cat parameters >> cnode_def
  287. echo " }" >> cnode_def
  288. if [[ $nodetype == 'xForm' ]]; then
  289. echo " self.links = {} # leave this empty for now!" >> cnode_def
  290. echo " # now set up the traverse target..." >> cnode_def
  291. echo " self.inputs["Relationship"].set_traverse_target(self.outputs["xForm Out"])" >> cnode_def
  292. echo " self.outputs["xForm Out"].set_traverse_target(self.inputs["Relationship"])" >> cnode_def
  293. echo " self.node_type = \"XFORM\"" >> cnode_def
  294. elif [[ $nodetype == 'Link' ]]; then
  295. echo " # now set up the traverse target..." >> cnode_def
  296. echo " self.inputs[\"Input Relationship\"].set_traverse_target(self.outputs[\"Output Relationship\"])" >> cnode_def
  297. echo " self.outputs[\"Output Relationship\"].set_traverse_target(self.inputs[\"Input Relationship\"])" >> cnode_def
  298. echo " self.node_type = \"LINK\"" >> cnode_def
  299. else
  300. echo " self.node_type = \"UTILITY\"" >> cnode_def
  301. fi
  302. echo >> cnode_def
  303. echo " def evaluate_input(self, input_name):" >> cnode_def
  304. echo " return evaluate_input(self, input_name)" >> cnode_def
  305. echo >> cnode_def
  306. echo " def bExecute(self, bContext = None,):" >> cnode_def
  307. echo " return" >> cnode_def
  308. echo >> cnode_def
  309. echo " def __repr__(self):" >> cnode_def
  310. echo " return self.signature.__repr__()" >> cnode_def
  311. echo >> cnode_def
  312. echo " def fill_parameters(self):" >> cnode_def
  313. echo " fill_parameters(self)" >> cnode_def
  314. # now it's done!
  315. cat cnode_def >> $cnodefile
  316. #time to fill upo the node definition
  317. echo " def traverse(self, socket):" >> classheader.txt
  318. echo " return default_traverse(self,socket)" >> classheader.txt
  319. # NODE FILE
  320. # operate on a duplicate of the file, use sed to rename.
  321. bakfile=$(echo $nodefile | sed s/.py/.bak.py/g)
  322. cp $nodefile $bakfile
  323. #find the line that is at the end of TellClasses:
  324. declare -i tc_end=$(grep -n -m 1 "]" $bakfile | cut -f1 -d:)
  325. ((tc_end=$tc_end-1))
  326. # the total length of the file, in lines
  327. nodefile_len=$(cat $bakfile | wc -l)
  328. #get indentation level
  329. declare -i ind_level=$(head -n $tc_end $bakfile | tail -n 1 | grep -o " " | wc -l)
  330. #create the string (the class name with the proper indentation, ending in a comma).
  331. tc_line_add="$tc_line_add$nodetype$class_name$n_text"
  332. until [ $ind_level == 0 ]
  333. do
  334. tc_line_add=" $tc_line_add"
  335. ((ind_level--))
  336. done
  337. tc_line_add="$tc_line_add,"
  338. #slice the text, then add some stuff to the middle, then add the end back to it
  339. head -n $tc_end $bakfile > tmp
  340. echo "$tc_line_add" >> tmp
  341. ((tc_end=$tc_end+1))
  342. tail -n +$tc_end $bakfile >> tmp
  343. cp tmp $bakfile
  344. cat classheader.txt >> $bakfile # add the class
  345. cp $bakfile $nodefile
  346. rm $bakfile
  347. # __init__.py
  348. # operate on a duplicate of the file, use sed to rename.
  349. bakfile="__init__.bak.py"
  350. cp __init__.py $bakfile
  351. #find the line that marks the node category.
  352. declare -i tc_end
  353. if [[ $nodetype == 'Link' ]]; then
  354. tc_end=$(grep -n -m 1 "AllNodeCategory('LINK'" $bakfile | cut -f1 -d:)
  355. elif [[ $nodetype == 'xForm' ]]; then
  356. tc_end=$(grep -n -m 1 "AllNodeCategory('XFORM'" $bakfile | cut -f1 -d:)
  357. elif [[ $nodetype == 'Utility' && $isinput == "0" ]]; then
  358. tc_end=$(grep -n -m 1 "AllNodeCategory('UTILITIES'" $bakfile | cut -f1 -d:)
  359. elif [[ $nodetype == 'Utility' && $isinput == "1" ]]; then
  360. tc_end=$(grep -n -m 1 "AllNodeCategory('INPUT'" $bakfile | cut -f1 -d:)
  361. elif [[ $nodetype == 'Math' ]]; then
  362. tc_end=$(grep -n -m 1 "AllNodeCategory('UTILITIES'" $bakfile | cut -f1 -d:)
  363. fi
  364. # the total length of the file, in lines
  365. nodefile_len=$(cat $bakfile | wc -l)
  366. #get indentation level
  367. ((tc_end=$tc_end+1))
  368. declare -i ind_level=$(head -n $tc_end $bakfile | tail -n 1 | grep -o " " | wc -l)
  369. ((tc_end=$tc_end-1))
  370. #create the string (the class name with the proper indentation, ending in a comma).
  371. tc_line_add="NodeItem(\"$nodetype$class_name\")"
  372. until [ $ind_level == 0 ]
  373. do
  374. tc_line_add=" $tc_line_add"
  375. ((ind_level--))
  376. done
  377. tc_line_add="$tc_line_add,"
  378. #slice the text, then add some stuff to the middle, then add the end back to it
  379. head -n $tc_end $bakfile > tmp
  380. echo "$tc_line_add" >> tmp
  381. ((tc_end=$tc_end+1))
  382. tail -n +$tc_end $bakfile >> tmp
  383. cp tmp $bakfile
  384. cp $bakfile __init__.py
  385. rm $bakfile
  386. #clean up
  387. rm classheader.txt
  388. rm tmp
  389. rm cnode_def
  390. rm parameters
  391. # now we need to do the same for the container classes.
  392. whiptail --title "add_node.sh" --msgbox \
  393. "Finished adding node to addon!" 8 78
  394. fi