/doc/python_api/rst_from_bmesh_opdefines.py
Python | 383 lines | 349 code | 5 blank | 29 comment | 0 complexity | 9c2963ba4aa66fbacc773eb49d1081d0 MD5 | raw file
- # ##### BEGIN GPL LICENSE BLOCK #####
- #
- # This program is free software; you can redistribute it and/or
- # modify it under the terms of the GNU General Public License
- # as published by the Free Software Foundation; either version 2
- # of the License, or (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software Foundation,
- # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- #
- # ##### END GPL LICENSE BLOCK #####
- # <pep8 compliant>
- # This is a quite stupid script which extracts bmesh api docs from
- # 'bmesh_opdefines.c' in order to avoid having to add a lot of introspection
- # data access into the api.
- #
- # The script is stupid becase it makes assumptions about formatting...
- # that each arg has its own line, that comments above or directly after will be __doc__ etc...
- #
- # We may want to replace this script with something else one day but for now its good enough.
- # if it needs large updates it may be better to rewrite using a real parser or
- # add introspection into bmesh.ops.
- # - campbell
- import os
- CURRENT_DIR = os.path.abspath(os.path.dirname(__file__))
- SOURCE_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(CURRENT_DIR, "..", ".."))))
- FILE_OP_DEFINES_C = os.path.join(SOURCE_DIR, "source", "blender", "bmesh", "intern", "bmesh_opdefines.c")
- OUT_RST = os.path.join(CURRENT_DIR, "rst", "bmesh.ops.rst")
- HEADER = r"""
- BMesh Operators (bmesh.ops)
- ===========================
- .. module:: bmesh.ops
- This module gives access to low level bmesh operations.
- Most operators take input and return output, they can be chained together
- to perform useful operations.
- .. note::
- This API us new in 2.65 and not yet well tested.
- Operator Example
- ++++++++++++++++
- This script shows how operators can be used to model a link of a chain.
- .. literalinclude:: ../examples/bmesh.ops.1.py
- """
- def main():
- fsrc = open(FILE_OP_DEFINES_C, 'r', encoding="utf-8")
- blocks = []
- is_block = False
- is_comment = False # /* global comments only */
- comment_ctx = None
- block_ctx = None
- for l in fsrc:
- l = l[:-1]
- # weak but ok
- if ("BMOpDefine" in l and l.split()[1] == "BMOpDefine") and not "bmo_opdefines[]" in l:
- is_block = True
- block_ctx = []
- blocks.append((comment_ctx, block_ctx))
- elif l.strip().startswith("/*"):
- is_comment = True
- comment_ctx = []
- if is_block:
- if l.strip().startswith("//"):
- pass
- else:
- # remove c++ comment if we have one
- cpp_comment = l.find("//")
- if cpp_comment != -1:
- l = l[:cpp_comment]
- block_ctx.append(l)
- if l.strip() == "};":
- is_block = False
- comment_ctx = None
- if is_comment:
- c_comment_start = l.find("/*")
- if c_comment_start != -1:
- l = l[c_comment_start + 2:]
- c_comment_end = l.find("*/")
- if c_comment_end != -1:
- l = l[:c_comment_end]
- is_comment = False
- comment_ctx.append(l)
- fsrc.close()
- del fsrc
- # namespace hack
- vars = (
- "BMO_OP_SLOT_ELEMENT_BUF",
- "BMO_OP_SLOT_BOOL",
- "BMO_OP_SLOT_FLT",
- "BMO_OP_SLOT_INT",
- "BMO_OP_SLOT_MAT",
- "BMO_OP_SLOT_VEC",
- "BMO_OP_SLOT_PTR",
- "BMO_OP_SLOT_MAPPING",
- "BMO_OP_SLOT_SUBTYPE_MAP_ELEM",
- "BMO_OP_SLOT_SUBTYPE_MAP_BOOL",
- "BMO_OP_SLOT_SUBTYPE_MAP_INT",
- "BMO_OP_SLOT_SUBTYPE_MAP_FLT",
- "BMO_OP_SLOT_SUBTYPE_MAP_EMPTY",
- "BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL",
- "BMO_OP_SLOT_SUBTYPE_PTR_SCENE",
- "BMO_OP_SLOT_SUBTYPE_PTR_OBJECT",
- "BMO_OP_SLOT_SUBTYPE_PTR_MESH",
- "BMO_OP_SLOT_SUBTYPE_PTR_BMESH",
- "BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE",
- "BM_VERT",
- "BM_EDGE",
- "BM_FACE",
- "BMO_OPTYPE_FLAG_NORMALS_CALC",
- "BMO_OPTYPE_FLAG_UNTAN_MULTIRES",
- "BMO_OPTYPE_FLAG_SELECT_FLUSH",
- "BMO_OPTYPE_FLAG_NOP",
- )
- vars_dict = {}
- for i, v in enumerate(vars):
- vars_dict[v] = (1 << i)
- globals().update(vars_dict)
- # reverse lookup
- vars_dict_reverse = {v: k for k, v in vars_dict.items()}
- # end namespace hack
- blocks_py = []
- for comment, b in blocks:
- # magic, translate into python
- b[0] = b[0].replace("static BMOpDefine ", "")
- for i, l in enumerate(b):
- l = l.strip()
- l = l.replace("{", "(")
- l = l.replace("}", ")")
- if l.startswith("/*"):
- l = l.replace("/*", "'''own <")
- else:
- l = l.replace("/*", "'''inline <")
- l = l.replace("*/", ">''',")
- # exec func. eg: bmo_rotate_edges_exec,
- if l.startswith("bmo_") and l.endswith("_exec,"):
- l = "None,"
- b[i] = l
- #for l in b:
- # print(l)
- text = "\n".join(b)
- global_namespace = {
- "__file__": "generated",
- "__name__": "__main__",
- }
- global_namespace.update(vars_dict)
- text_a, text_b = text.split("=", 1)
- text = "result = " + text_b
- exec(compile(text, "generated", 'exec'), global_namespace)
- # print(global_namespace["result"])
- blocks_py.append((comment, global_namespace["result"]))
- # ---------------------
- # Now convert into rst.
- fout = open(OUT_RST, 'w', encoding="utf-8")
- fw = fout.write
- fw(HEADER)
- for comment, b in blocks_py:
- args_in = None
- args_out = None
- for member in b[1:]:
- if type(member) == tuple:
- if args_in is None:
- args_in = member
- elif args_out is None:
- args_out = member
- break
- args_in_index = []
- args_out_index = []
- if args_in is not None:
- args_in_index[:] = [i for (i, a) in enumerate(args_in) if type(a) == tuple]
- if args_out is not None:
- args_out_index[:] = [i for (i, a) in enumerate(args_out) if type(a) == tuple]
- fw(".. function:: %s(bm, %s)\n\n" % (b[0], ", ".join([args_in[i][0] for i in args_in_index])))
- # -- wash the comment
- comment_washed = []
- for i, l in enumerate(comment):
- assert((l.strip() == "") or
- (l in {"/*", " *"}) or
- (l.startswith(("/* ", " * "))))
- l = l[3:]
- if i == 0 and not l.strip():
- continue
- if l.strip():
- l = " " + l
- comment_washed.append(l)
- fw("\n".join(comment_washed))
- fw("\n")
- # -- done
- # get the args
- def get_args_wash(args, args_index, is_ret):
- args_wash = []
- for i in args_index:
- arg = args[i]
- if len(arg) == 3:
- name, tp, tp_sub = arg
- elif len(arg) == 2:
- name, tp = arg
- tp_sub = None
- else:
- print(arg)
- assert(0)
- tp_str = ""
- comment_prev = ""
- comment_next = ""
- if i != 0:
- comment_prev = args[i + 1]
- if type(comment_prev) == str and comment_prev.startswith("our <"):
- comment_prev = comment_next[5:-1] # strip inline <...>
- else:
- comment_prev = ""
- if i + 1 < len(args):
- comment_next = args[i + 1]
- if type(comment_next) == str and comment_next.startswith("inline <"):
- comment_next = comment_next[8:-1] # strip inline <...>
- else:
- comment_next = ""
- comment = ""
- if comment_prev:
- comment += comment_prev.strip()
- if comment_next:
- comment += ("\n" if comment_prev else "") + comment_next.strip()
- if tp == BMO_OP_SLOT_FLT:
- tp_str = "float"
- elif tp == BMO_OP_SLOT_INT:
- tp_str = "int"
- elif tp == BMO_OP_SLOT_BOOL:
- tp_str = "bool"
- elif tp == BMO_OP_SLOT_MAT:
- tp_str = ":class:`mathutils.Matrix`"
- elif tp == BMO_OP_SLOT_VEC:
- tp_str = ":class:`mathutils.Vector`"
- if not is_ret:
- tp_str += " or any sequence of 3 floats"
- elif tp == BMO_OP_SLOT_PTR:
- tp_str = "dict"
- assert(tp_sub is not None)
- if tp_sub == BMO_OP_SLOT_SUBTYPE_PTR_BMESH:
- tp_str = ":class:`bmesh.types.BMesh`"
- elif tp_sub == BMO_OP_SLOT_SUBTYPE_PTR_SCENE:
- tp_str = ":class:`bpy.types.Scene`"
- elif tp_sub == BMO_OP_SLOT_SUBTYPE_PTR_OBJECT:
- tp_str = ":class:`bpy.types.Object`"
- elif tp_sub == BMO_OP_SLOT_SUBTYPE_PTR_MESH:
- tp_str = ":class:`bpy.types.Mesh`"
- else:
- print("Cant find", vars_dict_reverse[tp_sub])
- assert(0)
- elif tp == BMO_OP_SLOT_ELEMENT_BUF:
- assert(tp_sub is not None)
- ls = []
- if tp_sub & BM_VERT:
- ls.append(":class:`bmesh.types.BMVert`")
- if tp_sub & BM_EDGE:
- ls.append(":class:`bmesh.types.BMEdge`")
- if tp_sub & BM_FACE:
- ls.append(":class:`bmesh.types.BMFace`")
- assert(ls) # must be at least one
- if tp_sub & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE:
- tp_str = "/".join(ls)
- else:
- tp_str = ("list of (%s)" % ", ".join(ls))
- del ls
- elif tp == BMO_OP_SLOT_MAPPING:
- if tp_sub & BMO_OP_SLOT_SUBTYPE_MAP_EMPTY:
- tp_str = "set of vert/edge/face type"
- else:
- tp_str = "dict mapping vert/edge/face types to "
- if tp_sub == BMO_OP_SLOT_SUBTYPE_MAP_BOOL:
- tp_str += "bool"
- elif tp_sub == BMO_OP_SLOT_SUBTYPE_MAP_INT:
- tp_str += "int"
- elif tp_sub == BMO_OP_SLOT_SUBTYPE_MAP_FLT:
- tp_str += "float"
- elif tp_sub == BMO_OP_SLOT_SUBTYPE_MAP_ELEM:
- tp_str += ":class:`bmesh.types.BMVert`/:class:`bmesh.types.BMEdge`/:class:`bmesh.types.BMFace`"
- elif tp_sub == BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL:
- tp_str += "unknown internal data, not compatible with python"
- else:
- print("Cant find", vars_dict_reverse[tp_sub])
- assert(0)
- else:
- print("Cant find", vars_dict_reverse[tp])
- assert(0)
- args_wash.append((name, tp_str, comment))
- return args_wash
- # end get_args_wash
- # all ops get this arg
- fw(" :arg bm: The bmesh to operate on.\n")
- fw(" :type bm: :class:`bmesh.types.BMesh`\n")
- args_in_wash = get_args_wash(args_in, args_in_index, False)
- args_out_wash = get_args_wash(args_out, args_out_index, True)
- for (name, tp, comment) in args_in_wash:
- if comment == "":
- comment = "Undocumented."
- fw(" :arg %s: %s\n" % (name, comment))
- fw(" :type %s: %s\n" % (name, tp))
- if args_out_wash:
- fw(" :return:\n\n")
- for (name, tp, comment) in args_out_wash:
- assert(name.endswith(".out"))
- name = name[:-4]
- fw(" - ``%s``: %s\n\n" % (name, comment))
- fw(" **type** %s\n" % tp)
- fw("\n")
- fw(" :rtype: dict with string keys\n")
- fw("\n\n")
- fout.close()
- del fout
- print(OUT_RST)
- if __name__ == "__main__":
- main()