bmesh python api additions:

- BMesh.is_wrapped
- BMesh.copy()
- BMesh.clear()
- BMesh.free()
- BMesh.from_object(obj, apply_modifiers=True)
- BMEdge.calc_length()
- BMLoop.calc_normal()
- BMLoop.calc_tangent()
This commit is contained in:
2012-03-11 05:58:22 +00:00
parent 050439fd9d
commit 7fa7e4ba1f
6 changed files with 276 additions and 33 deletions

View File

@@ -57,14 +57,11 @@ PyDoc_STRVAR(bpy_bm_new_doc,
static PyObject *bpy_bm_new(PyObject *UNUSED(self))
{
BPy_BMesh *py_bmesh;
BMesh *bm;
bm = BM_mesh_create(NULL, &bm_mesh_allocsize_default);
py_bmesh = (BPy_BMesh *)BPy_BMesh_CreatePyObject(bm);
py_bmesh->py_owns = TRUE;
return (PyObject *)py_bmesh;
return BPy_BMesh_CreatePyObject(bm, BPY_BMFLAG_NOP);
}
PyDoc_STRVAR(bpy_bm_from_edit_mesh_doc,
@@ -77,7 +74,6 @@ PyDoc_STRVAR(bpy_bm_from_edit_mesh_doc,
);
static PyObject *bpy_bm_from_edit_mesh(PyObject *UNUSED(self), PyObject *value)
{
BPy_BMesh *py_bmesh;
BMesh *bm;
Mesh *me = PyC_RNA_AsPointer(value, "Mesh");
@@ -93,9 +89,7 @@ static PyObject *bpy_bm_from_edit_mesh(PyObject *UNUSED(self), PyObject *value)
bm = me->edit_btmesh->bm;
py_bmesh = (BPy_BMesh *)BPy_BMesh_CreatePyObject(bm);
py_bmesh->py_owns = FALSE;
return (PyObject *)py_bmesh;
return BPy_BMesh_CreatePyObject(bm, BPY_BMFLAG_IS_WRAPPED);
}
static struct PyMethodDef BPy_BM_methods[] = {

View File

@@ -36,6 +36,7 @@
#include "BKE_depsgraph.h"
#include "BKE_customdata.h"
#include "BKE_DerivedMesh.h"
#include "bmesh.h"
@@ -252,6 +253,15 @@ 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"
@@ -484,7 +494,8 @@ static PyGetSetDef bpy_bmesh_getseters[] = {
{(char *)"select_history", (getter)bpy_bmesh_select_history_get, (setter)bpy_bmesh_select_history_set, (char *)bpy_bmesh_select_history_doc, NULL},
/* readonly checks */
{(char *)"is_valid", (getter)bpy_bm_is_valid_get, (setter)NULL, (char *)bpy_bm_is_valid_doc, NULL},
{(char *)"is_wrapped", (getter)bpy_bmesh_is_wrapped_get, (setter)NULL, (char *)bpy_bmesh_is_wrapped_doc, NULL}, /* as with mathutils */
{(char *)"is_valid", (getter)bpy_bm_is_valid_get, (setter)NULL, (char *)bpy_bm_is_valid_doc, NULL},
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
};
@@ -590,6 +601,76 @@ static PyGetSetDef bpy_bmloop_getseters[] = {
/* 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);
}
else {
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;
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"
@@ -630,6 +711,49 @@ static PyObject *bpy_bmesh_to_mesh(BPy_BMesh *self, PyObject *args)
Py_RETURN_NONE;
}
/* note: rna_Object_to_mesh() also has apply_modifiers arg that works the same way */
PyDoc_STRVAR(bpy_bmesh_from_object_doc,
".. method:: from_object(mesh, apply_modifiers=True)\n"
"\n"
" Initialize this bmesh from existing object datablock.\n"
"\n"
" :arg object: The object data to load.\n"
" :type object: :class:`Object`\n"
" :arg apply_modifiers: Use the final display mesh rather then the deformed cage.\n"
" :type apply_modifiers: boolean\n"
);
static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args)
{
PyObject *py_object;
Object *ob;
BMesh *bm;
int apply_modifiers = TRUE;
DerivedMesh *dm;
BPY_BM_CHECK_OBJ(self);
if (!PyArg_ParseTuple(args, "O|i:from_object", &py_object, &apply_modifiers) ||
!(ob = PyC_RNA_AsPointer(py_object, "Object")))
{
return NULL;
}
dm = apply_modifiers ? ob->derivedFinal : ob->derivedDeform;
if (dm == NULL) {
PyErr_Format(PyExc_ValueError,
"from_object(...): Object '%s' has no usable mesh data", ob->id.name + 2);
return NULL;
}
bm = self->bm;
DM_to_bmesh_ex(dm, bm);
Py_RETURN_NONE;
}
PyDoc_STRVAR(bpy_bmesh_from_mesh_doc,
".. method:: from_mesh(mesh, use_shape_key=False, shape_key_index=0)\n"
"\n"
@@ -651,7 +775,7 @@ static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject *
int use_shape_key = FALSE;
int shape_key_index = 0;
if (!PyArg_ParseTupleAndKeywords(args, kw, "O|ii:to_mesh", (char **)kwlist,
if (!PyArg_ParseTupleAndKeywords(args, kw, "O|ii:from_mesh", (char **)kwlist,
&py_mesh, &use_shape_key, &shape_key_index) ||
!(me = PyC_RNA_AsPointer(py_mesh, "Mesh")))
{
@@ -1001,12 +1125,22 @@ static PyObject *bpy_bmvert_normal_update(BPy_BMVert *self)
/* 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()\n"
"\n"
" Return the angle between 2 connected faces.\n"
"\n"
" :return: The angle between both faces in radians.\n"
" :return: The angle between 2 connected faces in radians.\n"
" :rtype: float\n"
);
static PyObject *bpy_bmedge_calc_face_angle(BPy_BMEdge *self)
@@ -1270,21 +1404,54 @@ static PyObject *bpy_bmloop_copy_from_face_interp(BPy_BMLoop *self, PyObject *ar
}
PyDoc_STRVAR(bpy_bmloop_calc_face_angle_doc,
".. method:: calc_face_angle()\n"
PyDoc_STRVAR(bpy_bmloop_calc_angle_doc,
".. method:: calc_angle()\n"
"\n"
" Return angle at this loops corner of the face.\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_face_angle(BPy_BMLoop *self)
static PyObject *bpy_bmloop_calc_angle(BPy_BMLoop *self)
{
BPY_BM_CHECK_OBJ(self);
return PyFloat_FromDouble(BM_loop_face_angle(self->bm, 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 straignt 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_face_normal(self->bm, self->l, vec);
return Vector_CreatePyObject(vec, 3, Py_NEW, 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 straignt 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_face_tangent(self->bm, self->l, vec);
return Vector_CreatePyObject(vec, 3, Py_NEW, NULL);
}
/* Vert Seq
* -------- */
@@ -1796,9 +1963,17 @@ static PyObject *bpy_bmelemseq_index_update(BPy_BMElemSeq *self)
static struct PyMethodDef bpy_bmesh_methods[] = {
{"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},
/* 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_VARARGS, bpy_bmesh_normal_update_doc},
@@ -1827,6 +2002,7 @@ static struct PyMethodDef bpy_bmedge_methods[] = {
{"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_NOARGS, bpy_bmedge_calc_face_angle_doc},
{"normal_update", (PyCFunction)bpy_bmedge_normal_update, METH_NOARGS, bpy_bmedge_normal_update_doc},
@@ -1856,7 +2032,9 @@ 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_face_angle, METH_NOARGS, bpy_bmloop_calc_face_angle_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}
};
@@ -2143,10 +2321,10 @@ static void bpy_bmesh_dealloc(BPy_BMesh *self)
BM_data_layer_free(bm, &bm->ldata, CD_BM_ELEM_PYPTR);
bm->py_handle = NULL;
}
if (self->py_owns) {
BM_mesh_free(bm);
if ((self->flag & BPY_BMFLAG_IS_WRAPPED) == 0) {
BM_mesh_free(bm);
}
}
PyObject_DEL(self);
@@ -2476,7 +2654,7 @@ PyObject *BPyInit_bmesh_types(void)
/* Utility Functions
* ***************** */
PyObject *BPy_BMesh_CreatePyObject(BMesh *bm)
PyObject *BPy_BMesh_CreatePyObject(BMesh *bm, int flag)
{
BPy_BMesh *self;
@@ -2487,6 +2665,8 @@ PyObject *BPy_BMesh_CreatePyObject(BMesh *bm)
else {
self = PyObject_New(BPy_BMesh, &BPy_BMesh_Type);
self->bm = bm;
self->flag = flag;
bm->py_handle = self; /* point back */
BM_data_layer_add(bm, &bm->vdata, CD_BM_ELEM_PYPTR);

View File

@@ -62,7 +62,7 @@ typedef struct BPy_BMElem {
typedef struct BPy_BMesh {
PyObject_VAR_HEAD
struct BMesh *bm; /* keep first */
char py_owns; /* when set, free along with the PyObject */
int flag;
} BPy_BMesh;
/* element types */
@@ -120,7 +120,12 @@ void BPy_BM_init_types(void);
PyObject *BPyInit_bmesh_types(void);
PyObject *BPy_BMesh_CreatePyObject(BMesh *bm);
enum {
BPY_BMFLAG_NOP = 0, /* do nothing */
BPY_BMFLAG_IS_WRAPPED = 1 /* the mesh is owned by editmode */
};
PyObject *BPy_BMesh_CreatePyObject(BMesh *bm, int flag);
PyObject *BPy_BMVert_CreatePyObject(BMesh *bm, BMVert *v);
PyObject *BPy_BMEdge_CreatePyObject(BMesh *bm, BMEdge *e);
PyObject *BPy_BMFace_CreatePyObject(BMesh *bm, BMFace *f);