2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2012 Blender Foundation.
19 * All rights reserved.
21 * Contributor(s): Campbell Barton
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/python/bmesh/bmesh_py_types_meshdata.c
29 * This file defines customdata types which can't be accessed as primitive
30 * python types such as MDeformVert, MLoopUV, MTexPoly
35 #include "../mathutils/mathutils.h"
37 #include "DNA_object_types.h"
38 #include "DNA_meshdata_types.h"
40 #include "BLI_utildefines.h"
41 #include "BLI_math_vector.h"
43 #include "BKE_deform.h"
44 #include "BKE_library.h"
46 #include "bmesh_py_types_meshdata.h"
52 #define BPy_BMTexPoly_Check(v) (Py_TYPE(v) == &BPy_BMTexPoly_Type)
54 typedef struct BPy_BMTexPoly {
59 extern PyObject *pyrna_id_CreatePyObject(ID *id);
60 extern int pyrna_id_FromPyObject(PyObject *obj, ID **id);
62 PyDoc_STRVAR(bpy_bmtexpoly_image_doc,
63 "Image or None.\n\n:type: :class:`bpy.types.Image`"
65 static PyObject *bpy_bmtexpoly_image_get(BPy_BMTexPoly *self, void *UNUSED(closure))
67 return pyrna_id_CreatePyObject((ID *)self->data->tpage);
70 static int bpy_bmtexpoly_image_set(BPy_BMTexPoly *self, PyObject *value, void *UNUSED(closure))
74 if (value == Py_None) {
77 else if (pyrna_id_FromPyObject(value, &id) && id && GS(id->name) == ID_IM) {
81 PyErr_Format(PyExc_KeyError, "BMTexPoly.image = x"
82 "expected an image or None, not '%.200s'",
83 Py_TYPE(value)->tp_name);
88 self->data->tpage = (struct Image *)id;
93 static PyGetSetDef bpy_bmtexpoly_getseters[] = {
94 /* attributes match rna_def_mtpoly */
95 {(char *)"image", (getter)bpy_bmtexpoly_image_get, (setter)bpy_bmtexpoly_image_set, (char *)bpy_bmtexpoly_image_doc, NULL},
97 {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
100 PyTypeObject BPy_BMTexPoly_Type = {{{0}}}; /* bm.loops.layers.uv.active */
102 static void bm_init_types_bmtexpoly(void)
104 BPy_BMTexPoly_Type.tp_basicsize = sizeof(BPy_BMTexPoly);
106 BPy_BMTexPoly_Type.tp_name = "BMTexPoly";
108 BPy_BMTexPoly_Type.tp_doc = NULL; // todo
110 BPy_BMTexPoly_Type.tp_getset = bpy_bmtexpoly_getseters;
112 BPy_BMTexPoly_Type.tp_flags = Py_TPFLAGS_DEFAULT;
114 PyType_Ready(&BPy_BMTexPoly_Type);
117 int BPy_BMTexPoly_AssignPyObject(struct MTexPoly *mtpoly, PyObject *value)
119 if (UNLIKELY(!BPy_BMTexPoly_Check(value))) {
120 PyErr_Format(PyExc_TypeError, "expected BMTexPoly, not a %.200s", Py_TYPE(value)->tp_name);
124 *((MTexPoly *)mtpoly) = *(((BPy_BMTexPoly *)value)->data);
129 PyObject *BPy_BMTexPoly_CreatePyObject(struct MTexPoly *mtpoly)
131 BPy_BMTexPoly *self = PyObject_New(BPy_BMTexPoly, &BPy_BMTexPoly_Type);
133 return (PyObject *)self;
136 /* --- End Mesh BMTexPoly --- */
141 #define BPy_BMLoopUV_Check(v) (Py_TYPE(v) == &BPy_BMLoopUV_Type)
143 typedef struct BPy_BMLoopUV {
148 PyDoc_STRVAR(bpy_bmloopuv_uv_doc,
149 "Loops UV (as a 2D Vector).\n\n:type: :class:`mathutils.Vector`"
151 static PyObject *bpy_bmloopuv_uv_get(BPy_BMLoopUV *self, void *UNUSED(closure))
153 return Vector_CreatePyObject(self->data->uv, 2, Py_WRAP, NULL);
156 static int bpy_bmloopuv_uv_set(BPy_BMLoopUV *self, PyObject *value, void *UNUSED(closure))
159 if (mathutils_array_parse(tvec, 2, 2, value, "BMLoopUV.uv") != -1) {
160 copy_v2_v2(self->data->uv, tvec);
168 PyDoc_STRVAR(bpy_bmloopuv_flag__pin_uv_doc,
169 "UV pin state.\n\n:type: boolean"
171 PyDoc_STRVAR(bpy_bmloopuv_flag__select_doc,
172 "UV select state.\n\n:type: boolean"
174 PyDoc_STRVAR(bpy_bmloopuv_flag__select_edge_doc,
175 "UV edge select state.\n\n:type: boolean"
179 static PyObject *bpy_bmloopuv_flag_get(BPy_BMLoopUV *self, void *flag_p)
181 const int flag = GET_INT_FROM_POINTER(flag_p);
182 return PyBool_FromLong(self->data->flag & flag);
185 static int bpy_bmloopuv_flag_set(BPy_BMLoopUV *self, PyObject *value, void *flag_p)
187 const int flag = GET_INT_FROM_POINTER(flag_p);
189 switch (PyLong_AsLong(value)) {
191 self->data->flag |= flag;
194 self->data->flag &= ~flag;
197 PyErr_SetString(PyExc_TypeError,
198 "expected a boolean type 0/1");
203 static PyGetSetDef bpy_bmloopuv_getseters[] = {
204 /* attributes match rna_def_mloopuv */
205 {(char *)"uv", (getter)bpy_bmloopuv_uv_get, (setter)bpy_bmloopuv_uv_set, (char *)bpy_bmloopuv_uv_doc, NULL},
206 {(char *)"pin_uv", (getter)bpy_bmloopuv_flag_get, (setter)bpy_bmloopuv_flag_set, (char *)bpy_bmloopuv_flag__pin_uv_doc, (void *)MLOOPUV_PINNED},
207 {(char *)"select", (getter)bpy_bmloopuv_flag_get, (setter)bpy_bmloopuv_flag_set, (char *)bpy_bmloopuv_flag__select_doc, (void *)MLOOPUV_VERTSEL},
208 {(char *)"select_edge", (getter)bpy_bmloopuv_flag_get, (setter)bpy_bmloopuv_flag_set, (char *)bpy_bmloopuv_flag__select_edge_doc, (void *)MLOOPUV_EDGESEL},
210 {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
213 PyTypeObject BPy_BMLoopUV_Type = {{{0}}}; /* bm.loops.layers.uv.active */
215 static void bm_init_types_bmloopuv(void)
217 BPy_BMLoopUV_Type.tp_basicsize = sizeof(BPy_BMLoopUV);
219 BPy_BMLoopUV_Type.tp_name = "BMLoopUV";
221 BPy_BMLoopUV_Type.tp_doc = NULL; // todo
223 BPy_BMLoopUV_Type.tp_getset = bpy_bmloopuv_getseters;
225 BPy_BMLoopUV_Type.tp_flags = Py_TPFLAGS_DEFAULT;
227 PyType_Ready(&BPy_BMLoopUV_Type);
230 int BPy_BMLoopUV_AssignPyObject(struct MLoopUV *mloopuv, PyObject *value)
232 if (UNLIKELY(!BPy_BMLoopUV_Check(value))) {
233 PyErr_Format(PyExc_TypeError, "expected BMLoopUV, not a %.200s", Py_TYPE(value)->tp_name);
237 *((MLoopUV *)mloopuv) = *(((BPy_BMLoopUV *)value)->data);
242 PyObject *BPy_BMLoopUV_CreatePyObject(struct MLoopUV *mloopuv)
244 BPy_BMLoopUV *self = PyObject_New(BPy_BMLoopUV, &BPy_BMLoopUV_Type);
245 self->data = mloopuv;
246 return (PyObject *)self;
249 /* --- End Mesh Loop UV --- */
254 /* This simply provices a color wrapper for
255 * color which uses mathutils callbacks for mathutils.Color
258 #define MLOOPCOL_FROM_CAPSULE(color_capsule) \
259 ((MLoopCol *)PyCapsule_GetPointer(color_capsule, NULL))
261 static void mloopcol_to_float(const MLoopCol *mloopcol, float col_r[3])
263 rgb_uchar_to_float(col_r, (unsigned char *)&mloopcol->r);
266 static void mloopcol_from_float(MLoopCol *mloopcol, const float col[3])
268 rgb_float_to_uchar((unsigned char *)&mloopcol->r, col);
271 static unsigned char mathutils_bmloopcol_cb_index = -1;
273 static int mathutils_bmloopcol_check(BaseMathObject *UNUSED(bmo))
279 static int mathutils_bmloopcol_get(BaseMathObject *bmo, int UNUSED(subtype))
281 MLoopCol *mloopcol = MLOOPCOL_FROM_CAPSULE(bmo->cb_user);
282 mloopcol_to_float(mloopcol, bmo->data);
286 static int mathutils_bmloopcol_set(BaseMathObject *bmo, int UNUSED(subtype))
288 MLoopCol *mloopcol = MLOOPCOL_FROM_CAPSULE(bmo->cb_user);
289 mloopcol_from_float(mloopcol, bmo->data);
293 static int mathutils_bmloopcol_get_index(BaseMathObject *bmo, int subtype, int UNUSED(index))
295 /* lazy, avoid repeteing the case statement */
296 if (mathutils_bmloopcol_get(bmo, subtype) == -1)
301 static int mathutils_bmloopcol_set_index(BaseMathObject *bmo, int subtype, int index)
303 const float f = bmo->data[index];
305 /* lazy, avoid repeteing the case statement */
306 if (mathutils_bmloopcol_get(bmo, subtype) == -1)
309 bmo->data[index] = f;
310 return mathutils_bmloopcol_set(bmo, subtype);
313 Mathutils_Callback mathutils_bmloopcol_cb = {
314 mathutils_bmloopcol_check,
315 mathutils_bmloopcol_get,
316 mathutils_bmloopcol_set,
317 mathutils_bmloopcol_get_index,
318 mathutils_bmloopcol_set_index
321 static void bm_init_types_bmloopcol(void)
324 mathutils_bmloopcol_cb_index = Mathutils_RegisterCallback(&mathutils_bmloopcol_cb);
327 int BPy_BMLoopColor_AssignPyObject(struct MLoopCol *mloopcol, PyObject *value)
330 if (mathutils_array_parse(tvec, 3, 3, value, "BMLoopCol") != -1) {
331 mloopcol_from_float(mloopcol, tvec);
339 PyObject *BPy_BMLoopColor_CreatePyObject(struct MLoopCol *data)
341 PyObject *color_capsule;
342 color_capsule = PyCapsule_New(data, NULL, NULL);
343 return Color_CreatePyObject_cb(color_capsule, mathutils_bmloopcol_cb_index, 0);
346 #undef MLOOPCOL_FROM_CAPSULE
348 /* --- End Mesh Loop Color --- */
352 * **************** */
355 * This is python type wraps a deform vert as a python dictionary,
356 * hiding the #MDeformWeight on access, since the mapping is very close, eg:
359 * weight = defvert_find_weight(dv, group_nr);
360 * defvert_remove_group(dv, dw)
363 * weight = dv[group_nr]
366 * \note: there is nothing BMesh specific here,
367 * its only that BMesh is the only part of blender that uses a hand written api like this.
368 * This type could eventually be used to access lattice weights.
370 * \note: Many of blender-api's dict-like-wrappers act like ordered dicts,
371 * This is intentional _not_ ordered, the weights can be in any order and it wont matter,
372 * the order should not be used in the api in any meaningful way (as with a python dict)
373 * only expose as mapping, not a sequence.
376 #define BPy_BMDeformVert_Check(v) (Py_TYPE(v) == &BPy_BMDeformVert_Type)
378 typedef struct BPy_BMDeformVert {
385 * ================= */
387 static int bpy_bmdeformvert_len(BPy_BMDeformVert *self)
389 return self->data->totweight;
392 static PyObject *bpy_bmdeformvert_subscript(BPy_BMDeformVert *self, PyObject *key)
394 if (PyIndex_Check(key)) {
396 i = PyNumber_AsSsize_t(key, PyExc_IndexError);
397 if (i == -1 && PyErr_Occurred()) {
401 MDeformWeight *dw = defvert_find_index(self->data, i);
404 PyErr_SetString(PyExc_KeyError, "BMDeformVert[key] = x: "
409 return PyFloat_FromDouble(dw->weight);
414 PyErr_Format(PyExc_TypeError,
415 "BMDeformVert keys must be integers, not %.200s",
416 Py_TYPE(key)->tp_name);
421 static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key, PyObject *value)
423 if (PyIndex_Check(key)) {
426 i = PyNumber_AsSsize_t(key, PyExc_IndexError);
427 if (i == -1 && PyErr_Occurred()) {
432 /* dvert[group_index] = 0.5 */
434 PyErr_SetString(PyExc_KeyError, "BMDeformVert[key] = x: "
435 "weight keys can't be negative");
439 MDeformWeight *dw = defvert_verify_index(self->data, i);
440 const float f = PyFloat_AsDouble(value);
441 if (f == -1 && PyErr_Occurred()) { // parsed key not a number
442 PyErr_SetString(PyExc_TypeError,
443 "BMDeformVert[key] = x: "
444 "argument not a number");
448 dw->weight = CLAMPIS(f, 0.0f, 1.0f);
452 /* del dvert[group_index] */
453 MDeformWeight *dw = defvert_find_index(self->data, i);
456 PyErr_SetString(PyExc_KeyError, "del BMDeformVert[key]: "
459 defvert_remove_group(self->data, dw);
466 PyErr_Format(PyExc_TypeError,
467 "BMDeformVert keys must be integers, not %.200s",
468 Py_TYPE(key)->tp_name);
473 static int bpy_bmdeformvert_contains(BPy_BMDeformVert *self, PyObject *value)
475 const int key = PyLong_AsSsize_t(value);
477 if (key == -1 && PyErr_Occurred()) {
478 PyErr_SetString(PyExc_TypeError,
479 "BMDeformVert.__contains__: expected an int");
483 return (defvert_find_index(self->data, key) != NULL) ? 1 : 0;
486 /* only defined for __contains__ */
487 static PySequenceMethods bpy_bmdeformvert_as_sequence = {
488 (lenfunc)bpy_bmdeformvert_len, /* sq_length */
489 NULL, /* sq_concat */
490 NULL, /* sq_repeat */
492 /* note: if this is set PySequence_Check() returns True,
493 * but in this case we dont want to be treated as a seq */
497 NULL, /* sq_ass_item */
498 NULL, /* *was* sq_ass_slice */
499 (objobjproc)bpy_bmdeformvert_contains, /* sq_contains */
500 (binaryfunc) NULL, /* sq_inplace_concat */
501 (ssizeargfunc) NULL, /* sq_inplace_repeat */
504 static PyMappingMethods bpy_bmdeformvert_as_mapping = {
505 (lenfunc)bpy_bmdeformvert_len,
506 (binaryfunc)bpy_bmdeformvert_subscript,
507 (objobjargproc)bpy_bmdeformvert_ass_subscript
513 PyDoc_STRVAR(bpy_bmdeformvert_keys_doc,
514 ".. method:: keys()\n"
516 " Return the group indices used by this vertex\n"
517 " (matching pythons dict.keys() functionality).\n"
519 " :return: the deform group this vertex uses\n"
520 " :rtype: list of ints\n"
522 static PyObject *bpy_bmdeformvert_keys(BPy_BMDeformVert *self)
526 MDeformWeight *dw = self->data->dw;
528 ret = PyList_New(self->data->totweight);
529 for (i = 0; i < self->data->totweight; i++, dw++) {
530 PyList_SET_ITEM(ret, i, PyLong_FromSsize_t(dw->def_nr));
536 PyDoc_STRVAR(bpy_bmdeformvert_values_doc,
537 ".. method:: items()\n"
539 " Return (group, weight) pairs for this vertex\n"
540 " (matching pythons dict.items() functionality).\n"
542 " :return: (key, value) pairs for each deform weight of this vertex.\n"
543 " :rtype: list of tuples\n"
545 static PyObject *bpy_bmdeformvert_values(BPy_BMDeformVert *self)
549 MDeformWeight *dw = self->data->dw;
551 ret = PyList_New(self->data->totweight);
552 for (i = 0; i < self->data->totweight; i++, dw++) {
553 PyList_SET_ITEM(ret, i, PyFloat_FromDouble(dw->weight));
559 PyDoc_STRVAR(bpy_bmdeformvert_items_doc,
560 ".. method:: values()\n"
562 " Return the weights of the deform vertex\n"
563 " (matching pythons dict.values() functionality).\n"
565 " :return: The weights that influence this vertex\n"
566 " :rtype: list of floats\n"
568 static PyObject *bpy_bmdeformvert_items(BPy_BMDeformVert *self)
573 MDeformWeight *dw = self->data->dw;
575 ret = PyList_New(self->data->totweight);
576 for (i = 0; i < self->data->totweight; i++, dw++) {
577 item = PyTuple_New(2);
579 PyTuple_SET_ITEM(item, 0, PyLong_FromSsize_t(dw->def_nr));
580 PyTuple_SET_ITEM(item, 1, PyFloat_FromDouble(dw->weight));
582 PyList_SET_ITEM(ret, i, item);
588 PyDoc_STRVAR(bpy_bmdeformvert_get_doc,
589 ".. method:: get(key, default=None)\n"
591 " Returns the deform weight matching the key or default\n"
592 " when not found (matches pythons dictionary function of the same name).\n"
594 " :arg key: The key associated with deform weight.\n"
596 " :arg default: Optional argument for the value to return if\n"
597 " *key* is not found.\n"
598 " :type default: Undefined\n"
600 static PyObject *bpy_bmdeformvert_get(BPy_BMDeformVert *self, PyObject *args)
603 PyObject *def = Py_None;
605 if (!PyArg_ParseTuple(args, "i|O:get", &key, &def)) {
609 MDeformWeight *dw = defvert_find_index(self->data, key);
612 return PyFloat_FromDouble(dw->weight);
615 return Py_INCREF(def), def;
621 PyDoc_STRVAR(bpy_bmdeformvert_clear_doc,
622 ".. method:: clear()\n"
624 " Clears all weights.\n"
626 static PyObject *bpy_bmdeformvert_clear(BPy_BMDeformVert *self)
628 defvert_clear(self->data);
633 static struct PyMethodDef bpy_bmdeformvert_methods[] = {
634 {"keys", (PyCFunction)bpy_bmdeformvert_keys, METH_NOARGS, bpy_bmdeformvert_keys_doc},
635 {"values", (PyCFunction)bpy_bmdeformvert_values, METH_NOARGS, bpy_bmdeformvert_values_doc},
636 {"items", (PyCFunction)bpy_bmdeformvert_items, METH_NOARGS, bpy_bmdeformvert_items_doc},
637 {"get", (PyCFunction)bpy_bmdeformvert_get, METH_VARARGS, bpy_bmdeformvert_get_doc},
638 /* BMESH_TODO pop, popitem, update */
639 {"clear", (PyCFunction)bpy_bmdeformvert_clear, METH_NOARGS, bpy_bmdeformvert_clear_doc},
640 {NULL, NULL, 0, NULL}
643 PyTypeObject BPy_BMDeformVert_Type = {{{0}}}; /* bm.loops.layers.uv.active */
645 static void bm_init_types_bmdvert(void)
647 BPy_BMDeformVert_Type.tp_basicsize = sizeof(BPy_BMDeformVert);
649 BPy_BMDeformVert_Type.tp_name = "BMDeformVert";
651 BPy_BMDeformVert_Type.tp_doc = NULL; // todo
653 BPy_BMDeformVert_Type.tp_as_sequence = &bpy_bmdeformvert_as_sequence;
654 BPy_BMDeformVert_Type.tp_as_mapping = &bpy_bmdeformvert_as_mapping;
656 BPy_BMDeformVert_Type.tp_methods = bpy_bmdeformvert_methods;
658 BPy_BMDeformVert_Type.tp_flags = Py_TPFLAGS_DEFAULT;
660 PyType_Ready(&BPy_BMDeformVert_Type);
663 int BPy_BMDeformVert_AssignPyObject(struct MDeformVert *dvert, PyObject *value)
665 if (UNLIKELY(!BPy_BMDeformVert_Check(value))) {
666 PyErr_Format(PyExc_TypeError, "expected BMDeformVert, not a %.200s", Py_TYPE(value)->tp_name);
670 MDeformVert *dvert_src = ((BPy_BMDeformVert *)value)->data;
671 if (LIKELY(dvert != dvert_src)) {
672 defvert_copy(dvert, dvert_src);
678 PyObject *BPy_BMDeformVert_CreatePyObject(struct MDeformVert *dvert)
680 BPy_BMDeformVert *self = PyObject_New(BPy_BMDeformVert, &BPy_BMDeformVert_Type);
682 return (PyObject *)self;
685 /* --- End Mesh Deform Vert --- */
688 /* call to init all types */
689 void BPy_BM_init_types_meshdata(void)
691 bm_init_types_bmtexpoly();
692 bm_init_types_bmloopuv();
693 bm_init_types_bmloopcol();
694 bm_init_types_bmdvert();