PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/doc/python_api/rst_from_bmesh_opdefines.py

https://bitbucket.org/brita/blender-gl_debug
Python | 383 lines | 349 code | 5 blank | 29 comment | 0 complexity | 9c2963ba4aa66fbacc773eb49d1081d0 MD5 | raw file
  1. # ##### BEGIN GPL LICENSE BLOCK #####
  2. #
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; either version 2
  6. # of the License, or (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software Foundation,
  15. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. #
  17. # ##### END GPL LICENSE BLOCK #####
  18. # <pep8 compliant>
  19. # This is a quite stupid script which extracts bmesh api docs from
  20. # 'bmesh_opdefines.c' in order to avoid having to add a lot of introspection
  21. # data access into the api.
  22. #
  23. # The script is stupid becase it makes assumptions about formatting...
  24. # that each arg has its own line, that comments above or directly after will be __doc__ etc...
  25. #
  26. # We may want to replace this script with something else one day but for now its good enough.
  27. # if it needs large updates it may be better to rewrite using a real parser or
  28. # add introspection into bmesh.ops.
  29. # - campbell
  30. import os
  31. CURRENT_DIR = os.path.abspath(os.path.dirname(__file__))
  32. SOURCE_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(CURRENT_DIR, "..", ".."))))
  33. FILE_OP_DEFINES_C = os.path.join(SOURCE_DIR, "source", "blender", "bmesh", "intern", "bmesh_opdefines.c")
  34. OUT_RST = os.path.join(CURRENT_DIR, "rst", "bmesh.ops.rst")
  35. HEADER = r"""
  36. BMesh Operators (bmesh.ops)
  37. ===========================
  38. .. module:: bmesh.ops
  39. This module gives access to low level bmesh operations.
  40. Most operators take input and return output, they can be chained together
  41. to perform useful operations.
  42. .. note::
  43. This API us new in 2.65 and not yet well tested.
  44. Operator Example
  45. ++++++++++++++++
  46. This script shows how operators can be used to model a link of a chain.
  47. .. literalinclude:: ../examples/bmesh.ops.1.py
  48. """
  49. def main():
  50. fsrc = open(FILE_OP_DEFINES_C, 'r', encoding="utf-8")
  51. blocks = []
  52. is_block = False
  53. is_comment = False # /* global comments only */
  54. comment_ctx = None
  55. block_ctx = None
  56. for l in fsrc:
  57. l = l[:-1]
  58. # weak but ok
  59. if ("BMOpDefine" in l and l.split()[1] == "BMOpDefine") and not "bmo_opdefines[]" in l:
  60. is_block = True
  61. block_ctx = []
  62. blocks.append((comment_ctx, block_ctx))
  63. elif l.strip().startswith("/*"):
  64. is_comment = True
  65. comment_ctx = []
  66. if is_block:
  67. if l.strip().startswith("//"):
  68. pass
  69. else:
  70. # remove c++ comment if we have one
  71. cpp_comment = l.find("//")
  72. if cpp_comment != -1:
  73. l = l[:cpp_comment]
  74. block_ctx.append(l)
  75. if l.strip() == "};":
  76. is_block = False
  77. comment_ctx = None
  78. if is_comment:
  79. c_comment_start = l.find("/*")
  80. if c_comment_start != -1:
  81. l = l[c_comment_start + 2:]
  82. c_comment_end = l.find("*/")
  83. if c_comment_end != -1:
  84. l = l[:c_comment_end]
  85. is_comment = False
  86. comment_ctx.append(l)
  87. fsrc.close()
  88. del fsrc
  89. # namespace hack
  90. vars = (
  91. "BMO_OP_SLOT_ELEMENT_BUF",
  92. "BMO_OP_SLOT_BOOL",
  93. "BMO_OP_SLOT_FLT",
  94. "BMO_OP_SLOT_INT",
  95. "BMO_OP_SLOT_MAT",
  96. "BMO_OP_SLOT_VEC",
  97. "BMO_OP_SLOT_PTR",
  98. "BMO_OP_SLOT_MAPPING",
  99. "BMO_OP_SLOT_SUBTYPE_MAP_ELEM",
  100. "BMO_OP_SLOT_SUBTYPE_MAP_BOOL",
  101. "BMO_OP_SLOT_SUBTYPE_MAP_INT",
  102. "BMO_OP_SLOT_SUBTYPE_MAP_FLT",
  103. "BMO_OP_SLOT_SUBTYPE_MAP_EMPTY",
  104. "BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL",
  105. "BMO_OP_SLOT_SUBTYPE_PTR_SCENE",
  106. "BMO_OP_SLOT_SUBTYPE_PTR_OBJECT",
  107. "BMO_OP_SLOT_SUBTYPE_PTR_MESH",
  108. "BMO_OP_SLOT_SUBTYPE_PTR_BMESH",
  109. "BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE",
  110. "BM_VERT",
  111. "BM_EDGE",
  112. "BM_FACE",
  113. "BMO_OPTYPE_FLAG_NORMALS_CALC",
  114. "BMO_OPTYPE_FLAG_UNTAN_MULTIRES",
  115. "BMO_OPTYPE_FLAG_SELECT_FLUSH",
  116. "BMO_OPTYPE_FLAG_NOP",
  117. )
  118. vars_dict = {}
  119. for i, v in enumerate(vars):
  120. vars_dict[v] = (1 << i)
  121. globals().update(vars_dict)
  122. # reverse lookup
  123. vars_dict_reverse = {v: k for k, v in vars_dict.items()}
  124. # end namespace hack
  125. blocks_py = []
  126. for comment, b in blocks:
  127. # magic, translate into python
  128. b[0] = b[0].replace("static BMOpDefine ", "")
  129. for i, l in enumerate(b):
  130. l = l.strip()
  131. l = l.replace("{", "(")
  132. l = l.replace("}", ")")
  133. if l.startswith("/*"):
  134. l = l.replace("/*", "'''own <")
  135. else:
  136. l = l.replace("/*", "'''inline <")
  137. l = l.replace("*/", ">''',")
  138. # exec func. eg: bmo_rotate_edges_exec,
  139. if l.startswith("bmo_") and l.endswith("_exec,"):
  140. l = "None,"
  141. b[i] = l
  142. #for l in b:
  143. # print(l)
  144. text = "\n".join(b)
  145. global_namespace = {
  146. "__file__": "generated",
  147. "__name__": "__main__",
  148. }
  149. global_namespace.update(vars_dict)
  150. text_a, text_b = text.split("=", 1)
  151. text = "result = " + text_b
  152. exec(compile(text, "generated", 'exec'), global_namespace)
  153. # print(global_namespace["result"])
  154. blocks_py.append((comment, global_namespace["result"]))
  155. # ---------------------
  156. # Now convert into rst.
  157. fout = open(OUT_RST, 'w', encoding="utf-8")
  158. fw = fout.write
  159. fw(HEADER)
  160. for comment, b in blocks_py:
  161. args_in = None
  162. args_out = None
  163. for member in b[1:]:
  164. if type(member) == tuple:
  165. if args_in is None:
  166. args_in = member
  167. elif args_out is None:
  168. args_out = member
  169. break
  170. args_in_index = []
  171. args_out_index = []
  172. if args_in is not None:
  173. args_in_index[:] = [i for (i, a) in enumerate(args_in) if type(a) == tuple]
  174. if args_out is not None:
  175. args_out_index[:] = [i for (i, a) in enumerate(args_out) if type(a) == tuple]
  176. fw(".. function:: %s(bm, %s)\n\n" % (b[0], ", ".join([args_in[i][0] for i in args_in_index])))
  177. # -- wash the comment
  178. comment_washed = []
  179. for i, l in enumerate(comment):
  180. assert((l.strip() == "") or
  181. (l in {"/*", " *"}) or
  182. (l.startswith(("/* ", " * "))))
  183. l = l[3:]
  184. if i == 0 and not l.strip():
  185. continue
  186. if l.strip():
  187. l = " " + l
  188. comment_washed.append(l)
  189. fw("\n".join(comment_washed))
  190. fw("\n")
  191. # -- done
  192. # get the args
  193. def get_args_wash(args, args_index, is_ret):
  194. args_wash = []
  195. for i in args_index:
  196. arg = args[i]
  197. if len(arg) == 3:
  198. name, tp, tp_sub = arg
  199. elif len(arg) == 2:
  200. name, tp = arg
  201. tp_sub = None
  202. else:
  203. print(arg)
  204. assert(0)
  205. tp_str = ""
  206. comment_prev = ""
  207. comment_next = ""
  208. if i != 0:
  209. comment_prev = args[i + 1]
  210. if type(comment_prev) == str and comment_prev.startswith("our <"):
  211. comment_prev = comment_next[5:-1] # strip inline <...>
  212. else:
  213. comment_prev = ""
  214. if i + 1 < len(args):
  215. comment_next = args[i + 1]
  216. if type(comment_next) == str and comment_next.startswith("inline <"):
  217. comment_next = comment_next[8:-1] # strip inline <...>
  218. else:
  219. comment_next = ""
  220. comment = ""
  221. if comment_prev:
  222. comment += comment_prev.strip()
  223. if comment_next:
  224. comment += ("\n" if comment_prev else "") + comment_next.strip()
  225. if tp == BMO_OP_SLOT_FLT:
  226. tp_str = "float"
  227. elif tp == BMO_OP_SLOT_INT:
  228. tp_str = "int"
  229. elif tp == BMO_OP_SLOT_BOOL:
  230. tp_str = "bool"
  231. elif tp == BMO_OP_SLOT_MAT:
  232. tp_str = ":class:`mathutils.Matrix`"
  233. elif tp == BMO_OP_SLOT_VEC:
  234. tp_str = ":class:`mathutils.Vector`"
  235. if not is_ret:
  236. tp_str += " or any sequence of 3 floats"
  237. elif tp == BMO_OP_SLOT_PTR:
  238. tp_str = "dict"
  239. assert(tp_sub is not None)
  240. if tp_sub == BMO_OP_SLOT_SUBTYPE_PTR_BMESH:
  241. tp_str = ":class:`bmesh.types.BMesh`"
  242. elif tp_sub == BMO_OP_SLOT_SUBTYPE_PTR_SCENE:
  243. tp_str = ":class:`bpy.types.Scene`"
  244. elif tp_sub == BMO_OP_SLOT_SUBTYPE_PTR_OBJECT:
  245. tp_str = ":class:`bpy.types.Object`"
  246. elif tp_sub == BMO_OP_SLOT_SUBTYPE_PTR_MESH:
  247. tp_str = ":class:`bpy.types.Mesh`"
  248. else:
  249. print("Cant find", vars_dict_reverse[tp_sub])
  250. assert(0)
  251. elif tp == BMO_OP_SLOT_ELEMENT_BUF:
  252. assert(tp_sub is not None)
  253. ls = []
  254. if tp_sub & BM_VERT:
  255. ls.append(":class:`bmesh.types.BMVert`")
  256. if tp_sub & BM_EDGE:
  257. ls.append(":class:`bmesh.types.BMEdge`")
  258. if tp_sub & BM_FACE:
  259. ls.append(":class:`bmesh.types.BMFace`")
  260. assert(ls) # must be at least one
  261. if tp_sub & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE:
  262. tp_str = "/".join(ls)
  263. else:
  264. tp_str = ("list of (%s)" % ", ".join(ls))
  265. del ls
  266. elif tp == BMO_OP_SLOT_MAPPING:
  267. if tp_sub & BMO_OP_SLOT_SUBTYPE_MAP_EMPTY:
  268. tp_str = "set of vert/edge/face type"
  269. else:
  270. tp_str = "dict mapping vert/edge/face types to "
  271. if tp_sub == BMO_OP_SLOT_SUBTYPE_MAP_BOOL:
  272. tp_str += "bool"
  273. elif tp_sub == BMO_OP_SLOT_SUBTYPE_MAP_INT:
  274. tp_str += "int"
  275. elif tp_sub == BMO_OP_SLOT_SUBTYPE_MAP_FLT:
  276. tp_str += "float"
  277. elif tp_sub == BMO_OP_SLOT_SUBTYPE_MAP_ELEM:
  278. tp_str += ":class:`bmesh.types.BMVert`/:class:`bmesh.types.BMEdge`/:class:`bmesh.types.BMFace`"
  279. elif tp_sub == BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL:
  280. tp_str += "unknown internal data, not compatible with python"
  281. else:
  282. print("Cant find", vars_dict_reverse[tp_sub])
  283. assert(0)
  284. else:
  285. print("Cant find", vars_dict_reverse[tp])
  286. assert(0)
  287. args_wash.append((name, tp_str, comment))
  288. return args_wash
  289. # end get_args_wash
  290. # all ops get this arg
  291. fw(" :arg bm: The bmesh to operate on.\n")
  292. fw(" :type bm: :class:`bmesh.types.BMesh`\n")
  293. args_in_wash = get_args_wash(args_in, args_in_index, False)
  294. args_out_wash = get_args_wash(args_out, args_out_index, True)
  295. for (name, tp, comment) in args_in_wash:
  296. if comment == "":
  297. comment = "Undocumented."
  298. fw(" :arg %s: %s\n" % (name, comment))
  299. fw(" :type %s: %s\n" % (name, tp))
  300. if args_out_wash:
  301. fw(" :return:\n\n")
  302. for (name, tp, comment) in args_out_wash:
  303. assert(name.endswith(".out"))
  304. name = name[:-4]
  305. fw(" - ``%s``: %s\n\n" % (name, comment))
  306. fw(" **type** %s\n" % tp)
  307. fw("\n")
  308. fw(" :rtype: dict with string keys\n")
  309. fw("\n\n")
  310. fout.close()
  311. del fout
  312. print(OUT_RST)
  313. if __name__ == "__main__":
  314. main()