add_node.sh 16 KB

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