/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2012 Blender Foundation. All rights reserved. */ /** \file * \ingroup pybmesh */ #include "BLI_math.h" #include "BLI_sort.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_object_types.h" #include "BKE_customdata.h" #include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" #include "bmesh.h" #include #include "../mathutils/mathutils.h" #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" #include "bmesh_py_types.h" /* own include */ #include "bmesh_py_types_customdata.h" #include "bmesh_py_types_meshdata.h" #include "bmesh_py_types_select.h" static void bm_dealloc_editmode_warn(BPy_BMesh *self); /* Common Flags * ************ */ /* scene does not use BM_* flags. */ PyC_FlagSet bpy_bm_scene_vert_edge_face_flags[] = { {1, "VERT"}, {2, "EDGE"}, {4, "FACE"}, {0, NULL}, }; PyC_FlagSet bpy_bm_htype_vert_edge_face_flags[] = { {BM_VERT, "VERT"}, {BM_EDGE, "EDGE"}, {BM_FACE, "FACE"}, {0, NULL}, }; PyC_FlagSet bpy_bm_htype_all_flags[] = { {BM_VERT, "VERT"}, {BM_LOOP, "EDGE"}, {BM_FACE, "FACE"}, {BM_LOOP, "LOOP"}, {0, NULL}, }; #define BPY_BM_HFLAG_ALL_STR "('SELECT', 'HIDE', 'SEAM', 'SMOOTH', 'TAG')" PyC_FlagSet bpy_bm_hflag_all_flags[] = { {BM_ELEM_SELECT, "SELECT"}, {BM_ELEM_HIDDEN, "HIDE"}, {BM_ELEM_SEAM, "SEAM"}, {BM_ELEM_SMOOTH, "SMOOTH"}, {BM_ELEM_TAG, "TAG"}, {0, NULL}, }; /* py-type definitions * ******************* */ /* getseters * ========= */ /* bmesh elems * ----------- */ PyDoc_STRVAR(bpy_bm_elem_select_doc, "Selected state of this element.\n\n:type: boolean"); PyDoc_STRVAR(bpy_bm_elem_hide_doc, "Hidden state of this element.\n\n:type: boolean"); PyDoc_STRVAR(bpy_bm_elem_tag_doc, "Generic attribute scripts can use for own logic\n\n:type: boolean"); PyDoc_STRVAR(bpy_bm_elem_smooth_doc, "Smooth state of this element.\n\n:type: boolean"); PyDoc_STRVAR(bpy_bm_elem_seam_doc, "Seam for UV unwrapping.\n\n:type: boolean"); static PyObject *bpy_bm_elem_hflag_get(BPy_BMElem *self, void *flag) { const char hflag = (char)POINTER_AS_INT(flag); BPY_BM_CHECK_OBJ(self); return PyBool_FromLong(BM_elem_flag_test(self->ele, hflag)); } static int bpy_bm_elem_hflag_set(BPy_BMElem *self, PyObject *value, void *flag) { const char hflag = (char)POINTER_AS_INT(flag); int param; BPY_BM_CHECK_INT(self); if ((param = PyC_Long_AsBool(value)) == -1) { return -1; } if (hflag == BM_ELEM_SELECT) { BM_elem_select_set(self->bm, self->ele, param); } else { BM_elem_flag_set(self->ele, hflag, param); } return 0; } PyDoc_STRVAR( bpy_bm_elem_index_doc, "Index of this element.\n" "\n" ":type: int\n" "\n" ".. note::\n" "\n" " This value is not necessarily valid, while editing the mesh it can become *dirty*.\n" "\n" " It's also possible to assign any number to this attribute for a scripts internal logic.\n" "\n" " To ensure the value is up to date - see :class:`BMElemSeq.index_update`.\n"); static PyObject *bpy_bm_elem_index_get(BPy_BMElem *self, void *UNUSED(flag)) { BPY_BM_CHECK_OBJ(self); return PyLong_FromLong(BM_elem_index_get(self->ele)); } static int bpy_bm_elem_index_set(BPy_BMElem *self, PyObject *value, void *UNUSED(flag)) { int param; BPY_BM_CHECK_INT(self); if (((param = PyC_Long_AsI32(value)) == -1) && PyErr_Occurred()) { /* error is set */ return -1; } BM_elem_index_set(self->ele, param); /* set_dirty! */ /* when setting the index assume its set invalid */ self->bm->elem_index_dirty |= self->ele->head.htype; return 0; } /* type specific get/sets * ---------------------- */ /* Mesh * ^^^^ */ /* doc-strings for all uses of this function */ PyDoc_STRVAR(bpy_bmvertseq_doc, "This meshes vert sequence (read-only).\n\n:type: :class:`BMVertSeq`"); static PyObject *bpy_bmvertseq_get(BPy_BMesh *self, void *UNUSED(closure)) { BPY_BM_CHECK_OBJ(self); return BPy_BMVertSeq_CreatePyObject(self->bm); } PyDoc_STRVAR(bpy_bmedgeseq_doc, "This meshes edge sequence (read-only).\n\n:type: :class:`BMEdgeSeq`"); static PyObject *bpy_bmedgeseq_get(BPy_BMesh *self, void *UNUSED(closure)) { BPY_BM_CHECK_OBJ(self); return BPy_BMEdgeSeq_CreatePyObject(self->bm); } PyDoc_STRVAR(bpy_bmfaceseq_doc, "This meshes face sequence (read-only).\n\n:type: :class:`BMFaceSeq`"); static PyObject *bpy_bmfaceseq_get(BPy_BMesh *self, void *UNUSED(closure)) { BPY_BM_CHECK_OBJ(self); return BPy_BMFaceSeq_CreatePyObject(self->bm); } PyDoc_STRVAR(bpy_bmloopseq_doc, "This meshes loops (read-only).\n\n:type: :class:`BMLoopSeq`\n" "\n" ".. note::\n" "\n" " Loops must be accessed via faces, this is only exposed for layer access.\n"); static PyObject *bpy_bmloopseq_get(BPy_BMesh *self, void *UNUSED(closure)) { BPY_BM_CHECK_OBJ(self); return BPy_BMLoopSeq_CreatePyObject(self->bm); } /* vert */ PyDoc_STRVAR( bpy_bmvert_link_edges_doc, "Edges connected to this vertex (read-only).\n\n:type: :class:`BMElemSeq` of :class:`BMEdge`"); PyDoc_STRVAR( bpy_bmvert_link_faces_doc, "Faces connected to this vertex (read-only).\n\n:type: :class:`BMElemSeq` of :class:`BMFace`"); PyDoc_STRVAR( bpy_bmvert_link_loops_doc, "Loops that use this vertex (read-only).\n\n:type: :class:`BMElemSeq` of :class:`BMLoop`"); /* edge */ PyDoc_STRVAR(bpy_bmedge_verts_doc, "Verts this edge uses (always 2), (read-only).\n\n:type: :class:`BMElemSeq` of " ":class:`BMVert`"); PyDoc_STRVAR( bpy_bmedge_link_faces_doc, "Faces connected to this edge, (read-only).\n\n:type: :class:`BMElemSeq` of :class:`BMFace`"); PyDoc_STRVAR( bpy_bmedge_link_loops_doc, "Loops connected to this edge, (read-only).\n\n:type: :class:`BMElemSeq` of :class:`BMLoop`"); /* face */ PyDoc_STRVAR(bpy_bmface_verts_doc, "Verts of this face, (read-only).\n\n:type: :class:`BMElemSeq` of :class:`BMVert`"); PyDoc_STRVAR(bpy_bmface_edges_doc, "Edges of this face, (read-only).\n\n:type: :class:`BMElemSeq` of :class:`BMEdge`"); PyDoc_STRVAR(bpy_bmface_loops_doc, "Loops of this face, (read-only).\n\n:type: :class:`BMElemSeq` of :class:`BMLoop`"); /* loop */ PyDoc_STRVAR( bpy_bmloops_link_loops_doc, "Loops connected to this loop, (read-only).\n\n:type: :class:`BMElemSeq` of :class:`BMLoop`"); static PyObject *bpy_bmelemseq_elem_get(BPy_BMElem *self, void *itype) { BPY_BM_CHECK_OBJ(self); return BPy_BMElemSeq_CreatePyObject(self->bm, self, POINTER_AS_INT(itype)); } PyDoc_STRVAR(bpy_bm_is_valid_doc, "True when this element is valid (hasn't been removed).\n\n:type: boolean"); static PyObject *bpy_bm_is_valid_get(BPy_BMGeneric *self) { return PyBool_FromLong(BPY_BM_IS_VALID(self)); } PyDoc_STRVAR( bpy_bmesh_is_wrapped_doc, "True when this mesh is owned by blender (typically the editmode BMesh).\n\n:type: boolean"); static PyObject *bpy_bmesh_is_wrapped_get(BPy_BMesh *self) { BPY_BM_CHECK_OBJ(self); return PyBool_FromLong(self->flag & BPY_BMFLAG_IS_WRAPPED); } PyDoc_STRVAR(bpy_bmesh_select_mode_doc, "The selection mode, values can be {'VERT', 'EDGE', 'FACE'}, can't be assigned an " "empty set.\n\n:type: set"); static PyObject *bpy_bmesh_select_mode_get(BPy_BMesh *self) { BPY_BM_CHECK_OBJ(self); return PyC_FlagSet_FromBitfield(bpy_bm_scene_vert_edge_face_flags, self->bm->selectmode); } static int bpy_bmesh_select_mode_set(BPy_BMesh *self, PyObject *value) { int flag = 0; BPY_BM_CHECK_INT(self); if (PyC_FlagSet_ToBitfield(bpy_bm_scene_vert_edge_face_flags, value, &flag, "bm.select_mode") == -1) { return -1; } if (flag == 0) { PyErr_SetString(PyExc_TypeError, "bm.select_mode: can't assign an empty value"); return -1; } self->bm->selectmode = flag; return 0; } PyDoc_STRVAR(bpy_bmesh_select_history_doc, "Sequence of selected items (the last is displayed as active).\n\n:type: " ":class:`BMEditSelSeq`"); static PyObject *bpy_bmesh_select_history_get(BPy_BMesh *self) { BPY_BM_CHECK_OBJ(self); return BPy_BMEditSel_CreatePyObject(self->bm); } static int bpy_bmesh_select_history_set(BPy_BMesh *self, PyObject *value) { BPY_BM_CHECK_INT(self); return BPy_BMEditSel_Assign(self, value); } /* Vert * ^^^^ */ PyDoc_STRVAR(bpy_bmvert_co_doc, "The coordinates for this vertex as a 3D, wrapped vector.\n\n:type: " ":class:`mathutils.Vector`"); static PyObject *bpy_bmvert_co_get(BPy_BMVert *self) { BPY_BM_CHECK_OBJ(self); return Vector_CreatePyObject_wrap(self->v->co, 3, NULL); } static int bpy_bmvert_co_set(BPy_BMVert *self, PyObject *value) { BPY_BM_CHECK_INT(self); if (mathutils_array_parse(self->v->co, 3, 3, value, "BMVert.co") != -1) { return 0; } return -1; } PyDoc_STRVAR( bpy_bmvert_normal_doc, "The normal for this vertex as a 3D, wrapped vector.\n\n:type: :class:`mathutils.Vector`"); static PyObject *bpy_bmvert_normal_get(BPy_BMVert *self) { BPY_BM_CHECK_OBJ(self); return Vector_CreatePyObject_wrap(self->v->no, 3, NULL); } static int bpy_bmvert_normal_set(BPy_BMVert *self, PyObject *value) { BPY_BM_CHECK_INT(self); if (mathutils_array_parse(self->v->no, 3, 3, value, "BMVert.normal") != -1) { return 0; } return -1; } PyDoc_STRVAR(bpy_bmvert_is_manifold_doc, "True when this vertex is manifold (read-only).\n\n:type: boolean"); static PyObject *bpy_bmvert_is_manifold_get(BPy_BMVert *self) { BPY_BM_CHECK_OBJ(self); return PyBool_FromLong(BM_vert_is_manifold(self->v)); } PyDoc_STRVAR(bpy_bmvert_is_wire_doc, "True when this vertex is not connected to any faces (read-only).\n\n:type: boolean"); static PyObject *bpy_bmvert_is_wire_get(BPy_BMVert *self) { BPY_BM_CHECK_OBJ(self); return PyBool_FromLong(BM_vert_is_wire(self->v)); } PyDoc_STRVAR( bpy_bmvert_is_boundary_doc, "True when this vertex is connected to boundary edges (read-only).\n\n:type: boolean"); static PyObject *bpy_bmvert_is_boundary_get(BPy_BMVert *self) { BPY_BM_CHECK_OBJ(self); return PyBool_FromLong(BM_vert_is_boundary(self->v)); } /* Edge * ^^^^ */ PyDoc_STRVAR(bpy_bmedge_is_manifold_doc, "True when this edge is manifold (read-only).\n\n:type: boolean"); static PyObject *bpy_bmedge_is_manifold_get(BPy_BMEdge *self) { BPY_BM_CHECK_OBJ(self); return PyBool_FromLong(BM_edge_is_manifold(self->e)); } PyDoc_STRVAR(bpy_bmedge_is_contiguous_doc, "True when this edge is manifold, between two faces with the same winding " "(read-only).\n\n:type: boolean"); static PyObject *bpy_bmedge_is_contiguous_get(BPy_BMEdge *self) { BPY_BM_CHECK_OBJ(self); return PyBool_FromLong(BM_edge_is_contiguous(self->e)); } PyDoc_STRVAR(bpy_bmedge_is_convex_doc, "True when this edge joins two convex faces, depends on a valid face normal " "(read-only).\n\n:type: boolean"); static PyObject *bpy_bmedge_is_convex_get(BPy_BMEdge *self) { BPY_BM_CHECK_OBJ(self); return PyBool_FromLong(BM_edge_is_convex(self->e)); } PyDoc_STRVAR(bpy_bmedge_is_wire_doc, "True when this edge is not connected to any faces (read-only).\n\n:type: boolean"); static PyObject *bpy_bmedge_is_wire_get(BPy_BMEdge *self) { BPY_BM_CHECK_OBJ(self); return PyBool_FromLong(BM_edge_is_wire(self->e)); } PyDoc_STRVAR(bpy_bmedge_is_boundary_doc, "True when this edge is at the boundary of a face (read-only).\n\n:type: boolean"); static PyObject *bpy_bmedge_is_boundary_get(BPy_BMEdge *self) { BPY_BM_CHECK_OBJ(self); return PyBool_FromLong(BM_edge_is_boundary(self->e)); } /* Face * ^^^^ */ PyDoc_STRVAR( bpy_bmface_normal_doc, "The normal for this face as a 3D, wrapped vector.\n\n:type: :class:`mathutils.Vector`"); static PyObject *bpy_bmface_normal_get(BPy_BMFace *self) { BPY_BM_CHECK_OBJ(self); return Vector_CreatePyObject_wrap(self->f->no, 3, NULL); } static int bpy_bmface_normal_set(BPy_BMFace *self, PyObject *value) { BPY_BM_CHECK_INT(self); if (mathutils_array_parse(self->f->no, 3, 3, value, "BMFace.normal") != -1) { return 0; } return -1; } PyDoc_STRVAR(bpy_bmface_material_index_doc, "The face's material index.\n\n:type: int"); static PyObject *bpy_bmface_material_index_get(BPy_BMFace *self) { BPY_BM_CHECK_OBJ(self); return PyLong_FromLong(self->f->mat_nr); } static int bpy_bmface_material_index_set(BPy_BMFace *self, PyObject *value) { int param; BPY_BM_CHECK_INT(self); if (((param = PyC_Long_AsI32(value)) == -1) && PyErr_Occurred()) { /* error is set */ return -1; } if ((param < 0) || (param > MAXMAT)) { /* normally we clamp but in this case raise an error */ PyErr_SetString(PyExc_ValueError, "material index outside of usable range (0 - 32766)"); return -1; } self->f->mat_nr = (short)param; return 0; } /* Loop * ^^^^ */ PyDoc_STRVAR(bpy_bmloop_vert_doc, "The loop's vertex (read-only).\n\n:type: :class:`BMVert`"); static PyObject *bpy_bmloop_vert_get(BPy_BMLoop *self) { BPY_BM_CHECK_OBJ(self); return BPy_BMVert_CreatePyObject(self->bm, self->l->v); } PyDoc_STRVAR( bpy_bmloop_edge_doc, "The loop's edge (between this loop and the next), (read-only).\n\n:type: :class:`BMEdge`"); static PyObject *bpy_bmloop_edge_get(BPy_BMLoop *self) { BPY_BM_CHECK_OBJ(self); return BPy_BMEdge_CreatePyObject(self->bm, self->l->e); } PyDoc_STRVAR(bpy_bmloop_face_doc, "The face this loop makes (read-only).\n\n:type: :class:`BMFace`"); static PyObject *bpy_bmloop_face_get(BPy_BMLoop *self) { BPY_BM_CHECK_OBJ(self); return BPy_BMFace_CreatePyObject(self->bm, self->l->f); } PyDoc_STRVAR(bpy_bmloop_link_loop_next_doc, "The next face corner (read-only).\n\n:type: :class:`BMLoop`"); static PyObject *bpy_bmloop_link_loop_next_get(BPy_BMLoop *self) { BPY_BM_CHECK_OBJ(self); return BPy_BMLoop_CreatePyObject(self->bm, self->l->next); } PyDoc_STRVAR(bpy_bmloop_link_loop_prev_doc, "The previous face corner (read-only).\n\n:type: :class:`BMLoop`"); static PyObject *bpy_bmloop_link_loop_prev_get(BPy_BMLoop *self) { BPY_BM_CHECK_OBJ(self); return BPy_BMLoop_CreatePyObject(self->bm, self->l->prev); } PyDoc_STRVAR(bpy_bmloop_link_loop_radial_next_doc, "The next loop around the edge (read-only).\n\n:type: :class:`BMLoop`"); static PyObject *bpy_bmloop_link_loop_radial_next_get(BPy_BMLoop *self) { BPY_BM_CHECK_OBJ(self); return BPy_BMLoop_CreatePyObject(self->bm, self->l->radial_next); } PyDoc_STRVAR(bpy_bmloop_link_loop_radial_prev_doc, "The previous loop around the edge (read-only).\n\n:type: :class:`BMLoop`"); static PyObject *bpy_bmloop_link_loop_radial_prev_get(BPy_BMLoop *self) { BPY_BM_CHECK_OBJ(self); return BPy_BMLoop_CreatePyObject(self->bm, self->l->radial_prev); } PyDoc_STRVAR(bpy_bmloop_is_convex_doc, "True when this loop is at the convex corner of a face, depends on a valid face " "normal (read-only).\n\n:type: boolean"); static PyObject *bpy_bmloop_is_convex_get(BPy_BMLoop *self) { BPY_BM_CHECK_OBJ(self); return PyBool_FromLong(BM_loop_is_convex(self->l)); } /* ElemSeq * ^^^^^^^ */ /* NOTE: use for bmvert/edge/face/loop seq's use these, not bmelemseq directly. */ PyDoc_STRVAR(bpy_bmelemseq_layers_vert_doc, "custom-data layers (read-only).\n\n:type: :class:`BMLayerAccessVert`"); PyDoc_STRVAR(bpy_bmelemseq_layers_edge_doc, "custom-data layers (read-only).\n\n:type: :class:`BMLayerAccessEdge`"); PyDoc_STRVAR(bpy_bmelemseq_layers_face_doc, "custom-data layers (read-only).\n\n:type: :class:`BMLayerAccessFace`"); PyDoc_STRVAR(bpy_bmelemseq_layers_loop_doc, "custom-data layers (read-only).\n\n:type: :class:`BMLayerAccessLoop`"); static PyObject *bpy_bmelemseq_layers_get(BPy_BMElemSeq *self, void *htype) { BPY_BM_CHECK_OBJ(self); return BPy_BMLayerAccess_CreatePyObject(self->bm, POINTER_AS_INT(htype)); } /* FaceSeq * ^^^^^^^ */ PyDoc_STRVAR(bpy_bmfaceseq_active_doc, "active face.\n\n:type: :class:`BMFace` or None"); static PyObject *bpy_bmfaceseq_active_get(BPy_BMElemSeq *self, void *UNUSED(closure)) { BMesh *bm = self->bm; BPY_BM_CHECK_OBJ(self); if (bm->act_face) { return BPy_BMElem_CreatePyObject(bm, (BMHeader *)bm->act_face); } Py_RETURN_NONE; } static int bpy_bmfaceseq_active_set(BPy_BMElem *self, PyObject *value, void *UNUSED(closure)) { BMesh *bm = self->bm; if (value == Py_None) { bm->act_face = NULL; return 0; } if (BPy_BMFace_Check(value)) { BPY_BM_CHECK_SOURCE_INT(bm, "faces.active = f", value); bm->act_face = ((BPy_BMFace *)value)->f; return 0; } PyErr_Format(PyExc_TypeError, "faces.active = f: expected BMFace or None, not %.200s", Py_TYPE(value)->tp_name); return -1; } static PyGetSetDef bpy_bmesh_getseters[] = { {"verts", (getter)bpy_bmvertseq_get, (setter)NULL, bpy_bmvertseq_doc, NULL}, {"edges", (getter)bpy_bmedgeseq_get, (setter)NULL, bpy_bmedgeseq_doc, NULL}, {"faces", (getter)bpy_bmfaceseq_get, (setter)NULL, bpy_bmfaceseq_doc, NULL}, {"loops", (getter)bpy_bmloopseq_get, (setter)NULL, bpy_bmloopseq_doc, NULL}, {"select_mode", (getter)bpy_bmesh_select_mode_get, (setter)bpy_bmesh_select_mode_set, bpy_bmesh_select_mode_doc, NULL}, {"select_history", (getter)bpy_bmesh_select_history_get, (setter)bpy_bmesh_select_history_set, bpy_bmesh_select_history_doc, NULL}, /* readonly checks */ {"is_wrapped", (getter)bpy_bmesh_is_wrapped_get, (setter)NULL, bpy_bmesh_is_wrapped_doc, NULL}, /* as with mathutils */ {"is_valid", (getter)bpy_bm_is_valid_get, (setter)NULL, bpy_bm_is_valid_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; static PyGetSetDef bpy_bmvert_getseters[] = { /* generic */ {"select", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_select_doc, (void *)BM_ELEM_SELECT}, {"hide", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_hide_doc, (void *)BM_ELEM_HIDDEN}, {"tag", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_tag_doc, (void *)BM_ELEM_TAG}, {"index", (getter)bpy_bm_elem_index_get, (setter)bpy_bm_elem_index_set, bpy_bm_elem_index_doc, NULL}, {"co", (getter)bpy_bmvert_co_get, (setter)bpy_bmvert_co_set, bpy_bmvert_co_doc, NULL}, {"normal", (getter)bpy_bmvert_normal_get, (setter)bpy_bmvert_normal_set, bpy_bmvert_normal_doc, NULL}, /* connectivity data */ {"link_edges", (getter)bpy_bmelemseq_elem_get, (setter)NULL, bpy_bmvert_link_edges_doc, (void *)BM_EDGES_OF_VERT}, {"link_faces", (getter)bpy_bmelemseq_elem_get, (setter)NULL, bpy_bmvert_link_faces_doc, (void *)BM_FACES_OF_VERT}, {"link_loops", (getter)bpy_bmelemseq_elem_get, (setter)NULL, bpy_bmvert_link_loops_doc, (void *)BM_LOOPS_OF_VERT}, /* readonly checks */ {"is_manifold", (getter)bpy_bmvert_is_manifold_get, (setter)NULL, bpy_bmvert_is_manifold_doc, NULL}, {"is_wire", (getter)bpy_bmvert_is_wire_get, (setter)NULL, bpy_bmvert_is_wire_doc, NULL}, {"is_boundary", (getter)bpy_bmvert_is_boundary_get, (setter)NULL, bpy_bmvert_is_boundary_doc, NULL}, {"is_valid", (getter)bpy_bm_is_valid_get, (setter)NULL, bpy_bm_is_valid_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; static PyGetSetDef bpy_bmedge_getseters[] = { /* generic */ {"select", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_select_doc, (void *)BM_ELEM_SELECT}, {"hide", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_hide_doc, (void *)BM_ELEM_HIDDEN}, {"tag", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_tag_doc, (void *)BM_ELEM_TAG}, {"index", (getter)bpy_bm_elem_index_get, (setter)bpy_bm_elem_index_set, bpy_bm_elem_index_doc, NULL}, {"smooth", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_smooth_doc, (void *)BM_ELEM_SMOOTH}, {"seam", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_seam_doc, (void *)BM_ELEM_SEAM}, /* connectivity data */ {"verts", (getter)bpy_bmelemseq_elem_get, (setter)NULL, bpy_bmedge_verts_doc, (void *)BM_VERTS_OF_EDGE}, {"link_faces", (getter)bpy_bmelemseq_elem_get, (setter)NULL, bpy_bmedge_link_faces_doc, (void *)BM_FACES_OF_EDGE}, {"link_loops", (getter)bpy_bmelemseq_elem_get, (setter)NULL, bpy_bmedge_link_loops_doc, (void *)BM_LOOPS_OF_EDGE}, /* readonly checks */ {"is_manifold", (getter)bpy_bmedge_is_manifold_get, (setter)NULL, bpy_bmedge_is_manifold_doc, NULL}, {"is_contiguous", (getter)bpy_bmedge_is_contiguous_get, (setter)NULL, bpy_bmedge_is_contiguous_doc, NULL}, {"is_convex", (getter)bpy_bmedge_is_convex_get, (setter)NULL, bpy_bmedge_is_convex_doc, NULL}, {"is_wire", (getter)bpy_bmedge_is_wire_get, (setter)NULL, bpy_bmedge_is_wire_doc, NULL}, {"is_boundary", (getter)bpy_bmedge_is_boundary_get, (setter)NULL, bpy_bmedge_is_boundary_doc, NULL}, {"is_valid", (getter)bpy_bm_is_valid_get, (setter)NULL, bpy_bm_is_valid_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; static PyGetSetDef bpy_bmface_getseters[] = { /* generic */ {"select", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_select_doc, (void *)BM_ELEM_SELECT}, {"hide", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_hide_doc, (void *)BM_ELEM_HIDDEN}, {"tag", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_tag_doc, (void *)BM_ELEM_TAG}, {"index", (getter)bpy_bm_elem_index_get, (setter)bpy_bm_elem_index_set, bpy_bm_elem_index_doc, NULL}, {"smooth", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_smooth_doc, (void *)BM_ELEM_SMOOTH}, {"normal", (getter)bpy_bmface_normal_get, (setter)bpy_bmface_normal_set, bpy_bmface_normal_doc, NULL}, {"material_index", (getter)bpy_bmface_material_index_get, (setter)bpy_bmface_material_index_set, bpy_bmface_material_index_doc, NULL}, /* connectivity data */ {"verts", (getter)bpy_bmelemseq_elem_get, (setter)NULL, bpy_bmface_verts_doc, (void *)BM_VERTS_OF_FACE}, {"edges", (getter)bpy_bmelemseq_elem_get, (setter)NULL, bpy_bmface_edges_doc, (void *)BM_EDGES_OF_FACE}, {"loops", (getter)bpy_bmelemseq_elem_get, (setter)NULL, bpy_bmface_loops_doc, (void *)BM_LOOPS_OF_FACE}, /* readonly checks */ {"is_valid", (getter)bpy_bm_is_valid_get, (setter)NULL, bpy_bm_is_valid_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; static PyGetSetDef bpy_bmloop_getseters[] = { /* generic */ /* flags are available but not used for loops. */ #if 0 {"select", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_select_doc, (void *)BM_ELEM_SELECT}, {"hide", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_hide_doc, (void *)BM_ELEM_HIDDEN}, #endif {"tag", (getter)bpy_bm_elem_hflag_get, (setter)bpy_bm_elem_hflag_set, bpy_bm_elem_tag_doc, (void *)BM_ELEM_TAG}, {"index", (getter)bpy_bm_elem_index_get, (setter)bpy_bm_elem_index_set, bpy_bm_elem_index_doc, NULL}, {"vert", (getter)bpy_bmloop_vert_get, (setter)NULL, bpy_bmloop_vert_doc, NULL}, {"edge", (getter)bpy_bmloop_edge_get, (setter)NULL, bpy_bmloop_edge_doc, NULL}, {"face", (getter)bpy_bmloop_face_get, (setter)NULL, bpy_bmloop_face_doc, NULL}, /* connectivity data */ {"link_loops", (getter)bpy_bmelemseq_elem_get, (setter)NULL, bpy_bmloops_link_loops_doc, (void *)BM_LOOPS_OF_LOOP}, {"link_loop_next", (getter)bpy_bmloop_link_loop_next_get, (setter)NULL, bpy_bmloop_link_loop_next_doc, NULL}, {"link_loop_prev", (getter)bpy_bmloop_link_loop_prev_get, (setter)NULL, bpy_bmloop_link_loop_prev_doc, NULL}, {"link_loop_radial_next", (getter)bpy_bmloop_link_loop_radial_next_get, (setter)NULL, bpy_bmloop_link_loop_radial_next_doc, NULL}, {"link_loop_radial_prev", (getter)bpy_bmloop_link_loop_radial_prev_get, (setter)NULL, bpy_bmloop_link_loop_radial_prev_doc, NULL}, /* readonly checks */ {"is_convex", (getter)bpy_bmloop_is_convex_get, (setter)NULL, bpy_bmloop_is_convex_doc, NULL}, {"is_valid", (getter)bpy_bm_is_valid_get, (setter)NULL, bpy_bm_is_valid_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; static PyGetSetDef bpy_bmvertseq_getseters[] = { {"layers", (getter)bpy_bmelemseq_layers_get, (setter)NULL, bpy_bmelemseq_layers_vert_doc, (void *)BM_VERT}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; static PyGetSetDef bpy_bmedgeseq_getseters[] = { {"layers", (getter)bpy_bmelemseq_layers_get, (setter)NULL, bpy_bmelemseq_layers_edge_doc, (void *)BM_EDGE}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; static PyGetSetDef bpy_bmfaceseq_getseters[] = { {"layers", (getter)bpy_bmelemseq_layers_get, (setter)NULL, bpy_bmelemseq_layers_face_doc, (void *)BM_FACE}, /* face only */ {"active", (getter)bpy_bmfaceseq_active_get, (setter)bpy_bmfaceseq_active_set, bpy_bmfaceseq_active_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; static PyGetSetDef bpy_bmloopseq_getseters[] = { {"layers", (getter)bpy_bmelemseq_layers_get, (setter)NULL, bpy_bmelemseq_layers_loop_doc, (void *)BM_LOOP}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; /* Methods * ======= */ /* Mesh * ---- */ PyDoc_STRVAR(bpy_bmesh_copy_doc, ".. method:: copy()\n" "\n" " :return: A copy of this BMesh.\n" " :rtype: :class:`BMesh`\n"); static PyObject *bpy_bmesh_copy(BPy_BMesh *self) { BMesh *bm; BMesh *bm_copy; BPY_BM_CHECK_OBJ(self); bm = self->bm; bm_copy = BM_mesh_copy(bm); if (bm_copy) { return BPy_BMesh_CreatePyObject(bm_copy, BPY_BMFLAG_NOP); } PyErr_SetString(PyExc_SystemError, "Unable to copy BMesh, internal error"); return NULL; } PyDoc_STRVAR(bpy_bmesh_clear_doc, ".. method:: clear()\n" "\n" " Clear all mesh data.\n"); static PyObject *bpy_bmesh_clear(BPy_BMesh *self) { BMesh *bm; BPY_BM_CHECK_OBJ(self); bm = self->bm; BM_mesh_clear(bm); Py_RETURN_NONE; } PyDoc_STRVAR( bpy_bmesh_free_doc, ".. method:: free()\n" "\n" " Explicitly free the BMesh data from memory, causing exceptions on further access.\n" "\n" " .. note::\n" "\n" " The BMesh is freed automatically, typically when the script finishes executing.\n" " However in some cases its hard to predict when this will be and its useful to\n" " explicitly free the data.\n"); static PyObject *bpy_bmesh_free(BPy_BMesh *self) { if (self->bm) { BMesh *bm = self->bm; bm_dealloc_editmode_warn(self); if ((self->flag & BPY_BMFLAG_IS_WRAPPED) == 0) { BM_mesh_free(bm); } bpy_bm_generic_invalidate((BPy_BMGeneric *)self); } Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmesh_to_mesh_doc, ".. method:: to_mesh(mesh)\n" "\n" " Writes this BMesh data into an existing Mesh datablock.\n" "\n" " :arg mesh: The mesh data to write into.\n" " :type mesh: :class:`Mesh`\n"); static PyObject *bpy_bmesh_to_mesh(BPy_BMesh *self, PyObject *args) { PyObject *py_mesh; Mesh *me; BMesh *bm; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "O:to_mesh", &py_mesh) || !(me = PyC_RNA_AsPointer(py_mesh, "Mesh"))) { return NULL; } /* we could allow this but its almost certainly _not_ what script authors want */ if (me->edit_mesh) { PyErr_Format(PyExc_ValueError, "to_mesh(): Mesh '%s' is in editmode", me->id.name + 2); return NULL; } bm = self->bm; struct Main *bmain = NULL; struct BMeshToMeshParams params = { .update_shapekey_indices = true, }; if (me->id.tag & LIB_TAG_NO_MAIN) { /* Mesh might be coming from a self-contained source like object.to_mesh(). No need to remap * anything in this case. */ } else { BLI_assert(BKE_id_is_in_global_main(&me->id)); bmain = G_MAIN; /* XXX UGLY! */ params.calc_object_remap = true; } BM_mesh_bm_to_me(bmain, bm, me, ¶ms); /* we could have the user do this but if they forget blender can easy crash * since the references arrays for the objects derived meshes are now invalid */ DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY_ALL_MODES); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmesh_from_object_doc, ".. method:: from_object(object, depsgraph, cage=False, face_normals=True, " "vertex_normals=True)\n" "\n" " Initialize this bmesh from existing object data-block (only meshes are currently " "supported).\n" "\n" " :arg object: The object data to load.\n" " :type object: :class:`Object`\n" " :arg cage: Get the mesh as a deformed cage.\n" " :type cage: boolean\n" " :arg face_normals: Calculate face normals.\n" " :type face_normals: boolean\n" " :arg vertex_normals: Calculate vertex normals.\n" " :type vertex_normals: boolean\n"); static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject *kw) { static const char *kwlist[] = { "object", "depsgraph", "cage", "face_normals", "vertex_normals", NULL}; PyObject *py_object; PyObject *py_depsgraph; Object *ob, *ob_eval; struct Depsgraph *depsgraph; struct Scene *scene_eval; Mesh *me_eval; BMesh *bm; bool use_cage = false; bool use_fnorm = true; bool use_vert_normal = true; const CustomData_MeshMasks data_masks = CD_MASK_BMESH; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|$O&O&O&:from_object", (char **)kwlist, &py_object, &py_depsgraph, PyC_ParseBool, &use_cage, PyC_ParseBool, &use_fnorm, PyC_ParseBool, &use_vert_normal) || !(ob = PyC_RNA_AsPointer(py_object, "Object")) || !(depsgraph = PyC_RNA_AsPointer(py_depsgraph, "Depsgraph"))) { return NULL; } if (ob->type != OB_MESH) { PyErr_SetString(PyExc_ValueError, "from_object(...): currently only mesh objects are supported"); return NULL; } const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER; scene_eval = DEG_get_evaluated_scene(depsgraph); ob_eval = DEG_get_evaluated_object(depsgraph, ob); bool need_free = false; /* Write the display mesh into the dummy mesh */ if (use_render) { if (use_cage) { PyErr_SetString(PyExc_ValueError, "from_object(...): cage arg is unsupported when dependency graph " "evaluation mode is RENDER"); return NULL; } me_eval = BKE_mesh_new_from_object(depsgraph, ob_eval, true, false); need_free = true; } else { if (use_cage) { me_eval = mesh_get_eval_deform(depsgraph, scene_eval, ob_eval, &data_masks); } else { me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &data_masks); } } if (me_eval == NULL) { PyErr_Format(PyExc_ValueError, "from_object(...): Object '%s' has no usable mesh data", ob->id.name + 2); return NULL; } bm = self->bm; BM_mesh_bm_from_me(bm, me_eval, (&(struct BMeshFromMeshParams){ .calc_face_normal = use_fnorm, .calc_vert_normal = use_vert_normal, })); if (need_free) { BKE_id_free(NULL, me_eval); } Py_RETURN_NONE; } PyDoc_STRVAR( bpy_bmesh_from_mesh_doc, ".. method:: from_mesh(mesh, face_normals=True, vertex_normals=True, use_shape_key=False, " "shape_key_index=0)\n" "\n" " Initialize this bmesh from existing mesh datablock.\n" "\n" " :arg mesh: The mesh data to load.\n" " :type mesh: :class:`Mesh`\n" " :arg use_shape_key: Use the locations from a shape key.\n" " :type use_shape_key: boolean\n" " :arg shape_key_index: The shape key index to use.\n" " :type shape_key_index: int\n" "\n" " .. note::\n" "\n" " Multiple calls can be used to join multiple meshes.\n" "\n" " Custom-data layers are only copied from ``mesh`` on initialization.\n" " Further calls will copy custom-data to matching layers, layers missing on the target " "mesh won't be added.\n"); static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject *kw) { static const char *kwlist[] = { "mesh", "face_normals", "vertex_normals", "use_shape_key", "shape_key_index", NULL}; BMesh *bm; PyObject *py_mesh; Mesh *me; bool use_fnorm = true; bool use_vert_normal = true; bool use_shape_key = false; int shape_key_index = 0; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTupleAndKeywords(args, kw, "O|$O&O&O&i:from_mesh", (char **)kwlist, &py_mesh, PyC_ParseBool, &use_fnorm, PyC_ParseBool, &use_vert_normal, PyC_ParseBool, &use_shape_key, &shape_key_index) || !(me = PyC_RNA_AsPointer(py_mesh, "Mesh"))) { return NULL; } bm = self->bm; BM_mesh_bm_from_me(bm, me, (&(struct BMeshFromMeshParams){ .calc_face_normal = use_fnorm, .calc_vert_normal = use_vert_normal, .use_shapekey = use_shape_key, .active_shapekey = shape_key_index + 1, })); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmesh_select_flush_mode_doc, ".. method:: select_flush_mode()\n" "\n" " flush selection based on the current mode current :class:`BMesh.select_mode`.\n"); static PyObject *bpy_bmesh_select_flush_mode(BPy_BMesh *self) { BPY_BM_CHECK_OBJ(self); BM_mesh_select_mode_flush(self->bm); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmesh_select_flush_doc, ".. method:: select_flush(select)\n" "\n" " Flush selection, independent of the current selection mode.\n" "\n" " :arg select: flush selection or de-selected elements.\n" " :type select: boolean\n"); static PyObject *bpy_bmesh_select_flush(BPy_BMesh *self, PyObject *value) { int param; BPY_BM_CHECK_OBJ(self); if ((param = PyC_Long_AsBool(value)) == -1) { return NULL; } if (param) { BM_mesh_select_flush(self->bm); } else { BM_mesh_deselect_flush(self->bm); } Py_RETURN_NONE; } PyDoc_STRVAR( bpy_bmesh_normal_update_doc, ".. method:: normal_update()\n" "\n" " Update normals of mesh faces and verts.\n" "\n" " .. note::\n" "\n" " The normal of any vertex where :attr:`is_wire` is True will be a zero vector.\n"); static PyObject *bpy_bmesh_normal_update(BPy_BMesh *self) { BPY_BM_CHECK_OBJ(self); BM_mesh_normals_update(self->bm); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmesh_transform_doc, ".. method:: transform(matrix, filter=None)\n" "\n" " Transform the mesh (optionally filtering flagged data only).\n" "\n" " :arg matrix: transform matrix.\n" " :type matrix: 4x4 :class:`mathutils.Matrix`\n" " :arg filter: set of values in " BPY_BM_HFLAG_ALL_STR ".\n" " :type filter: set\n"); static PyObject *bpy_bmesh_transform(BPy_BMElem *self, PyObject *args, PyObject *kw) { static const char *kwlist[] = {"matrix", "filter", NULL}; MatrixObject *mat; PyObject *filter = NULL; int filter_flags = 0; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTupleAndKeywords(args, kw, "O!|$O!:transform", (char **)kwlist, &matrix_Type, &mat, &PySet_Type, &filter)) { return NULL; } BMVert *eve; BMIter iter; void *mat_ptr; if (BaseMath_ReadCallback(mat) == -1) { return NULL; } if (mat->col_num != 4 || mat->row_num != 4) { PyErr_SetString(PyExc_ValueError, "expected a 4x4 matrix"); return NULL; } if (filter != NULL && PyC_FlagSet_ToBitfield( bpy_bm_hflag_all_flags, filter, &filter_flags, "bm.transform") == -1) { return NULL; } mat_ptr = mat->matrix; if (!filter_flags) { BM_ITER_MESH (eve, &iter, self->bm, BM_VERTS_OF_MESH) { mul_m4_v3((float(*)[4])mat_ptr, eve->co); } } else { const char filter_flags_ch = (char)filter_flags; BM_ITER_MESH (eve, &iter, self->bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(eve, filter_flags_ch)) { mul_m4_v3((float(*)[4])mat_ptr, eve->co); } } } Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmesh_calc_volume_doc, ".. method:: calc_volume(signed=False)\n" "\n" " Calculate mesh volume based on face normals.\n" "\n" " :arg signed: when signed is true, negative values may be returned.\n" " :type signed: bool\n" " :return: The volume of the mesh.\n" " :rtype: float\n"); static PyObject *bpy_bmesh_calc_volume(BPy_BMElem *self, PyObject *args, PyObject *kw) { static const char *kwlist[] = {"signed", NULL}; PyObject *is_signed = Py_False; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTupleAndKeywords( args, kw, "|$O!:calc_volume", (char **)kwlist, &PyBool_Type, &is_signed)) { return NULL; } return PyFloat_FromDouble(BM_mesh_calc_volume(self->bm, is_signed != Py_False)); } PyDoc_STRVAR(bpy_bmesh_calc_loop_triangles_doc, ".. method:: calc_loop_triangles()\n" "\n" " Calculate triangle tessellation from quads/ngons.\n" "\n" " :return: The triangulated faces.\n" " :rtype: list of :class:`BMLoop` tuples\n"); static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self) { BMesh *bm; int looptris_tot; BMLoop *(*looptris)[3]; PyObject *ret; int i; BPY_BM_CHECK_OBJ(self); bm = self->bm; looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); looptris = PyMem_MALLOC(sizeof(*looptris) * looptris_tot); BM_mesh_calc_tessellation(bm, looptris); ret = PyList_New(looptris_tot); for (i = 0; i < looptris_tot; i++) { PyList_SET_ITEM(ret, i, BPy_BMLoop_Array_As_Tuple(bm, looptris[i], 3)); } PyMem_FREE(looptris); return ret; } /* Elem * ---- */ PyDoc_STRVAR(bpy_bm_elem_select_set_doc, ".. method:: select_set(select)\n" "\n" " Set the selection.\n" " This is different from the *select* attribute because it updates the selection " "state of associated geometry.\n" "\n" " :arg select: Select or de-select.\n" " :type select: boolean\n" "\n" " .. note::\n" "\n" " Currently this only flushes down, so selecting a face will select all its " "vertices but de-selecting a vertex " " won't de-select all the faces that use it, before finishing with a mesh " "typically flushing is still needed.\n"); static PyObject *bpy_bm_elem_select_set(BPy_BMElem *self, PyObject *value) { int param; BPY_BM_CHECK_OBJ(self); if ((param = PyC_Long_AsBool(value)) == -1) { return NULL; } BM_elem_select_set(self->bm, self->ele, param); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bm_elem_hide_set_doc, ".. method:: hide_set(hide)\n" "\n" " Set the hide state.\n" " This is different from the *hide* attribute because it updates the selection and " "hide state of associated geometry.\n" "\n" " :arg hide: Hidden or visible.\n" " :type hide: boolean\n"); static PyObject *bpy_bm_elem_hide_set(BPy_BMElem *self, PyObject *value) { int param; BPY_BM_CHECK_OBJ(self); if ((param = PyC_Long_AsBool(value)) == -1) { return NULL; } BM_elem_hide_set(self->bm, self->ele, param); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bm_elem_copy_from_doc, ".. method:: copy_from(other)\n" "\n" " Copy values from another element of matching type.\n"); static PyObject *bpy_bm_elem_copy_from(BPy_BMElem *self, BPy_BMElem *value) { BPY_BM_CHECK_OBJ(self); if (Py_TYPE(self) != Py_TYPE(value)) { PyErr_Format(PyExc_TypeError, "expected element of type '%.200s' not '%.200s'", Py_TYPE(self)->tp_name, Py_TYPE(value)->tp_name); return NULL; } if (value->ele != self->ele) { BM_elem_attrs_copy_ex(value->bm, self->bm, value->ele, self->ele, 0xff, CD_MASK_BM_ELEM_PYPTR); } Py_RETURN_NONE; } /* Vert * ---- */ PyDoc_STRVAR(bpy_bmvert_copy_from_vert_interp_doc, ".. method:: copy_from_vert_interp(vert_pair, fac)\n" "\n" " Interpolate the customdata from a vert between 2 other verts.\n" "\n" " :arg vert_pair: The vert to interpolate data from.\n" " :type vert_pair: :class:`BMVert`\n"); static PyObject *bpy_bmvert_copy_from_vert_interp(BPy_BMVert *self, PyObject *args) { PyObject *vert_seq; float fac; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "Of:BMVert.copy_from_vert_interp", &vert_seq, &fac)) { return NULL; } BMesh *bm = self->bm; BMVert **vert_array = NULL; Py_ssize_t vert_seq_len; /* always 2 */ vert_array = BPy_BMElem_PySeq_As_Array(&bm, vert_seq, 2, 2, &vert_seq_len, BM_VERT, true, true, "BMVert.copy_from_vert_interp(...)"); if (vert_array == NULL) { return NULL; } BM_data_interp_from_verts(bm, vert_array[0], vert_array[1], self->v, clamp_f(fac, 0.0f, 1.0f)); PyMem_FREE(vert_array); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmvert_copy_from_face_interp_doc, ".. method:: copy_from_face_interp(face)\n" "\n" " Interpolate the customdata from a face onto this loop (the loops vert should " "overlap the face).\n" "\n" " :arg face: The face to interpolate data from.\n" " :type face: :class:`BMFace`\n"); static PyObject *bpy_bmvert_copy_from_face_interp(BPy_BMVert *self, PyObject *args) { BPy_BMFace *py_face = NULL; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "O!:BMVert.copy_from_face_interp", &BPy_BMFace_Type, &py_face)) { return NULL; } BMesh *bm = self->bm; BPY_BM_CHECK_SOURCE_OBJ(bm, "copy_from_face_interp()", py_face); BM_vert_interp_from_face(bm, self->v, py_face->f); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmvert_calc_edge_angle_doc, ".. method:: calc_edge_angle(fallback=None)\n" "\n" " Return the angle between this vert's two connected edges.\n" "\n" " :arg fallback: return this when the vert doesn't have 2 edges\n" " (instead of raising a :exc:`ValueError`).\n" " :type fallback: any\n" " :return: Angle between edges in radians.\n" " :rtype: float\n"); static PyObject *bpy_bmvert_calc_edge_angle(BPy_BMVert *self, PyObject *args) { const float angle_invalid = -1.0f; float angle; PyObject *fallback = NULL; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "|O:calc_edge_angle", &fallback)) { return NULL; } angle = BM_vert_calc_edge_angle_ex(self->v, angle_invalid); if (angle == angle_invalid) { /* avoid exception */ if (fallback) { Py_INCREF(fallback); return fallback; } PyErr_SetString(PyExc_ValueError, "BMVert.calc_edge_angle(): " "vert must connect to exactly 2 edges"); return NULL; } return PyFloat_FromDouble(angle); } PyDoc_STRVAR( bpy_bmvert_calc_shell_factor_doc, ".. method:: calc_shell_factor()\n" "\n" " Return a multiplier calculated based on the sharpness of the vertex.\n" " Where a flat surface gives 1.0, and higher values sharper edges.\n" " This is used to maintain shell thickness when offsetting verts along their normals.\n" "\n" " :return: offset multiplier\n" " :rtype: float\n"); static PyObject *bpy_bmvert_calc_shell_factor(BPy_BMVert *self) { BPY_BM_CHECK_OBJ(self); return PyFloat_FromDouble(BM_vert_calc_shell_factor(self->v)); } PyDoc_STRVAR(bpy_bmvert_normal_update_doc, ".. method:: normal_update()\n" "\n" " Update vertex normal.\n" " This does not update the normals of adjoining faces.\n" "\n" " .. note::\n" "\n" " The vertex normal will be a zero vector if vertex :attr:`is_wire` is True.\n"); static PyObject *bpy_bmvert_normal_update(BPy_BMVert *self) { BPY_BM_CHECK_OBJ(self); BM_vert_normal_update(self->v); Py_RETURN_NONE; } /* Edge * ---- */ PyDoc_STRVAR(bpy_bmedge_calc_length_doc, ".. method:: calc_length()\n" "\n" " :return: The length between both verts.\n" " :rtype: float\n"); static PyObject *bpy_bmedge_calc_length(BPy_BMEdge *self) { BPY_BM_CHECK_OBJ(self); return PyFloat_FromDouble(len_v3v3(self->e->v1->co, self->e->v2->co)); } PyDoc_STRVAR(bpy_bmedge_calc_face_angle_doc, ".. method:: calc_face_angle(fallback=None)\n" "\n" " :arg fallback: return this when the edge doesn't have 2 faces\n" " (instead of raising a :exc:`ValueError`).\n" " :type fallback: any\n" " :return: The angle between 2 connected faces in radians.\n" " :rtype: float\n"); static PyObject *bpy_bmedge_calc_face_angle(BPy_BMEdge *self, PyObject *args) { const float angle_invalid = -1.0f; float angle; PyObject *fallback = NULL; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "|O:calc_face_angle", &fallback)) { return NULL; } angle = BM_edge_calc_face_angle_ex(self->e, angle_invalid); if (angle == angle_invalid) { /* avoid exception */ if (fallback) { Py_INCREF(fallback); return fallback; } PyErr_SetString(PyExc_ValueError, "BMEdge.calc_face_angle(): " "edge doesn't use 2 faces"); return NULL; } return PyFloat_FromDouble(angle); } PyDoc_STRVAR( bpy_bmedge_calc_face_angle_signed_doc, ".. method:: calc_face_angle_signed(fallback=None)\n" "\n" " :arg fallback: return this when the edge doesn't have 2 faces\n" " (instead of raising a :exc:`ValueError`).\n" " :type fallback: any\n" " :return: The angle between 2 connected faces in radians (negative for concave join).\n" " :rtype: float\n"); static PyObject *bpy_bmedge_calc_face_angle_signed(BPy_BMEdge *self, PyObject *args) { const float angle_invalid = -FLT_MAX; float angle; PyObject *fallback = NULL; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "|O:calc_face_angle_signed", &fallback)) { return NULL; } angle = BM_edge_calc_face_angle_signed_ex(self->e, angle_invalid); if (angle == angle_invalid) { /* avoid exception */ if (fallback) { Py_INCREF(fallback); return fallback; } PyErr_SetString(PyExc_ValueError, "BMEdge.calc_face_angle_signed(): " "edge doesn't use 2 faces"); return NULL; } return PyFloat_FromDouble(angle); } PyDoc_STRVAR( bpy_bmedge_calc_tangent_doc, ".. method:: calc_tangent(loop)\n" "\n" " Return the tangent at this edge relative to a face (pointing inward into the face).\n" " This uses the face normal for calculation.\n" "\n" " :arg loop: The loop used for tangent calculation.\n" " :type loop: :class:`BMLoop`\n" " :return: a normalized vector.\n" " :rtype: :class:`mathutils.Vector`\n"); static PyObject *bpy_bmedge_calc_tangent(BPy_BMEdge *self, PyObject *args) { BPy_BMLoop *py_loop; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "O!:BMEdge.calc_face_tangent", &BPy_BMLoop_Type, &py_loop)) { return NULL; } float vec[3]; BPY_BM_CHECK_OBJ(py_loop); /* no need to check if they are from the same mesh or even connected */ BM_edge_calc_face_tangent(self->e, py_loop->l, vec); return Vector_CreatePyObject(vec, 3, NULL); } PyDoc_STRVAR( bpy_bmedge_other_vert_doc, ".. method:: other_vert(vert)\n" "\n" " Return the other vertex on this edge or None if the vertex is not used by this edge.\n" "\n" " :arg vert: a vert in this edge.\n" " :type vert: :class:`BMVert`\n" " :return: The edges other vert.\n" " :rtype: :class:`BMVert` or None\n"); static PyObject *bpy_bmedge_other_vert(BPy_BMEdge *self, BPy_BMVert *value) { BMVert *other; BPY_BM_CHECK_OBJ(self); if (!BPy_BMVert_Check(value)) { PyErr_Format(PyExc_TypeError, "BMEdge.other_vert(vert): BMVert expected, not '%.200s'", Py_TYPE(value)->tp_name); return NULL; } BPY_BM_CHECK_SOURCE_OBJ(self->bm, "BMEdge.other_vert(vert)", value); other = BM_edge_other_vert(self->e, value->v); if (other) { return BPy_BMVert_CreatePyObject(self->bm, other); } /* could raise an exception here */ Py_RETURN_NONE; } PyDoc_STRVAR( bpy_bmedge_normal_update_doc, ".. method:: normal_update()\n" "\n" " Update normals of all connected faces and the edge verts.\n" "\n" " .. note::\n" "\n" " The normal of edge vertex will be a zero vector if vertex :attr:`is_wire` is True.\n"); static PyObject *bpy_bmedge_normal_update(BPy_BMEdge *self) { BPY_BM_CHECK_OBJ(self); BM_edge_normals_update(self->e); Py_RETURN_NONE; } /* Face * ---- */ PyDoc_STRVAR( bpy_bmface_copy_from_face_interp_doc, ".. method:: copy_from_face_interp(face, vert=True)\n" "\n" " Interpolate the customdata from another face onto this one (faces should overlap).\n" "\n" " :arg face: The face to interpolate data from.\n" " :type face: :class:`BMFace`\n" " :arg vert: When True, also copy vertex data.\n" " :type vert: boolean\n"); static PyObject *bpy_bmface_copy_from_face_interp(BPy_BMFace *self, PyObject *args) { BPy_BMFace *py_face = NULL; bool do_vertex = true; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "O!|O&:BMFace.copy_from_face_interp", &BPy_BMFace_Type, &py_face, PyC_ParseBool, &do_vertex)) { return NULL; } BMesh *bm = self->bm; BPY_BM_CHECK_SOURCE_OBJ(bm, "BMFace.copy_from_face_interp(face)", py_face); BM_face_interp_from_face(bm, self->f, py_face->f, do_vertex); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmface_copy_doc, ".. method:: copy(verts=True, edges=True)\n" "\n" " Make a copy of this face.\n" "\n" " :arg verts: When set, the faces verts will be duplicated too.\n" " :type verts: boolean\n" " :arg edges: When set, the faces edges will be duplicated too.\n" " :type edges: boolean\n" " :return: The newly created face.\n" " :rtype: :class:`BMFace`\n"); static PyObject *bpy_bmface_copy(BPy_BMFace *self, PyObject *args, PyObject *kw) { static const char *kwlist[] = {"verts", "edges", NULL}; BMesh *bm = self->bm; bool do_verts = true; bool do_edges = true; BMFace *f_cpy; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTupleAndKeywords(args, kw, "|$O&O&:BMFace.copy", (char **)kwlist, PyC_ParseBool, &do_verts, PyC_ParseBool, &do_edges)) { return NULL; } f_cpy = BM_face_copy(bm, bm, self->f, do_verts, do_edges); if (f_cpy) { return BPy_BMFace_CreatePyObject(bm, f_cpy); } PyErr_SetString(PyExc_ValueError, "BMFace.copy(): couldn't create the new face, internal error"); return NULL; } PyDoc_STRVAR(bpy_bmface_calc_area_doc, ".. method:: calc_area()\n" "\n" " Return the area of the face.\n" "\n" " :return: Return the area of the face.\n" " :rtype: float\n"); static PyObject *bpy_bmface_calc_area(BPy_BMFace *self) { BPY_BM_CHECK_OBJ(self); return PyFloat_FromDouble(BM_face_calc_area(self->f)); } PyDoc_STRVAR(bpy_bmface_calc_perimeter_doc, ".. method:: calc_perimeter()\n" "\n" " Return the perimeter of the face.\n" "\n" " :return: Return the perimeter of the face.\n" " :rtype: float\n"); static PyObject *bpy_bmface_calc_perimeter(BPy_BMFace *self) { BPY_BM_CHECK_OBJ(self); return PyFloat_FromDouble(BM_face_calc_perimeter(self->f)); } PyDoc_STRVAR(bpy_bmface_calc_tangent_edge_doc, ".. method:: calc_tangent_edge()\n" "\n" " Return face tangent based on longest edge.\n" "\n" " :return: a normalized vector.\n" " :rtype: :class:`mathutils.Vector`\n"); static PyObject *bpy_bmface_calc_tangent_edge(BPy_BMFace *self) { float tangent[3]; BPY_BM_CHECK_OBJ(self); BM_face_calc_tangent_edge(self->f, tangent); return Vector_CreatePyObject(tangent, 3, NULL); } PyDoc_STRVAR(bpy_bmface_calc_tangent_edge_pair_doc, ".. method:: calc_tangent_edge_pair()\n" "\n" " Return face tangent based on the two longest disconnected edges.\n" "\n" " - Tris: Use the edge pair with the most similar lengths.\n" " - Quads: Use the longest edge pair.\n" " - NGons: Use the two longest disconnected edges.\n" "\n" " :return: a normalized vector.\n" " :rtype: :class:`mathutils.Vector`\n"); static PyObject *bpy_bmface_calc_tangent_edge_pair(BPy_BMFace *self) { float tangent[3]; BPY_BM_CHECK_OBJ(self); BM_face_calc_tangent_edge_pair(self->f, tangent); return Vector_CreatePyObject(tangent, 3, NULL); } PyDoc_STRVAR(bpy_bmface_calc_tangent_edge_diagonal_doc, ".. method:: calc_tangent_edge_diagonal()\n" "\n" " Return face tangent based on the edge farthest from any vertex.\n" "\n" " :return: a normalized vector.\n" " :rtype: :class:`mathutils.Vector`\n"); static PyObject *bpy_bmface_calc_tangent_edge_diagonal(BPy_BMFace *self) { float tangent[3]; BPY_BM_CHECK_OBJ(self); BM_face_calc_tangent_edge_diagonal(self->f, tangent); return Vector_CreatePyObject(tangent, 3, NULL); } PyDoc_STRVAR(bpy_bmface_calc_tangent_vert_diagonal_doc, ".. method:: calc_tangent_vert_diagonal()\n" "\n" " Return face tangent based on the two most distant vertices.\n" "\n" " :return: a normalized vector.\n" " :rtype: :class:`mathutils.Vector`\n"); static PyObject *bpy_bmface_calc_tangent_vert_diagonal(BPy_BMFace *self) { float tangent[3]; BPY_BM_CHECK_OBJ(self); BM_face_calc_tangent_vert_diagonal(self->f, tangent); return Vector_CreatePyObject(tangent, 3, NULL); } PyDoc_STRVAR(bpy_bmface_calc_center_median_doc, ".. method:: calc_center_median()\n" "\n" " Return median center of the face.\n" "\n" " :return: a 3D vector.\n" " :rtype: :class:`mathutils.Vector`\n"); static PyObject *bpy_bmface_calc_center_mean(BPy_BMFace *self) { float cent[3]; BPY_BM_CHECK_OBJ(self); BM_face_calc_center_median(self->f, cent); return Vector_CreatePyObject(cent, 3, NULL); } PyDoc_STRVAR(bpy_bmface_calc_center_median_weighted_doc, ".. method:: calc_center_median_weighted()\n" "\n" " Return median center of the face weighted by edge lengths.\n" "\n" " :return: a 3D vector.\n" " :rtype: :class:`mathutils.Vector`\n"); static PyObject *bpy_bmface_calc_center_median_weighted(BPy_BMFace *self) { float cent[3]; BPY_BM_CHECK_OBJ(self); BM_face_calc_center_median_weighted(self->f, cent); return Vector_CreatePyObject(cent, 3, NULL); } PyDoc_STRVAR(bpy_bmface_calc_center_bounds_doc, ".. method:: calc_center_bounds()\n" "\n" " Return bounds center of the face.\n" "\n" " :return: a 3D vector.\n" " :rtype: :class:`mathutils.Vector`\n"); static PyObject *bpy_bmface_calc_center_bounds(BPy_BMFace *self) { float cent[3]; BPY_BM_CHECK_OBJ(self); BM_face_calc_center_bounds(self->f, cent); return Vector_CreatePyObject(cent, 3, NULL); } PyDoc_STRVAR(bpy_bmface_normal_update_doc, ".. method:: normal_update()\n" "\n" " Update face normal based on the positions of the face verts.\n" " This does not update the normals of face verts.\n"); static PyObject *bpy_bmface_normal_update(BPy_BMFace *self) { BPY_BM_CHECK_OBJ(self); BM_face_normal_update(self->f); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmface_normal_flip_doc, ".. method:: normal_flip()\n" "\n" " Reverses winding of a face, which flips its normal.\n"); static PyObject *bpy_bmface_normal_flip(BPy_BMFace *self) { BPY_BM_CHECK_OBJ(self); BM_face_normal_flip(self->bm, self->f); Py_RETURN_NONE; } /* Loop * ---- */ PyDoc_STRVAR(bpy_bmloop_copy_from_face_interp_doc, ".. method:: copy_from_face_interp(face, vert=True, multires=True)\n" "\n" " Interpolate the customdata from a face onto this loop (the loops vert should " "overlap the face).\n" "\n" " :arg face: The face to interpolate data from.\n" " :type face: :class:`BMFace`\n" " :arg vert: When enabled, interpolate the loops vertex data (optional).\n" " :type vert: boolean\n" " :arg multires: When enabled, interpolate the loops multires data (optional).\n" " :type multires: boolean\n"); static PyObject *bpy_bmloop_copy_from_face_interp(BPy_BMLoop *self, PyObject *args) { BPy_BMFace *py_face = NULL; bool do_vertex = true; bool do_multires = true; BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "O!|O&O&:BMLoop.copy_from_face_interp", &BPy_BMFace_Type, &py_face, PyC_ParseBool, &do_vertex, PyC_ParseBool, &do_multires)) { return NULL; } BMesh *bm = self->bm; BPY_BM_CHECK_SOURCE_OBJ(bm, "BMLoop.copy_from_face_interp(face)", py_face); BM_loop_interp_from_face(bm, self->l, py_face->f, do_vertex, do_multires); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmloop_calc_angle_doc, ".. method:: calc_angle()\n" "\n" " Return the angle at this loops corner of the face.\n" " This is calculated so sharper corners give lower angles.\n" "\n" " :return: The angle in radians.\n" " :rtype: float\n"); static PyObject *bpy_bmloop_calc_angle(BPy_BMLoop *self) { BPY_BM_CHECK_OBJ(self); return PyFloat_FromDouble(BM_loop_calc_face_angle(self->l)); } PyDoc_STRVAR(bpy_bmloop_calc_normal_doc, ".. method:: calc_normal()\n" "\n" " Return normal at this loops corner of the face.\n" " Falls back to the face normal for straight lines.\n" "\n" " :return: a normalized vector.\n" " :rtype: :class:`mathutils.Vector`\n"); static PyObject *bpy_bmloop_calc_normal(BPy_BMLoop *self) { float vec[3]; BPY_BM_CHECK_OBJ(self); BM_loop_calc_face_normal(self->l, vec); return Vector_CreatePyObject(vec, 3, NULL); } PyDoc_STRVAR( bpy_bmloop_calc_tangent_doc, ".. method:: calc_tangent()\n" "\n" " Return the tangent at this loops corner of the face (pointing inward into the face).\n" " Falls back to the face normal for straight lines.\n" "\n" " :return: a normalized vector.\n" " :rtype: :class:`mathutils.Vector`\n"); static PyObject *bpy_bmloop_calc_tangent(BPy_BMLoop *self) { float vec[3]; BPY_BM_CHECK_OBJ(self); BM_loop_calc_face_tangent(self->l, vec); return Vector_CreatePyObject(vec, 3, NULL); } /* Vert Seq * -------- */ PyDoc_STRVAR(bpy_bmvertseq_new_doc, ".. method:: new(co=(0.0, 0.0, 0.0), example=None)\n" "\n" " Create a new vertex.\n" "\n" " :arg co: The initial location of the vertex (optional argument).\n" " :type co: float triplet\n" " :arg example: Existing vert to initialize settings.\n" " :type example: :class:`BMVert`\n" " :return: The newly created edge.\n" " :rtype: :class:`BMVert`\n"); static PyObject *bpy_bmvertseq_new(BPy_BMElemSeq *self, PyObject *args) { PyObject *py_co = NULL; BPy_BMVert *py_vert_example = NULL; /* optional */ BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "|OO!:verts.new", &py_co, &BPy_BMVert_Type, &py_vert_example)) { return NULL; } BMesh *bm = self->bm; BMVert *v; float co[3] = {0.0f, 0.0f, 0.0f}; if (py_vert_example) { BPY_BM_CHECK_OBJ(py_vert_example); } if (py_co && mathutils_array_parse(co, 3, 3, py_co, "verts.new(co)") == -1) { return NULL; } v = BM_vert_create(bm, co, NULL, BM_CREATE_NOP); if (v == NULL) { PyErr_SetString(PyExc_ValueError, "faces.new(verts): couldn't create the new face, internal error"); return NULL; } if (py_vert_example) { BM_elem_attrs_copy(py_vert_example->bm, bm, py_vert_example->v, v); } return BPy_BMVert_CreatePyObject(bm, v); } /* Edge Seq * -------- */ PyDoc_STRVAR(bpy_bmedgeseq_new_doc, ".. method:: new(verts, example=None)\n" "\n" " Create a new edge from a given pair of verts.\n" "\n" " :arg verts: Vertex pair.\n" " :type verts: pair of :class:`BMVert`\n" " :arg example: Existing edge to initialize settings (optional argument).\n" " :type example: :class:`BMEdge`\n" " :return: The newly created edge.\n" " :rtype: :class:`BMEdge`\n"); static PyObject *bpy_bmedgeseq_new(BPy_BMElemSeq *self, PyObject *args) { PyObject *vert_seq; BPy_BMEdge *py_edge_example = NULL; /* optional */ BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "O|O!:edges.new", &vert_seq, &BPy_BMEdge_Type, &py_edge_example)) { return NULL; } BMesh *bm = self->bm; BMEdge *e; BMVert **vert_array = NULL; Py_ssize_t vert_seq_len; /* always 2 */ PyObject *ret = NULL; if (py_edge_example) { BPY_BM_CHECK_OBJ(py_edge_example); } vert_array = BPy_BMElem_PySeq_As_Array( &bm, vert_seq, 2, 2, &vert_seq_len, BM_VERT, true, true, "edges.new(...)"); if (vert_array == NULL) { return NULL; } if (BM_edge_exists(vert_array[0], vert_array[1])) { PyErr_SetString(PyExc_ValueError, "edges.new(): this edge exists"); goto cleanup; } e = BM_edge_create(bm, vert_array[0], vert_array[1], NULL, BM_CREATE_NOP); if (e == NULL) { PyErr_SetString(PyExc_ValueError, "faces.new(verts): couldn't create the new face, internal error"); goto cleanup; } if (py_edge_example) { BM_elem_attrs_copy(py_edge_example->bm, bm, py_edge_example->e, e); } ret = BPy_BMEdge_CreatePyObject(bm, e); cleanup: if (vert_array) { PyMem_FREE(vert_array); } return ret; } /* Face Seq * -------- */ PyDoc_STRVAR(bpy_bmfaceseq_new_doc, ".. method:: new(verts, example=None)\n" "\n" " Create a new face from a given set of verts.\n" "\n" " :arg verts: Sequence of 3 or more verts.\n" " :type verts: :class:`BMVert`\n" " :arg example: Existing face to initialize settings (optional argument).\n" " :type example: :class:`BMFace`\n" " :return: The newly created face.\n" " :rtype: :class:`BMFace`\n"); static PyObject *bpy_bmfaceseq_new(BPy_BMElemSeq *self, PyObject *args) { PyObject *vert_seq; BPy_BMFace *py_face_example = NULL; /* optional */ BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "O|O!:faces.new", &vert_seq, &BPy_BMFace_Type, &py_face_example)) { return NULL; } BMesh *bm = self->bm; Py_ssize_t vert_seq_len; BMVert **vert_array = NULL; PyObject *ret = NULL; BMFace *f_new; if (py_face_example) { BPY_BM_CHECK_OBJ(py_face_example); } vert_array = BPy_BMElem_PySeq_As_Array( &bm, vert_seq, 3, PY_SSIZE_T_MAX, &vert_seq_len, BM_VERT, true, true, "faces.new(...)"); if (vert_array == NULL) { return NULL; } /* check if the face exists */ if (BM_face_exists(vert_array, vert_seq_len) != NULL) { PyErr_SetString(PyExc_ValueError, "faces.new(verts): face already exists"); goto cleanup; } /* Go ahead and make the face! * --------------------------- */ f_new = BM_face_create_verts(bm, vert_array, vert_seq_len, py_face_example ? py_face_example->f : NULL, BM_CREATE_NOP, true); if (UNLIKELY(f_new == NULL)) { PyErr_SetString(PyExc_ValueError, "faces.new(verts): couldn't create the new face, internal error"); goto cleanup; } ret = BPy_BMFace_CreatePyObject(bm, f_new); /* pass through */ cleanup: if (vert_array) { PyMem_FREE(vert_array); } return ret; } /* Elem Seq * -------- */ PyDoc_STRVAR(bpy_bmvertseq_remove_doc, ".. method:: remove(vert)\n" "\n" " Remove a vert.\n"); static PyObject *bpy_bmvertseq_remove(BPy_BMElemSeq *self, BPy_BMVert *value) { BPY_BM_CHECK_OBJ(self); if (!BPy_BMVert_Check(value)) { return NULL; } BMesh *bm = self->bm; BPY_BM_CHECK_SOURCE_OBJ(bm, "verts.remove(vert)", value); BM_vert_kill(bm, value->v); bpy_bm_generic_invalidate((BPy_BMGeneric *)value); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmedgeseq_remove_doc, ".. method:: remove(edge)\n" "\n" " Remove an edge.\n"); static PyObject *bpy_bmedgeseq_remove(BPy_BMElemSeq *self, BPy_BMEdge *value) { BPY_BM_CHECK_OBJ(self); if (!BPy_BMEdge_Check(value)) { return NULL; } BMesh *bm = self->bm; BPY_BM_CHECK_SOURCE_OBJ(bm, "edges.remove(edges)", value); BM_edge_kill(bm, value->e); bpy_bm_generic_invalidate((BPy_BMGeneric *)value); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmfaceseq_remove_doc, ".. method:: remove(face)\n" "\n" " Remove a face.\n"); static PyObject *bpy_bmfaceseq_remove(BPy_BMElemSeq *self, BPy_BMFace *value) { BPY_BM_CHECK_OBJ(self); if (!BPy_BMFace_Check(value)) { return NULL; } BMesh *bm = self->bm; BPY_BM_CHECK_SOURCE_OBJ(bm, "faces.remove(face)", value); BM_face_kill(bm, value->f); bpy_bm_generic_invalidate((BPy_BMGeneric *)value); Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmedgeseq_get__method_doc, ".. method:: get(verts, fallback=None)\n" "\n" " Return an edge which uses the **verts** passed.\n" "\n" " :arg verts: Sequence of verts.\n" " :type verts: :class:`BMVert`\n" " :arg fallback: Return this value if nothing is found.\n" " :return: The edge found or None\n" " :rtype: :class:`BMEdge`\n"); static PyObject *bpy_bmedgeseq_get__method(BPy_BMElemSeq *self, PyObject *args) { PyObject *vert_seq; PyObject *fallback = Py_None; /* optional */ BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "O|O:edges.get", &vert_seq, &fallback)) { return NULL; } BMesh *bm = self->bm; BMEdge *e; BMVert **vert_array = NULL; Py_ssize_t vert_seq_len; /* always 2 */ PyObject *ret = NULL; vert_array = BPy_BMElem_PySeq_As_Array( &bm, vert_seq, 2, 2, &vert_seq_len, BM_VERT, true, true, "edges.get(...)"); if (vert_array == NULL) { return NULL; } if ((e = BM_edge_exists(vert_array[0], vert_array[1]))) { ret = BPy_BMEdge_CreatePyObject(bm, e); } else { ret = fallback; Py_INCREF(ret); } PyMem_FREE(vert_array); return ret; } PyDoc_STRVAR(bpy_bmfaceseq_get__method_doc, ".. method:: get(verts, fallback=None)\n" "\n" " Return a face which uses the **verts** passed.\n" "\n" " :arg verts: Sequence of verts.\n" " :type verts: :class:`BMVert`\n" " :arg fallback: Return this value if nothing is found.\n" " :return: The face found or None\n" " :rtype: :class:`BMFace`\n"); static PyObject *bpy_bmfaceseq_get__method(BPy_BMElemSeq *self, PyObject *args) { PyObject *vert_seq; PyObject *fallback = Py_None; /* optional */ BPY_BM_CHECK_OBJ(self); if (!PyArg_ParseTuple(args, "O|O:faces.get", &vert_seq, &fallback)) { return NULL; } BMesh *bm = self->bm; BMFace *f = NULL; BMVert **vert_array = NULL; Py_ssize_t vert_seq_len; PyObject *ret = NULL; vert_array = BPy_BMElem_PySeq_As_Array( &bm, vert_seq, 1, PY_SSIZE_T_MAX, &vert_seq_len, BM_VERT, true, true, "faces.get(...)"); if (vert_array == NULL) { return NULL; } f = BM_face_exists(vert_array, vert_seq_len); if (f != NULL) { ret = BPy_BMFace_CreatePyObject(bm, f); } else { ret = fallback; Py_INCREF(ret); } PyMem_FREE(vert_array); return ret; } PyDoc_STRVAR( bpy_bmelemseq_index_update_doc, ".. method:: index_update()\n" "\n" " Initialize the index values of this sequence.\n" "\n" " This is the equivalent of looping over all elements and assigning the index values.\n" "\n" " .. code-block:: python\n" "\n" " for index, ele in enumerate(sequence):\n" " ele.index = index\n" "\n" " .. note::\n" "\n" " Running this on sequences besides :class:`BMesh.verts`, :class:`BMesh.edges`, " ":class:`BMesh.faces`\n" " works but won't result in each element having a valid index, instead its order in the " "sequence will be set.\n"); static PyObject *bpy_bmelemseq_index_update(BPy_BMElemSeq *self) { BMesh *bm = self->bm; BPY_BM_CHECK_OBJ(self); switch ((BMIterType)self->itype) { case BM_VERTS_OF_MESH: BM_mesh_elem_index_ensure(self->bm, BM_VERT); break; case BM_EDGES_OF_MESH: BM_mesh_elem_index_ensure(self->bm, BM_EDGE); break; case BM_FACES_OF_MESH: BM_mesh_elem_index_ensure(self->bm, BM_FACE); break; default: { BMIter iter; BMElem *ele; int index = 0; const char htype = bm_iter_itype_htype_map[self->itype]; BM_ITER_BPY_BM_SEQ (ele, &iter, self) { BM_elem_index_set(ele, index); /* set_dirty! */ index++; } /* since this isn't the normal vert/edge/face loops, * we're setting dirty values here. so tag as dirty. */ bm->elem_index_dirty |= htype; break; } } Py_RETURN_NONE; } PyDoc_STRVAR(bpy_bmelemseq_ensure_lookup_table_doc, ".. method:: ensure_lookup_table()\n" "\n" " Ensure internal data needed for int subscription is initialized with " "verts/edges/faces, eg ``bm.verts[index]``.\n" "\n" " This needs to be called again after adding/removing data in this sequence."); static PyObject *bpy_bmelemseq_ensure_lookup_table(BPy_BMElemSeq *self) { BPY_BM_CHECK_OBJ(self); BM_mesh_elem_table_ensure(self->bm, bm_iter_itype_htype_map[self->itype]); Py_RETURN_NONE; } PyDoc_STRVAR( bpy_bmelemseq_sort_doc, ".. method:: sort(key=None, reverse=False)\n" "\n" " Sort the elements of this sequence, using an optional custom sort key.\n" " Indices of elements are not changed, BMElemeSeq.index_update() can be used for that.\n" "\n" " :arg key: The key that sets the ordering of the elements.\n" " :type key: :function: returning a number\n" " :arg reverse: Reverse the order of the elements\n" " :type reverse: :boolean:\n" "\n" " .. note::\n" "\n" " When the 'key' argument is not provided, the elements are reordered following their " "current index value.\n" " In particular this can be used by setting indices manually before calling this " "method.\n" "\n" " .. warning::\n" "\n" " Existing references to the N'th element, will continue to point the data at that " "index.\n"); /* Use a static variable here because there is the need to sort some array * doing comparisons on elements of another array, qsort_r would have been * wonderful to use here, but unfortunately it is not standard and it's not * portable across different platforms. * * If a portable alternative to qsort_r becomes available, remove this static * var hack! * * NOTE: the functions below assumes the keys array has been allocated and it * has enough elements to complete the task. */ static int bpy_bmelemseq_sort_cmp_by_keys_ascending(const void *index1_v, const void *index2_v, void *keys_v) { const double *keys = keys_v; const int *index1 = (int *)index1_v; const int *index2 = (int *)index2_v; if (keys[*index1] < keys[*index2]) { return -1; } if (keys[*index1] > keys[*index2]) { return 1; } return 0; } static int bpy_bmelemseq_sort_cmp_by_keys_descending(const void *index1_v, const void *index2_v, void *keys_v) { return -bpy_bmelemseq_sort_cmp_by_keys_ascending(index1_v, index2_v, keys_v); } static PyObject *bpy_bmelemseq_sort(BPy_BMElemSeq *self, PyObject *args, PyObject *kw) { static const char *kwlist[] = {"key", "reverse", NULL}; PyObject *keyfunc = NULL; /* optional */ bool do_reverse = false; /* optional */ const char htype = bm_iter_itype_htype_map[self->itype]; int n_elem; BMIter iter; BMElem *ele; double *keys; int *elem_idx; uint *elem_map_idx; int (*elem_idx_compare_by_keys)(const void *, const void *, void *); uint *vert_idx = NULL; uint *edge_idx = NULL; uint *face_idx = NULL; int i; BMesh *bm = self->bm; BPY_BM_CHECK_OBJ(self); if (args != NULL) { if (!PyArg_ParseTupleAndKeywords(args, kw, "|$OO&:BMElemSeq.sort", (char **)kwlist, &keyfunc, PyC_ParseBool, &do_reverse)) { return NULL; } } if (keyfunc != NULL && !PyCallable_Check(keyfunc)) { PyErr_SetString(PyExc_TypeError, "the 'key' argument is not a callable object"); return NULL; } n_elem = BM_mesh_elem_count(bm, htype); if (n_elem <= 1) { /* 0 or 1 elements: sorted already */ Py_RETURN_NONE; } keys = PyMem_MALLOC(sizeof(*keys) * n_elem); if (keys == NULL) { PyErr_NoMemory(); return NULL; } i = 0; BM_ITER_BPY_BM_SEQ (ele, &iter, self) { if (keyfunc != NULL) { PyObject *py_elem; PyObject *index; py_elem = BPy_BMElem_CreatePyObject(self->bm, (BMHeader *)ele); index = PyObject_CallFunctionObjArgs(keyfunc, py_elem, NULL); Py_DECREF(py_elem); if (index == NULL) { /* No need to set the exception here, * PyObject_CallFunctionObjArgs() does that */ PyMem_FREE(keys); return NULL; } if ((keys[i] = PyFloat_AsDouble(index)) == -1 && PyErr_Occurred()) { PyErr_SetString(PyExc_ValueError, "the value returned by the 'key' function is not a number"); Py_DECREF(index); PyMem_FREE(keys); return NULL; } Py_DECREF(index); } else { /* If the 'key' function is not provided we sort * according to the current index values */ keys[i] = ele->head.index; } i++; } elem_idx = PyMem_MALLOC(sizeof(*elem_idx) * n_elem); if (elem_idx == NULL) { PyErr_NoMemory(); PyMem_FREE(keys); return NULL; } /* Initialize the element index array */ range_vn_i(elem_idx, n_elem, 0); /* Sort the index array according to the order of the 'keys' array */ if (do_reverse) { elem_idx_compare_by_keys = bpy_bmelemseq_sort_cmp_by_keys_descending; } else { elem_idx_compare_by_keys = bpy_bmelemseq_sort_cmp_by_keys_ascending; } BLI_qsort_r(elem_idx, n_elem, sizeof(*elem_idx), elem_idx_compare_by_keys, keys); elem_map_idx = PyMem_MALLOC(sizeof(*elem_map_idx) * n_elem); if (elem_map_idx == NULL) { PyErr_NoMemory(); PyMem_FREE(elem_idx); PyMem_FREE(keys); return NULL; } /* Initialize the map array * * We need to know the index such that if used as the new_index in * BM_mesh_remap() will give the order of the sorted keys like in * elem_idx */ for (i = 0; i < n_elem; i++) { elem_map_idx[elem_idx[i]] = i; } switch ((BMIterType)self->itype) { case BM_VERTS_OF_MESH: vert_idx = elem_map_idx; break; case BM_EDGES_OF_MESH: edge_idx = elem_map_idx; break; case BM_FACES_OF_MESH: face_idx = elem_map_idx; break; default: PyErr_Format(PyExc_TypeError, "element type %d not supported", self->itype); PyMem_FREE(elem_map_idx); PyMem_FREE(elem_idx); PyMem_FREE(keys); return NULL; } BM_mesh_remap(bm, vert_idx, edge_idx, face_idx); PyMem_FREE(elem_map_idx); PyMem_FREE(elem_idx); PyMem_FREE(keys); Py_RETURN_NONE; } static struct PyMethodDef bpy_bmesh_methods[] = { /* utility */ {"copy", (PyCFunction)bpy_bmesh_copy, METH_NOARGS, bpy_bmesh_copy_doc}, {"clear", (PyCFunction)bpy_bmesh_clear, METH_NOARGS, bpy_bmesh_clear_doc}, {"free", (PyCFunction)bpy_bmesh_free, METH_NOARGS, bpy_bmesh_free_doc}, /* conversion */ {"from_object", (PyCFunction)bpy_bmesh_from_object, METH_VARARGS | METH_KEYWORDS, bpy_bmesh_from_object_doc}, {"from_mesh", (PyCFunction)bpy_bmesh_from_mesh, METH_VARARGS | METH_KEYWORDS, bpy_bmesh_from_mesh_doc}, {"to_mesh", (PyCFunction)bpy_bmesh_to_mesh, METH_VARARGS, bpy_bmesh_to_mesh_doc}, /* meshdata */ {"select_flush_mode", (PyCFunction)bpy_bmesh_select_flush_mode, METH_NOARGS, bpy_bmesh_select_flush_mode_doc}, {"select_flush", (PyCFunction)bpy_bmesh_select_flush, METH_O, bpy_bmesh_select_flush_doc}, {"normal_update", (PyCFunction)bpy_bmesh_normal_update, METH_NOARGS, bpy_bmesh_normal_update_doc}, {"transform", (PyCFunction)bpy_bmesh_transform, METH_VARARGS | METH_KEYWORDS, bpy_bmesh_transform_doc}, /* calculations */ {"calc_volume", (PyCFunction)bpy_bmesh_calc_volume, METH_VARARGS | METH_KEYWORDS, bpy_bmesh_calc_volume_doc}, {"calc_loop_triangles", (PyCFunction)bpy_bmesh_calc_loop_triangles, METH_NOARGS, bpy_bmesh_calc_loop_triangles_doc}, {NULL, NULL, 0, NULL}, }; static struct PyMethodDef bpy_bmvert_methods[] = { {"select_set", (PyCFunction)bpy_bm_elem_select_set, METH_O, bpy_bm_elem_select_set_doc}, {"hide_set", (PyCFunction)bpy_bm_elem_hide_set, METH_O, bpy_bm_elem_hide_set_doc}, {"copy_from", (PyCFunction)bpy_bm_elem_copy_from, METH_O, bpy_bm_elem_copy_from_doc}, {"copy_from_face_interp", (PyCFunction)bpy_bmvert_copy_from_face_interp, METH_VARARGS, bpy_bmvert_copy_from_face_interp_doc}, {"copy_from_vert_interp", (PyCFunction)bpy_bmvert_copy_from_vert_interp, METH_VARARGS, bpy_bmvert_copy_from_vert_interp_doc}, {"calc_edge_angle", (PyCFunction)bpy_bmvert_calc_edge_angle, METH_VARARGS, bpy_bmvert_calc_edge_angle_doc}, {"calc_shell_factor", (PyCFunction)bpy_bmvert_calc_shell_factor, METH_NOARGS, bpy_bmvert_calc_shell_factor_doc}, {"normal_update", (PyCFunction)bpy_bmvert_normal_update, METH_NOARGS, bpy_bmvert_normal_update_doc}, {NULL, NULL, 0, NULL}, }; static struct PyMethodDef bpy_bmedge_methods[] = { {"select_set", (PyCFunction)bpy_bm_elem_select_set, METH_O, bpy_bm_elem_select_set_doc}, {"hide_set", (PyCFunction)bpy_bm_elem_hide_set, METH_O, bpy_bm_elem_hide_set_doc}, {"copy_from", (PyCFunction)bpy_bm_elem_copy_from, METH_O, bpy_bm_elem_copy_from_doc}, {"other_vert", (PyCFunction)bpy_bmedge_other_vert, METH_O, bpy_bmedge_other_vert_doc}, {"calc_length", (PyCFunction)bpy_bmedge_calc_length, METH_NOARGS, bpy_bmedge_calc_length_doc}, {"calc_face_angle", (PyCFunction)bpy_bmedge_calc_face_angle, METH_VARARGS, bpy_bmedge_calc_face_angle_doc}, {"calc_face_angle_signed", (PyCFunction)bpy_bmedge_calc_face_angle_signed, METH_VARARGS, bpy_bmedge_calc_face_angle_signed_doc}, {"calc_tangent", (PyCFunction)bpy_bmedge_calc_tangent, METH_VARARGS, bpy_bmedge_calc_tangent_doc}, {"normal_update", (PyCFunction)bpy_bmedge_normal_update, METH_NOARGS, bpy_bmedge_normal_update_doc}, {NULL, NULL, 0, NULL}, }; static struct PyMethodDef bpy_bmface_methods[] = { {"select_set", (PyCFunction)bpy_bm_elem_select_set, METH_O, bpy_bm_elem_select_set_doc}, {"hide_set", (PyCFunction)bpy_bm_elem_hide_set, METH_O, bpy_bm_elem_hide_set_doc}, {"copy_from", (PyCFunction)bpy_bm_elem_copy_from, METH_O, bpy_bm_elem_copy_from_doc}, {"copy_from_face_interp", (PyCFunction)bpy_bmface_copy_from_face_interp, METH_O, bpy_bmface_copy_from_face_interp_doc}, {"copy", (PyCFunction)bpy_bmface_copy, METH_VARARGS | METH_KEYWORDS, bpy_bmface_copy_doc}, {"calc_area", (PyCFunction)bpy_bmface_calc_area, METH_NOARGS, bpy_bmface_calc_area_doc}, {"calc_perimeter", (PyCFunction)bpy_bmface_calc_perimeter, METH_NOARGS, bpy_bmface_calc_perimeter_doc}, {"calc_tangent_edge", (PyCFunction)bpy_bmface_calc_tangent_edge, METH_NOARGS, bpy_bmface_calc_tangent_edge_doc}, {"calc_tangent_edge_pair", (PyCFunction)bpy_bmface_calc_tangent_edge_pair, METH_NOARGS, bpy_bmface_calc_tangent_edge_pair_doc}, {"calc_tangent_edge_diagonal", (PyCFunction)bpy_bmface_calc_tangent_edge_diagonal, METH_NOARGS, bpy_bmface_calc_tangent_edge_diagonal_doc}, {"calc_tangent_vert_diagonal", (PyCFunction)bpy_bmface_calc_tangent_vert_diagonal, METH_NOARGS, bpy_bmface_calc_tangent_vert_diagonal_doc}, {"calc_center_median", (PyCFunction)bpy_bmface_calc_center_mean, METH_NOARGS, bpy_bmface_calc_center_median_doc}, {"calc_center_median_weighted", (PyCFunction)bpy_bmface_calc_center_median_weighted, METH_NOARGS, bpy_bmface_calc_center_median_weighted_doc}, {"calc_center_bounds", (PyCFunction)bpy_bmface_calc_center_bounds, METH_NOARGS, bpy_bmface_calc_center_bounds_doc}, {"normal_update", (PyCFunction)bpy_bmface_normal_update, METH_NOARGS, bpy_bmface_normal_update_doc}, {"normal_flip", (PyCFunction)bpy_bmface_normal_flip, METH_NOARGS, bpy_bmface_normal_flip_doc}, {NULL, NULL, 0, NULL}, }; static struct PyMethodDef bpy_bmloop_methods[] = { {"copy_from", (PyCFunction)bpy_bm_elem_copy_from, METH_O, bpy_bm_elem_copy_from_doc}, {"copy_from_face_interp", (PyCFunction)bpy_bmloop_copy_from_face_interp, METH_O, bpy_bmloop_copy_from_face_interp_doc}, {"calc_angle", (PyCFunction)bpy_bmloop_calc_angle, METH_NOARGS, bpy_bmloop_calc_angle_doc}, {"calc_normal", (PyCFunction)bpy_bmloop_calc_normal, METH_NOARGS, bpy_bmloop_calc_normal_doc}, {"calc_tangent", (PyCFunction)bpy_bmloop_calc_tangent, METH_NOARGS, bpy_bmloop_calc_tangent_doc}, {NULL, NULL, 0, NULL}, }; static struct PyMethodDef bpy_bmelemseq_methods[] = { /* odd function, initializes index values */ {"index_update", (PyCFunction)bpy_bmelemseq_index_update, METH_NOARGS, bpy_bmelemseq_index_update_doc}, {NULL, NULL, 0, NULL}, }; static struct PyMethodDef bpy_bmvertseq_methods[] = { {"new", (PyCFunction)bpy_bmvertseq_new, METH_VARARGS, bpy_bmvertseq_new_doc}, {"remove", (PyCFunction)bpy_bmvertseq_remove, METH_O, bpy_bmvertseq_remove_doc}, /* odd function, initializes index values */ {"index_update", (PyCFunction)bpy_bmelemseq_index_update, METH_NOARGS, bpy_bmelemseq_index_update_doc}, {"ensure_lookup_table", (PyCFunction)bpy_bmelemseq_ensure_lookup_table, METH_NOARGS, bpy_bmelemseq_ensure_lookup_table_doc}, {"sort", (PyCFunction)bpy_bmelemseq_sort, METH_VARARGS | METH_KEYWORDS, bpy_bmelemseq_sort_doc}, {NULL, NULL, 0, NULL}, }; static struct PyMethodDef bpy_bmedgeseq_methods[] = { {"new", (PyCFunction)bpy_bmedgeseq_new, METH_VARARGS, bpy_bmedgeseq_new_doc}, {"remove", (PyCFunction)bpy_bmedgeseq_remove, METH_O, bpy_bmedgeseq_remove_doc}, /* 'bpy_bmelemseq_get' for different purpose */ {"get", (PyCFunction)bpy_bmedgeseq_get__method, METH_VARARGS, bpy_bmedgeseq_get__method_doc}, /* odd function, initializes index values */ {"index_update", (PyCFunction)bpy_bmelemseq_index_update, METH_NOARGS, bpy_bmelemseq_index_update_doc}, {"ensure_lookup_table", (PyCFunction)bpy_bmelemseq_ensure_lookup_table, METH_NOARGS, bpy_bmelemseq_ensure_lookup_table_doc}, {"sort", (PyCFunction)bpy_bmelemseq_sort, METH_VARARGS | METH_KEYWORDS, bpy_bmelemseq_sort_doc}, {NULL, NULL, 0, NULL}, }; static struct PyMethodDef bpy_bmfaceseq_methods[] = { {"new", (PyCFunction)bpy_bmfaceseq_new, METH_VARARGS, bpy_bmfaceseq_new_doc}, {"remove", (PyCFunction)bpy_bmfaceseq_remove, METH_O, bpy_bmfaceseq_remove_doc}, /* 'bpy_bmelemseq_get' for different purpose */ {"get", (PyCFunction)bpy_bmfaceseq_get__method, METH_VARARGS, bpy_bmfaceseq_get__method_doc}, /* odd function, initializes index values */ {"index_update", (PyCFunction)bpy_bmelemseq_index_update, METH_NOARGS, bpy_bmelemseq_index_update_doc}, {"ensure_lookup_table", (PyCFunction)bpy_bmelemseq_ensure_lookup_table, METH_NOARGS, bpy_bmelemseq_ensure_lookup_table_doc}, {"sort", (PyCFunction)bpy_bmelemseq_sort, METH_VARARGS | METH_KEYWORDS, bpy_bmelemseq_sort_doc}, {NULL, NULL, 0, NULL}, }; static struct PyMethodDef bpy_bmloopseq_methods[] = { /* odd function, initializes index values */ /* no: index_update() function since we can't iterate over loops */ /* no: sort() function since we can't iterate over loops */ {NULL, NULL, 0, NULL}, }; /* Sequences * ========= */ /* BMElemSeq / Iter * ---------------- */ static PyTypeObject *bpy_bm_itype_as_pytype(const char itype) { /* should cover all types */ switch ((BMIterType)itype) { case BM_VERTS_OF_MESH: case BM_VERTS_OF_FACE: case BM_VERTS_OF_EDGE: return &BPy_BMVert_Type; case BM_EDGES_OF_MESH: case BM_EDGES_OF_FACE: case BM_EDGES_OF_VERT: return &BPy_BMEdge_Type; case BM_FACES_OF_MESH: case BM_FACES_OF_EDGE: case BM_FACES_OF_VERT: return &BPy_BMFace_Type; // case BM_ALL_LOOPS_OF_FACE: case BM_LOOPS_OF_FACE: case BM_LOOPS_OF_EDGE: case BM_LOOPS_OF_VERT: case BM_LOOPS_OF_LOOP: return &BPy_BMLoop_Type; } return NULL; } static Py_ssize_t bpy_bmelemseq_length(BPy_BMElemSeq *self) { BPY_BM_CHECK_INT(self); /* first check if the size is known */ switch ((BMIterType)self->itype) { /* main-types */ case BM_VERTS_OF_MESH: return self->bm->totvert; case BM_EDGES_OF_MESH: return self->bm->totedge; case BM_FACES_OF_MESH: return self->bm->totface; /* sub-types */ case BM_VERTS_OF_FACE: case BM_EDGES_OF_FACE: case BM_LOOPS_OF_FACE: BPY_BM_CHECK_INT(self->py_ele); return ((BMFace *)self->py_ele->ele)->len; case BM_VERTS_OF_EDGE: return 2; default: /* quiet compiler */ break; } /* loop over all items, avoid this if we can */ { BMIter iter; BMHeader *ele; Py_ssize_t tot = 0; BM_ITER_BPY_BM_SEQ (ele, &iter, self) { tot++; } return tot; } } static PyObject *bpy_bmelemseq_subscript_int(BPy_BMElemSeq *self, int keynum) { BPY_BM_CHECK_OBJ(self); if (keynum < 0) { /* only get length on negative value, may loop entire seq */ keynum += bpy_bmelemseq_length(self); } if (keynum >= 0) { if (self->itype <= BM_FACES_OF_MESH) { if ((self->bm->elem_table_dirty & bm_iter_itype_htype_map[self->itype]) == 0) { BMHeader *ele = NULL; switch (self->itype) { case BM_VERTS_OF_MESH: if (keynum < self->bm->totvert) { ele = (BMHeader *)self->bm->vtable[keynum]; } break; case BM_EDGES_OF_MESH: if (keynum < self->bm->totedge) { ele = (BMHeader *)self->bm->etable[keynum]; } break; case BM_FACES_OF_MESH: if (keynum < self->bm->totface) { ele = (BMHeader *)self->bm->ftable[keynum]; } break; } if (ele) { return BPy_BMElem_CreatePyObject(self->bm, ele); } /* fall through to index error below */ } else { PyErr_SetString(PyExc_IndexError, "BMElemSeq[index]: outdated internal index table, " "run ensure_lookup_table() first"); return NULL; } } else { BMHeader *ele = BM_iter_at_index( self->bm, self->itype, self->py_ele ? self->py_ele->ele : NULL, keynum); if (ele) { return BPy_BMElem_CreatePyObject(self->bm, ele); } } } PyErr_Format(PyExc_IndexError, "BMElemSeq[index]: index %d out of range", keynum); return NULL; } static PyObject *bpy_bmelemseq_subscript_slice(BPy_BMElemSeq *self, Py_ssize_t start, Py_ssize_t stop) { BMIter iter; int count = 0; bool ok; PyObject *list; BMHeader *ele; BPY_BM_CHECK_OBJ(self); list = PyList_New(0); ok = BM_iter_init(&iter, self->bm, self->itype, self->py_ele ? self->py_ele->ele : NULL); BLI_assert(ok == true); if (UNLIKELY(ok == false)) { return list; } /* first loop up-until the start */ for (ok = true; ok; ok = (BM_iter_step(&iter) != NULL)) { if (count == start) { break; } count++; } /* add items until stop */ while ((ele = BM_iter_step(&iter))) { PyList_APPEND(list, BPy_BMElem_CreatePyObject(self->bm, ele)); count++; if (count == stop) { break; } } return list; } static PyObject *bpy_bmelemseq_subscript(BPy_BMElemSeq *self, PyObject *key) { /* don't need error check here */ if (PyIndex_Check(key)) { const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return NULL; } return bpy_bmelemseq_subscript_int(self, i); } if (PySlice_Check(key)) { PySliceObject *key_slice = (PySliceObject *)key; Py_ssize_t step = 1; if (key_slice->step != Py_None && !_PyEval_SliceIndex(key, &step)) { return NULL; } if (step != 1) { PyErr_SetString(PyExc_TypeError, "BMElemSeq[slice]: slice steps not supported"); return NULL; } if (key_slice->start == Py_None && key_slice->stop == Py_None) { return bpy_bmelemseq_subscript_slice(self, 0, PY_SSIZE_T_MAX); } Py_ssize_t start = 0, stop = PY_SSIZE_T_MAX; /* avoid PySlice_GetIndicesEx because it needs to know the length ahead of time. */ if (key_slice->start != Py_None && !_PyEval_SliceIndex(key_slice->start, &start)) { return NULL; } if (key_slice->stop != Py_None && !_PyEval_SliceIndex(key_slice->stop, &stop)) { return NULL; } if (start < 0 || stop < 0) { /* only get the length for negative values */ const Py_ssize_t len = bpy_bmelemseq_length(self); if (start < 0) { start += len; CLAMP_MIN(start, 0); } if (stop < 0) { stop += len; CLAMP_MIN(stop, 0); } } if (stop - start <= 0) { return PyList_New(0); } return bpy_bmelemseq_subscript_slice(self, start, stop); } PyErr_SetString(PyExc_AttributeError, "BMElemSeq[key]: invalid key, key must be an int"); return NULL; } static int bpy_bmelemseq_contains(BPy_BMElemSeq *self, PyObject *value) { BPY_BM_CHECK_INT(self); if (Py_TYPE(value) == bpy_bm_itype_as_pytype(self->itype)) { BPy_BMElem *value_bm_ele = (BPy_BMElem *)value; if (value_bm_ele->bm == self->bm) { BMElem *ele, *ele_test = value_bm_ele->ele; BMIter iter; BM_ITER_BPY_BM_SEQ (ele, &iter, self) { if (ele == ele_test) { return 1; } } } } return 0; } /* BMElem (customdata) * ------------------- */ static PyObject *bpy_bmelem_subscript(BPy_BMElem *self, BPy_BMLayerItem *key) { BPY_BM_CHECK_OBJ(self); return BPy_BMLayerItem_GetItem(self, key); } static int bpy_bmelem_ass_subscript(BPy_BMElem *self, BPy_BMLayerItem *key, PyObject *value) { BPY_BM_CHECK_INT(self); return BPy_BMLayerItem_SetItem(self, key, value); } static PySequenceMethods bpy_bmelemseq_as_sequence = { (lenfunc)bpy_bmelemseq_length, /* sq_length */ NULL, /* sq_concat */ NULL, /* sq_repeat */ (ssizeargfunc)bpy_bmelemseq_subscript_int, /* sq_item */ /* Only set this so PySequence_Check() returns True */ NULL, /* sq_slice */ (ssizeobjargproc)NULL, /* sq_ass_item */ NULL, /* *was* sq_ass_slice */ (objobjproc)bpy_bmelemseq_contains, /* sq_contains */ (binaryfunc)NULL, /* sq_inplace_concat */ (ssizeargfunc)NULL, /* sq_inplace_repeat */ }; static PyMappingMethods bpy_bmelemseq_as_mapping = { (lenfunc)bpy_bmelemseq_length, /* mp_length */ (binaryfunc)bpy_bmelemseq_subscript, /* mp_subscript */ (objobjargproc)NULL, /* mp_ass_subscript */ }; /* for customdata access */ static PyMappingMethods bpy_bm_elem_as_mapping = { (lenfunc)NULL, /* mp_length */ /* keep this empty, messes up 'if elem: ...' test */ (binaryfunc)bpy_bmelem_subscript, /* mp_subscript */ (objobjargproc)bpy_bmelem_ass_subscript, /* mp_ass_subscript */ }; /* Iterator * -------- */ static PyObject *bpy_bmelemseq_iter(BPy_BMElemSeq *self) { BPy_BMIter *py_iter; BPY_BM_CHECK_OBJ(self); py_iter = (BPy_BMIter *)BPy_BMIter_CreatePyObject(self->bm); BM_iter_init(&(py_iter->iter), self->bm, self->itype, self->py_ele ? self->py_ele->ele : NULL); return (PyObject *)py_iter; } static PyObject *bpy_bmiter_next(BPy_BMIter *self) { BMHeader *ele = BM_iter_step(&self->iter); if (ele == NULL) { PyErr_SetNone(PyExc_StopIteration); return NULL; } return (PyObject *)BPy_BMElem_CreatePyObject(self->bm, ele); } /* Deallocate Functions * ==================== */ static void bpy_bmesh_dealloc(BPy_BMesh *self) { BMesh *bm = self->bm; /* The mesh has not been freed by #BMesh. */ if (bm) { bm_dealloc_editmode_warn(self); if (CustomData_has_layer(&bm->vdata, CD_BM_ELEM_PYPTR)) { BM_data_layer_free(bm, &bm->vdata, CD_BM_ELEM_PYPTR); } if (CustomData_has_layer(&bm->edata, CD_BM_ELEM_PYPTR)) { BM_data_layer_free(bm, &bm->edata, CD_BM_ELEM_PYPTR); } if (CustomData_has_layer(&bm->pdata, CD_BM_ELEM_PYPTR)) { BM_data_layer_free(bm, &bm->pdata, CD_BM_ELEM_PYPTR); } if (CustomData_has_layer(&bm->ldata, CD_BM_ELEM_PYPTR)) { BM_data_layer_free(bm, &bm->ldata, CD_BM_ELEM_PYPTR); } bm->py_handle = NULL; if ((self->flag & BPY_BMFLAG_IS_WRAPPED) == 0) { BM_mesh_free(bm); } } PyObject_DEL(self); } static void bpy_bmvert_dealloc(BPy_BMElem *self) { BMesh *bm = self->bm; if (bm) { void **ptr = CustomData_bmesh_get(&bm->vdata, self->ele->head.data, CD_BM_ELEM_PYPTR); if (ptr) { *ptr = NULL; } } PyObject_DEL(self); } static void bpy_bmedge_dealloc(BPy_BMElem *self) { BMesh *bm = self->bm; if (bm) { void **ptr = CustomData_bmesh_get(&bm->edata, self->ele->head.data, CD_BM_ELEM_PYPTR); if (ptr) { *ptr = NULL; } } PyObject_DEL(self); } static void bpy_bmface_dealloc(BPy_BMElem *self) { BMesh *bm = self->bm; if (bm) { void **ptr = CustomData_bmesh_get(&bm->pdata, self->ele->head.data, CD_BM_ELEM_PYPTR); if (ptr) { *ptr = NULL; } } PyObject_DEL(self); } static void bpy_bmloop_dealloc(BPy_BMElem *self) { BMesh *bm = self->bm; if (bm) { void **ptr = CustomData_bmesh_get(&bm->ldata, self->ele->head.data, CD_BM_ELEM_PYPTR); if (ptr) { *ptr = NULL; } } PyObject_DEL(self); } static void bpy_bmelemseq_dealloc(BPy_BMElemSeq *self) { Py_XDECREF(self->py_ele); PyObject_DEL(self); } /* not sure where this should go */ static Py_hash_t bpy_bm_elem_hash(PyObject *self) { return _Py_HashPointer(((BPy_BMElem *)self)->ele); } static Py_hash_t bpy_bm_hash(PyObject *self) { return _Py_HashPointer(((BPy_BMesh *)self)->bm); } /* Type Docstrings * =============== */ PyDoc_STRVAR(bpy_bmesh_doc, "The BMesh data structure\n"); PyDoc_STRVAR(bpy_bmvert_doc, "The BMesh vertex type\n"); PyDoc_STRVAR(bpy_bmedge_doc, "The BMesh edge connecting 2 verts\n"); PyDoc_STRVAR(bpy_bmface_doc, "The BMesh face with 3 or more sides\n"); PyDoc_STRVAR(bpy_bmloop_doc, "This is normally accessed from :class:`BMFace.loops` where each face loop " "represents a corner of the face.\n"); PyDoc_STRVAR(bpy_bmelemseq_doc, "General sequence type used for accessing any sequence of\n" ":class:`BMVert`, :class:`BMEdge`, :class:`BMFace`, :class:`BMLoop`.\n" "\n" "When accessed via :class:`BMesh.verts`, :class:`BMesh.edges`, :class:`BMesh.faces`\n" "there are also functions to create/remove items.\n"); PyDoc_STRVAR(bpy_bmiter_doc, "Internal BMesh type for looping over verts/faces/edges,\n" "used for iterating over :class:`BMElemSeq` types.\n"); static PyObject *bpy_bmesh_repr(BPy_BMesh *self) { BMesh *bm = self->bm; if (bm) { return PyUnicode_FromFormat("", bm, bm->totvert, bm->totedge, bm->totface, bm->totloop); } return PyUnicode_FromFormat("", self); } static PyObject *bpy_bmvert_repr(BPy_BMVert *self) { BMesh *bm = self->bm; if (bm) { BMVert *v = self->v; return PyUnicode_FromFormat("", v, BM_elem_index_get(v)); } return PyUnicode_FromFormat("", self); } static PyObject *bpy_bmedge_repr(BPy_BMEdge *self) { BMesh *bm = self->bm; if (bm) { BMEdge *e = self->e; return PyUnicode_FromFormat("", e, BM_elem_index_get(e), e->v1, BM_elem_index_get(e->v1), e->v2, BM_elem_index_get(e->v2)); } return PyUnicode_FromFormat("", self); } static PyObject *bpy_bmface_repr(BPy_BMFace *self) { BMesh *bm = self->bm; if (bm) { BMFace *f = self->f; return PyUnicode_FromFormat( "", f, BM_elem_index_get(f), f->len); } return PyUnicode_FromFormat("", self); } static PyObject *bpy_bmloop_repr(BPy_BMLoop *self) { BMesh *bm = self->bm; if (bm) { BMLoop *l = self->l; return PyUnicode_FromFormat("", l, BM_elem_index_get(l), l->v, BM_elem_index_get(l->v), l->e, BM_elem_index_get(l->e), l->f, BM_elem_index_get(l->f)); } return PyUnicode_FromFormat("", self); } /* Types * ===== */ PyTypeObject BPy_BMesh_Type; PyTypeObject BPy_BMVert_Type; PyTypeObject BPy_BMEdge_Type; PyTypeObject BPy_BMFace_Type; PyTypeObject BPy_BMLoop_Type; PyTypeObject BPy_BMElemSeq_Type; PyTypeObject BPy_BMVertSeq_Type; PyTypeObject BPy_BMEdgeSeq_Type; PyTypeObject BPy_BMFaceSeq_Type; PyTypeObject BPy_BMLoopSeq_Type; PyTypeObject BPy_BMIter_Type; void BPy_BM_init_types(void) { BPy_BMesh_Type.tp_basicsize = sizeof(BPy_BMesh); BPy_BMVert_Type.tp_basicsize = sizeof(BPy_BMVert); BPy_BMEdge_Type.tp_basicsize = sizeof(BPy_BMEdge); BPy_BMFace_Type.tp_basicsize = sizeof(BPy_BMFace); BPy_BMLoop_Type.tp_basicsize = sizeof(BPy_BMLoop); BPy_BMElemSeq_Type.tp_basicsize = sizeof(BPy_BMElemSeq); BPy_BMVertSeq_Type.tp_basicsize = sizeof(BPy_BMElemSeq); BPy_BMEdgeSeq_Type.tp_basicsize = sizeof(BPy_BMElemSeq); BPy_BMFaceSeq_Type.tp_basicsize = sizeof(BPy_BMElemSeq); BPy_BMLoopSeq_Type.tp_basicsize = sizeof(BPy_BMElemSeq); BPy_BMIter_Type.tp_basicsize = sizeof(BPy_BMIter); BPy_BMesh_Type.tp_name = "BMesh"; BPy_BMVert_Type.tp_name = "BMVert"; BPy_BMEdge_Type.tp_name = "BMEdge"; BPy_BMFace_Type.tp_name = "BMFace"; BPy_BMLoop_Type.tp_name = "BMLoop"; BPy_BMElemSeq_Type.tp_name = "BMElemSeq"; BPy_BMVertSeq_Type.tp_name = "BMVertSeq"; BPy_BMEdgeSeq_Type.tp_name = "BMEdgeSeq"; BPy_BMFaceSeq_Type.tp_name = "BMFaceSeq"; BPy_BMLoopSeq_Type.tp_name = "BMLoopSeq"; BPy_BMIter_Type.tp_name = "BMIter"; BPy_BMesh_Type.tp_doc = bpy_bmesh_doc; BPy_BMVert_Type.tp_doc = bpy_bmvert_doc; BPy_BMEdge_Type.tp_doc = bpy_bmedge_doc; BPy_BMFace_Type.tp_doc = bpy_bmface_doc; BPy_BMLoop_Type.tp_doc = bpy_bmloop_doc; BPy_BMElemSeq_Type.tp_doc = bpy_bmelemseq_doc; BPy_BMVertSeq_Type.tp_doc = NULL; BPy_BMEdgeSeq_Type.tp_doc = NULL; BPy_BMFaceSeq_Type.tp_doc = NULL; BPy_BMLoopSeq_Type.tp_doc = NULL; BPy_BMIter_Type.tp_doc = bpy_bmiter_doc; BPy_BMesh_Type.tp_repr = (reprfunc)bpy_bmesh_repr; BPy_BMVert_Type.tp_repr = (reprfunc)bpy_bmvert_repr; BPy_BMEdge_Type.tp_repr = (reprfunc)bpy_bmedge_repr; BPy_BMFace_Type.tp_repr = (reprfunc)bpy_bmface_repr; BPy_BMLoop_Type.tp_repr = (reprfunc)bpy_bmloop_repr; BPy_BMElemSeq_Type.tp_repr = NULL; BPy_BMVertSeq_Type.tp_repr = NULL; BPy_BMEdgeSeq_Type.tp_repr = NULL; BPy_BMFaceSeq_Type.tp_repr = NULL; BPy_BMLoopSeq_Type.tp_repr = NULL; BPy_BMIter_Type.tp_repr = NULL; BPy_BMesh_Type.tp_getset = bpy_bmesh_getseters; BPy_BMVert_Type.tp_getset = bpy_bmvert_getseters; BPy_BMEdge_Type.tp_getset = bpy_bmedge_getseters; BPy_BMFace_Type.tp_getset = bpy_bmface_getseters; BPy_BMLoop_Type.tp_getset = bpy_bmloop_getseters; BPy_BMElemSeq_Type.tp_getset = NULL; BPy_BMVertSeq_Type.tp_getset = bpy_bmvertseq_getseters; BPy_BMEdgeSeq_Type.tp_getset = bpy_bmedgeseq_getseters; BPy_BMFaceSeq_Type.tp_getset = bpy_bmfaceseq_getseters; BPy_BMLoopSeq_Type.tp_getset = bpy_bmloopseq_getseters; BPy_BMIter_Type.tp_getset = NULL; BPy_BMesh_Type.tp_methods = bpy_bmesh_methods; BPy_BMVert_Type.tp_methods = bpy_bmvert_methods; BPy_BMEdge_Type.tp_methods = bpy_bmedge_methods; BPy_BMFace_Type.tp_methods = bpy_bmface_methods; BPy_BMLoop_Type.tp_methods = bpy_bmloop_methods; BPy_BMElemSeq_Type.tp_methods = bpy_bmelemseq_methods; BPy_BMVertSeq_Type.tp_methods = bpy_bmvertseq_methods; BPy_BMEdgeSeq_Type.tp_methods = bpy_bmedgeseq_methods; BPy_BMFaceSeq_Type.tp_methods = bpy_bmfaceseq_methods; BPy_BMLoopSeq_Type.tp_methods = bpy_bmloopseq_methods; BPy_BMIter_Type.tp_methods = NULL; /* #BPy_BMElem_Check() uses #bpy_bm_elem_hash() to check types. * if this changes update the macro. */ BPy_BMesh_Type.tp_hash = bpy_bm_hash; BPy_BMVert_Type.tp_hash = bpy_bm_elem_hash; BPy_BMEdge_Type.tp_hash = bpy_bm_elem_hash; BPy_BMFace_Type.tp_hash = bpy_bm_elem_hash; BPy_BMLoop_Type.tp_hash = bpy_bm_elem_hash; BPy_BMElemSeq_Type.tp_hash = NULL; BPy_BMVertSeq_Type.tp_hash = NULL; BPy_BMEdgeSeq_Type.tp_hash = NULL; BPy_BMFaceSeq_Type.tp_hash = NULL; BPy_BMLoopSeq_Type.tp_hash = NULL; BPy_BMIter_Type.tp_hash = NULL; BPy_BMElemSeq_Type.tp_as_sequence = &bpy_bmelemseq_as_sequence; BPy_BMVertSeq_Type.tp_as_sequence = &bpy_bmelemseq_as_sequence; BPy_BMEdgeSeq_Type.tp_as_sequence = &bpy_bmelemseq_as_sequence; BPy_BMFaceSeq_Type.tp_as_sequence = &bpy_bmelemseq_as_sequence; BPy_BMLoopSeq_Type.tp_as_sequence = NULL; /* this is not a seq really, only for layer access */ BPy_BMElemSeq_Type.tp_as_mapping = &bpy_bmelemseq_as_mapping; BPy_BMVertSeq_Type.tp_as_mapping = &bpy_bmelemseq_as_mapping; BPy_BMEdgeSeq_Type.tp_as_mapping = &bpy_bmelemseq_as_mapping; BPy_BMFaceSeq_Type.tp_as_mapping = &bpy_bmelemseq_as_mapping; BPy_BMLoopSeq_Type.tp_as_mapping = NULL; /* this is not a seq really, only for layer access */ /* layer access */ BPy_BMVert_Type.tp_as_mapping = &bpy_bm_elem_as_mapping; BPy_BMEdge_Type.tp_as_mapping = &bpy_bm_elem_as_mapping; BPy_BMFace_Type.tp_as_mapping = &bpy_bm_elem_as_mapping; BPy_BMLoop_Type.tp_as_mapping = &bpy_bm_elem_as_mapping; BPy_BMElemSeq_Type.tp_iter = (getiterfunc)bpy_bmelemseq_iter; BPy_BMVertSeq_Type.tp_iter = (getiterfunc)bpy_bmelemseq_iter; BPy_BMEdgeSeq_Type.tp_iter = (getiterfunc)bpy_bmelemseq_iter; BPy_BMFaceSeq_Type.tp_iter = (getiterfunc)bpy_bmelemseq_iter; BPy_BMLoopSeq_Type.tp_iter = NULL; /* no mapping */ /* only 1 iteratir so far */ BPy_BMIter_Type.tp_iternext = (iternextfunc)bpy_bmiter_next; BPy_BMIter_Type.tp_iter = PyObject_SelfIter; BPy_BMesh_Type.tp_dealloc = (destructor)bpy_bmesh_dealloc; BPy_BMVert_Type.tp_dealloc = (destructor)bpy_bmvert_dealloc; BPy_BMEdge_Type.tp_dealloc = (destructor)bpy_bmedge_dealloc; BPy_BMFace_Type.tp_dealloc = (destructor)bpy_bmface_dealloc; BPy_BMLoop_Type.tp_dealloc = (destructor)bpy_bmloop_dealloc; BPy_BMElemSeq_Type.tp_dealloc = (destructor)bpy_bmelemseq_dealloc; BPy_BMVertSeq_Type.tp_dealloc = (destructor)bpy_bmelemseq_dealloc; BPy_BMEdgeSeq_Type.tp_dealloc = (destructor)bpy_bmelemseq_dealloc; BPy_BMFaceSeq_Type.tp_dealloc = (destructor)bpy_bmelemseq_dealloc; BPy_BMLoopSeq_Type.tp_dealloc = (destructor)bpy_bmelemseq_dealloc; BPy_BMIter_Type.tp_dealloc = NULL; BPy_BMesh_Type.tp_flags = Py_TPFLAGS_DEFAULT; BPy_BMVert_Type.tp_flags = Py_TPFLAGS_DEFAULT; BPy_BMEdge_Type.tp_flags = Py_TPFLAGS_DEFAULT; BPy_BMFace_Type.tp_flags = Py_TPFLAGS_DEFAULT; BPy_BMLoop_Type.tp_flags = Py_TPFLAGS_DEFAULT; BPy_BMElemSeq_Type.tp_flags = Py_TPFLAGS_DEFAULT; BPy_BMVertSeq_Type.tp_flags = Py_TPFLAGS_DEFAULT; BPy_BMEdgeSeq_Type.tp_flags = Py_TPFLAGS_DEFAULT; BPy_BMFaceSeq_Type.tp_flags = Py_TPFLAGS_DEFAULT; BPy_BMLoopSeq_Type.tp_flags = Py_TPFLAGS_DEFAULT; BPy_BMIter_Type.tp_flags = Py_TPFLAGS_DEFAULT; PyType_Ready(&BPy_BMesh_Type); PyType_Ready(&BPy_BMVert_Type); PyType_Ready(&BPy_BMEdge_Type); PyType_Ready(&BPy_BMFace_Type); PyType_Ready(&BPy_BMLoop_Type); PyType_Ready(&BPy_BMElemSeq_Type); PyType_Ready(&BPy_BMVertSeq_Type); PyType_Ready(&BPy_BMEdgeSeq_Type); PyType_Ready(&BPy_BMFaceSeq_Type); PyType_Ready(&BPy_BMLoopSeq_Type); PyType_Ready(&BPy_BMIter_Type); } /* bmesh.types submodule * ********************* */ static struct PyModuleDef BPy_BM_types_module_def = { PyModuleDef_HEAD_INIT, "bmesh.types", /* m_name */ NULL, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ }; PyObject *BPyInit_bmesh_types(void) { PyObject *submodule; submodule = PyModule_Create(&BPy_BM_types_module_def); /* bmesh_py_types.c */ PyModule_AddType(submodule, &BPy_BMesh_Type); PyModule_AddType(submodule, &BPy_BMVert_Type); PyModule_AddType(submodule, &BPy_BMEdge_Type); PyModule_AddType(submodule, &BPy_BMFace_Type); PyModule_AddType(submodule, &BPy_BMLoop_Type); PyModule_AddType(submodule, &BPy_BMElemSeq_Type); PyModule_AddType(submodule, &BPy_BMVertSeq_Type); PyModule_AddType(submodule, &BPy_BMEdgeSeq_Type); PyModule_AddType(submodule, &BPy_BMFaceSeq_Type); PyModule_AddType(submodule, &BPy_BMLoopSeq_Type); PyModule_AddType(submodule, &BPy_BMIter_Type); /* bmesh_py_types_select.c */ PyModule_AddType(submodule, &BPy_BMEditSelSeq_Type); PyModule_AddType(submodule, &BPy_BMEditSelIter_Type); /* bmesh_py_types_customdata.c */ PyModule_AddType(submodule, &BPy_BMLayerAccessVert_Type); PyModule_AddType(submodule, &BPy_BMLayerAccessEdge_Type); PyModule_AddType(submodule, &BPy_BMLayerAccessFace_Type); PyModule_AddType(submodule, &BPy_BMLayerAccessLoop_Type); PyModule_AddType(submodule, &BPy_BMLayerCollection_Type); PyModule_AddType(submodule, &BPy_BMLayerItem_Type); /* bmesh_py_types_meshdata.c */ PyModule_AddType(submodule, &BPy_BMLoopUV_Type); PyModule_AddType(submodule, &BPy_BMDeformVert_Type); return submodule; } /* Utility Functions * ***************** */ PyObject *BPy_BMesh_CreatePyObject(BMesh *bm, int flag) { BPy_BMesh *self; if (bm->py_handle) { self = bm->py_handle; Py_INCREF(self); } else { self = PyObject_New(BPy_BMesh, &BPy_BMesh_Type); self->bm = bm; self->flag = flag; bm->py_handle = self; /* point back */ /* avoid allocating layers when we don't have to */ #if 0 BM_data_layer_add(bm, &bm->vdata, CD_BM_ELEM_PYPTR); BM_data_layer_add(bm, &bm->edata, CD_BM_ELEM_PYPTR); BM_data_layer_add(bm, &bm->pdata, CD_BM_ELEM_PYPTR); BM_data_layer_add(bm, &bm->ldata, CD_BM_ELEM_PYPTR); #endif } return (PyObject *)self; } PyObject *BPy_BMVert_CreatePyObject(BMesh *bm, BMVert *v) { BPy_BMVert *self; void **ptr = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_BM_ELEM_PYPTR); /* bmesh may free layers, ensure we have one to store ourself */ if (UNLIKELY(ptr == NULL)) { BM_data_layer_add(bm, &bm->vdata, CD_BM_ELEM_PYPTR); ptr = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_BM_ELEM_PYPTR); } if (*ptr != NULL) { self = *ptr; Py_INCREF(self); } else { self = PyObject_New(BPy_BMVert, &BPy_BMVert_Type); BLI_assert(v != NULL); self->bm = bm; self->v = v; *ptr = self; } return (PyObject *)self; } PyObject *BPy_BMEdge_CreatePyObject(BMesh *bm, BMEdge *e) { BPy_BMEdge *self; void **ptr = CustomData_bmesh_get(&bm->edata, e->head.data, CD_BM_ELEM_PYPTR); /* bmesh may free layers, ensure we have one to store ourself */ if (UNLIKELY(ptr == NULL)) { BM_data_layer_add(bm, &bm->edata, CD_BM_ELEM_PYPTR); ptr = CustomData_bmesh_get(&bm->edata, e->head.data, CD_BM_ELEM_PYPTR); } if (*ptr != NULL) { self = *ptr; Py_INCREF(self); } else { self = PyObject_New(BPy_BMEdge, &BPy_BMEdge_Type); BLI_assert(e != NULL); self->bm = bm; self->e = e; *ptr = self; } return (PyObject *)self; } PyObject *BPy_BMFace_CreatePyObject(BMesh *bm, BMFace *f) { BPy_BMFace *self; void **ptr = CustomData_bmesh_get(&bm->pdata, f->head.data, CD_BM_ELEM_PYPTR); /* bmesh may free layers, ensure we have one to store ourself */ if (UNLIKELY(ptr == NULL)) { BM_data_layer_add(bm, &bm->pdata, CD_BM_ELEM_PYPTR); ptr = CustomData_bmesh_get(&bm->pdata, f->head.data, CD_BM_ELEM_PYPTR); } if (*ptr != NULL) { self = *ptr; Py_INCREF(self); } else { self = PyObject_New(BPy_BMFace, &BPy_BMFace_Type); BLI_assert(f != NULL); self->bm = bm; self->f = f; *ptr = self; } return (PyObject *)self; } PyObject *BPy_BMLoop_CreatePyObject(BMesh *bm, BMLoop *l) { BPy_BMLoop *self; void **ptr = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_BM_ELEM_PYPTR); /* bmesh may free layers, ensure we have one to store ourself */ if (UNLIKELY(ptr == NULL)) { BM_data_layer_add(bm, &bm->ldata, CD_BM_ELEM_PYPTR); ptr = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_BM_ELEM_PYPTR); } if (*ptr != NULL) { self = *ptr; Py_INCREF(self); } else { self = PyObject_New(BPy_BMLoop, &BPy_BMLoop_Type); BLI_assert(l != NULL); self->bm = bm; self->l = l; *ptr = self; } return (PyObject *)self; } PyObject *BPy_BMElemSeq_CreatePyObject(BMesh *bm, BPy_BMElem *py_ele, const char itype) { BPy_BMElemSeq *self = PyObject_New(BPy_BMElemSeq, &BPy_BMElemSeq_Type); self->bm = bm; self->py_ele = py_ele; /* can be NULL */ self->itype = itype; Py_XINCREF(py_ele); return (PyObject *)self; } PyObject *BPy_BMVertSeq_CreatePyObject(BMesh *bm) { BPy_BMElemSeq *self = PyObject_New(BPy_BMElemSeq, &BPy_BMVertSeq_Type); self->bm = bm; self->py_ele = NULL; /* unused */ self->itype = BM_VERTS_OF_MESH; return (PyObject *)self; } PyObject *BPy_BMEdgeSeq_CreatePyObject(BMesh *bm) { BPy_BMElemSeq *self = PyObject_New(BPy_BMElemSeq, &BPy_BMEdgeSeq_Type); self->bm = bm; self->py_ele = NULL; /* unused */ self->itype = BM_EDGES_OF_MESH; return (PyObject *)self; } PyObject *BPy_BMFaceSeq_CreatePyObject(BMesh *bm) { BPy_BMElemSeq *self = PyObject_New(BPy_BMElemSeq, &BPy_BMFaceSeq_Type); self->bm = bm; self->py_ele = NULL; /* unused */ self->itype = BM_FACES_OF_MESH; return (PyObject *)self; } PyObject *BPy_BMLoopSeq_CreatePyObject(BMesh *bm) { BPy_BMElemSeq *self = PyObject_New(BPy_BMElemSeq, &BPy_BMLoopSeq_Type); self->bm = bm; self->py_ele = NULL; /* unused */ self->itype = 0; /* should never be passed to the iterator function */ return (PyObject *)self; } PyObject *BPy_BMIter_CreatePyObject(BMesh *bm) { BPy_BMIter *self = PyObject_New(BPy_BMIter, &BPy_BMIter_Type); self->bm = bm; /* caller must initialize 'iter' member */ return (PyObject *)self; } PyObject *BPy_BMElem_CreatePyObject(BMesh *bm, BMHeader *ele) { switch (ele->htype) { case BM_VERT: return BPy_BMVert_CreatePyObject(bm, (BMVert *)ele); case BM_EDGE: return BPy_BMEdge_CreatePyObject(bm, (BMEdge *)ele); case BM_FACE: return BPy_BMFace_CreatePyObject(bm, (BMFace *)ele); case BM_LOOP: return BPy_BMLoop_CreatePyObject(bm, (BMLoop *)ele); default: BLI_assert_unreachable(); PyErr_SetString(PyExc_SystemError, "internal error"); return NULL; } } int bpy_bm_generic_valid_check(BPy_BMGeneric *self) { if (LIKELY(self->bm)) { /* far too slow to enable by default but handy * to uncomment for debugging tricky errors, * note that this will throw error on entering a * function where the actual error will be caused by * the previous action. */ #if 0 if (BM_mesh_validate(self->bm) == false) { PyErr_Format( PyExc_ReferenceError, "BMesh used by %.200s has become invalid", Py_TYPE(self)->tp_name); return -1; } #endif return 0; } PyErr_Format( PyExc_ReferenceError, "BMesh data of type %.200s has been removed", Py_TYPE(self)->tp_name); return -1; } int bpy_bm_generic_valid_check_source(BMesh *bm_source, const char *error_prefix, void **args, uint args_tot) { int ret = 0; while (args_tot--) { BPy_BMGeneric *py_bm_elem = args[args_tot]; if (py_bm_elem) { BLI_assert(BPy_BMesh_Check(py_bm_elem) || BPy_BMElem_Check(py_bm_elem)); ret = bpy_bm_generic_valid_check(py_bm_elem); if (UNLIKELY(ret == -1)) { break; } if (UNLIKELY(py_bm_elem->bm != bm_source)) { /* could give more info here */ PyErr_Format(PyExc_ValueError, "%.200s: BMesh data of type %.200s is from another mesh", error_prefix, Py_TYPE(py_bm_elem)->tp_name); ret = -1; break; } } } return ret; } void bpy_bm_generic_invalidate(BPy_BMGeneric *self) { self->bm = NULL; } void *BPy_BMElem_PySeq_As_Array_FAST(BMesh **r_bm, PyObject *seq_fast, Py_ssize_t min, Py_ssize_t max, Py_ssize_t *r_size, const char htype, const bool do_unique_check, const bool do_bm_check, const char *error_prefix) { BMesh *bm = (r_bm && *r_bm) ? *r_bm : NULL; PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast); const Py_ssize_t seq_len = PySequence_Fast_GET_SIZE(seq_fast); Py_ssize_t i, i_last_dirty = PY_SSIZE_T_MAX; BPy_BMElem *item; BMElem **alloc; *r_size = 0; if (seq_len < min || seq_len > max) { PyErr_Format(PyExc_TypeError, "%s: sequence incorrect size, expected [%d - %d], given %d", error_prefix, min, max, seq_len); return NULL; } /* from now on, use goto */ alloc = PyMem_MALLOC(seq_len * sizeof(BPy_BMElem **)); for (i = 0; i < seq_len; i++) { item = (BPy_BMElem *)seq_fast_items[i]; if (!BPy_BMElem_CheckHType(Py_TYPE(item), htype)) { PyErr_Format(PyExc_TypeError, "%s: expected %.200s, not '%.200s'", error_prefix, BPy_BMElem_StringFromHType(htype), Py_TYPE(item)->tp_name); goto err_cleanup; } else if (!BPY_BM_IS_VALID(item)) { PyErr_Format( PyExc_TypeError, "%s: %d %s has been removed", error_prefix, i, Py_TYPE(item)->tp_name); goto err_cleanup; } /* trick so we can ensure all items have the same mesh, * and allows us to pass the 'bm' as NULL. */ else if (do_bm_check && (bm && bm != item->bm)) { PyErr_Format(PyExc_ValueError, "%s: %d %s is from another mesh", error_prefix, i, BPy_BMElem_StringFromHType(htype)); goto err_cleanup; } if (bm == NULL) { bm = item->bm; } alloc[i] = item->ele; if (do_unique_check) { BM_elem_flag_enable(item->ele, BM_ELEM_INTERNAL_TAG); i_last_dirty = i; } } if (do_unique_check) { /* check for double verts! */ bool ok = true; for (i = 0; i < seq_len; i++) { if (UNLIKELY(BM_elem_flag_test(alloc[i], BM_ELEM_INTERNAL_TAG) == false)) { ok = false; } /* ensure we don't leave this enabled */ BM_elem_flag_disable(alloc[i], BM_ELEM_INTERNAL_TAG); } if (ok == false) { /* Cleared above. */ i_last_dirty = PY_SSIZE_T_MAX; PyErr_Format(PyExc_ValueError, "%s: found the same %.200s used multiple times", error_prefix, BPy_BMElem_StringFromHType(htype)); goto err_cleanup; } } *r_size = seq_len; if (r_bm) { *r_bm = bm; } return alloc; err_cleanup: if (do_unique_check && (i_last_dirty != PY_SSIZE_T_MAX)) { for (i = 0; i <= i_last_dirty; i++) { BM_elem_flag_disable(alloc[i], BM_ELEM_INTERNAL_TAG); } } PyMem_FREE(alloc); return NULL; } void *BPy_BMElem_PySeq_As_Array(BMesh **r_bm, PyObject *seq, Py_ssize_t min, Py_ssize_t max, Py_ssize_t *r_size, const char htype, const bool do_unique_check, const bool do_bm_check, const char *error_prefix) { PyObject *seq_fast; PyObject *ret; if (!(seq_fast = PySequence_Fast(seq, error_prefix))) { return NULL; } ret = BPy_BMElem_PySeq_As_Array_FAST( r_bm, seq_fast, min, max, r_size, htype, do_unique_check, do_bm_check, error_prefix); Py_DECREF(seq_fast); return ret; } PyObject *BPy_BMElem_Array_As_Tuple(BMesh *bm, BMHeader **elem, Py_ssize_t elem_len) { Py_ssize_t i; PyObject *ret = PyTuple_New(elem_len); for (i = 0; i < elem_len; i++) { PyTuple_SET_ITEM(ret, i, BPy_BMElem_CreatePyObject(bm, elem[i])); } return ret; } PyObject *BPy_BMVert_Array_As_Tuple(BMesh *bm, BMVert **elem, Py_ssize_t elem_len) { Py_ssize_t i; PyObject *ret = PyTuple_New(elem_len); for (i = 0; i < elem_len; i++) { PyTuple_SET_ITEM(ret, i, BPy_BMVert_CreatePyObject(bm, elem[i])); } return ret; } PyObject *BPy_BMEdge_Array_As_Tuple(BMesh *bm, BMEdge **elem, Py_ssize_t elem_len) { Py_ssize_t i; PyObject *ret = PyTuple_New(elem_len); for (i = 0; i < elem_len; i++) { PyTuple_SET_ITEM(ret, i, BPy_BMEdge_CreatePyObject(bm, elem[i])); } return ret; } PyObject *BPy_BMFace_Array_As_Tuple(BMesh *bm, BMFace **elem, Py_ssize_t elem_len) { Py_ssize_t i; PyObject *ret = PyTuple_New(elem_len); for (i = 0; i < elem_len; i++) { PyTuple_SET_ITEM(ret, i, BPy_BMFace_CreatePyObject(bm, elem[i])); } return ret; } PyObject *BPy_BMLoop_Array_As_Tuple(BMesh *bm, BMLoop **elem, Py_ssize_t elem_len) { Py_ssize_t i; PyObject *ret = PyTuple_New(elem_len); for (i = 0; i < elem_len; i++) { PyTuple_SET_ITEM(ret, i, BPy_BMLoop_CreatePyObject(bm, elem[i])); } return ret; } int BPy_BMElem_CheckHType(PyTypeObject *type, const char htype) { return (((htype & BM_VERT) && (type == &BPy_BMVert_Type)) || ((htype & BM_EDGE) && (type == &BPy_BMEdge_Type)) || ((htype & BM_FACE) && (type == &BPy_BMFace_Type)) || ((htype & BM_LOOP) && (type == &BPy_BMLoop_Type))); } char *BPy_BMElem_StringFromHType_ex(const char htype, char ret[32]) { /* zero to ensure string is always NULL terminated */ char *ret_ptr = ret; if (htype & BM_VERT) { ret_ptr += sprintf(ret_ptr, "/%s", BPy_BMVert_Type.tp_name); } if (htype & BM_EDGE) { ret_ptr += sprintf(ret_ptr, "/%s", BPy_BMEdge_Type.tp_name); } if (htype & BM_FACE) { ret_ptr += sprintf(ret_ptr, "/%s", BPy_BMFace_Type.tp_name); } if (htype & BM_LOOP) { ret_ptr += sprintf(ret_ptr, "/%s", BPy_BMLoop_Type.tp_name); } ret[0] = '('; *ret_ptr++ = ')'; *ret_ptr = '\0'; return ret; } char *BPy_BMElem_StringFromHType(const char htype) { /* zero to ensure string is always NULL terminated */ static char ret[32]; return BPy_BMElem_StringFromHType_ex(htype, ret); } /* -------------------------------------------------------------------- */ /* keep at bottom */ /* This function is called on free, it should stay quite fast */ static void bm_dealloc_editmode_warn(BPy_BMesh *self) { if (self->flag & BPY_BMFLAG_IS_WRAPPED) { /* Currently NOP - this works without warnings now. */ } }