bmesh py api:
[blender.git] / source / blender / python / bmesh / bmesh_py_types_meshdata.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
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.
8  *
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.
13  *
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.
17  *
18  * The Original Code is Copyright (C) 2012 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Campbell Barton
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/python/bmesh/bmesh_py_types_meshdata.c
27  *  \ingroup pybmesh
28  *
29  * This file defines customdata types which can't be accessed as primitive
30  * python types such as MDeformVert, MLoopUV, MTexPoly
31  */
32
33 #include <Python.h>
34
35 #include "../mathutils/mathutils.h"
36
37 #include "DNA_meshdata_types.h"
38
39 #include "BLI_utildefines.h"
40 #include "BLI_math_vector.h"
41
42 #include "BKE_deform.h"
43
44 #include "bmesh_py_types_meshdata.h"
45
46 /* Mesh Loop UV
47  * ************ */
48
49 #define BPy_BMLoopUV_Check(v)  (Py_TYPE(v) == &BPy_BMLoopUV_Type)
50
51 typedef struct BPy_BMLoopUV {
52         PyObject_VAR_HEAD
53         MLoopUV *data;
54 } BPy_BMLoopUV;
55
56 PyDoc_STRVAR(bpy_bmloopuv_uv_doc,
57 "Loops UV (as a 2D Vector).\n\n:type: :class:`mathutils.Vector`"
58 );
59 static PyObject *bpy_bmloopuv_uv_get(BPy_BMLoopUV *self, void *UNUSED(closure))
60 {
61         return Vector_CreatePyObject(self->data->uv, 2, Py_WRAP, NULL);
62 }
63
64 static int bpy_bmloopuv_uv_set(BPy_BMLoopUV *self, PyObject *value, void *UNUSED(closure))
65 {
66         float tvec[2];
67         if (mathutils_array_parse(tvec, 2, 2, value, "BMLoopUV.uv") != -1) {
68                 copy_v2_v2(self->data->uv, tvec);
69                 return 0;
70         }
71         else {
72                 return -1;
73         }
74 }
75
76 PyDoc_STRVAR(bpy_bmloopuv_flag__pin_uv_doc,
77 "UV pin state.\n\n:type: boolean"
78 );
79 PyDoc_STRVAR(bpy_bmloopuv_flag__select_doc,
80 "UV select state.\n\n:type: boolean"
81 );
82 PyDoc_STRVAR(bpy_bmloopuv_flag__select_edge_doc,
83 "UV edge select state.\n\n:type: boolean"
84 );
85
86
87 static PyObject *bpy_bmloopuv_flag_get(BPy_BMLoopUV *self, void *flag_p)
88 {
89         const int flag = GET_INT_FROM_POINTER(flag_p);
90         return PyBool_FromLong(self->data->flag & flag);
91 }
92
93 static int bpy_bmloopuv_flag_set(BPy_BMLoopUV *self, PyObject *value, void *flag_p)
94 {
95         const int flag = GET_INT_FROM_POINTER(flag_p);
96
97         switch (PyLong_AsLong(value)) {
98                 case TRUE:
99                         self->data->flag |= flag;
100                         return 0;
101                 case FALSE:
102                         self->data->flag &= ~flag;
103                         return 0;
104                 default:
105                         PyErr_SetString(PyExc_TypeError,
106                                         "expected a boolean type 0/1");
107                         return -1;
108         }
109 }
110
111 static PyGetSetDef bpy_bmloopuv_getseters[] = {
112     /* attributes match rna_def_mloopuv  */
113     {(char *)"uv",          (getter)bpy_bmloopuv_uv_get,   (setter)bpy_bmloopuv_uv_set,   (char *)bpy_bmloopuv_uv_doc, NULL},
114     {(char *)"pin_uv",      (getter)bpy_bmloopuv_flag_get, (setter)bpy_bmloopuv_flag_set, (char *)bpy_bmloopuv_flag__pin_uv_doc, (void *)MLOOPUV_PINNED},
115     {(char *)"select",      (getter)bpy_bmloopuv_flag_get, (setter)bpy_bmloopuv_flag_set, (char *)bpy_bmloopuv_flag__select_doc, (void *)MLOOPUV_VERTSEL},
116     {(char *)"select_edge", (getter)bpy_bmloopuv_flag_get, (setter)bpy_bmloopuv_flag_set, (char *)bpy_bmloopuv_flag__select_edge_doc, (void *)MLOOPUV_EDGESEL},
117
118     {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
119 };
120
121 PyTypeObject BPy_BMLoopUV_Type = {{{0}}}; /* bm.loops.layers.uv.active */
122
123 static void bm_init_types_bmloopuv(void)
124 {
125         BPy_BMLoopUV_Type.tp_basicsize = sizeof(BPy_BMLoopUV);
126
127         BPy_BMLoopUV_Type.tp_name = "BMLoopUV";
128
129         BPy_BMLoopUV_Type.tp_doc = NULL; // todo
130
131         BPy_BMLoopUV_Type.tp_getset = bpy_bmloopuv_getseters;
132
133         BPy_BMLoopUV_Type.tp_flags = Py_TPFLAGS_DEFAULT;
134
135         PyType_Ready(&BPy_BMLoopUV_Type);
136 }
137
138 int BPy_BMLoopUV_AssignPyObject(struct MLoopUV *mloopuv, PyObject *value)
139 {
140         if (UNLIKELY(!BPy_BMLoopUV_Check(value))) {
141                 PyErr_Format(PyExc_TypeError, "expected BMLoopUV, not a %.200s", Py_TYPE(value)->tp_name);
142                 return -1;
143         }
144         else {
145                 *((MLoopUV *)mloopuv) = *(((BPy_BMLoopUV *)value)->data);
146                 return 0;
147         }
148 }
149
150 PyObject *BPy_BMLoopUV_CreatePyObject(struct MLoopUV *mloopuv)
151 {
152         BPy_BMLoopUV *self = PyObject_New(BPy_BMLoopUV, &BPy_BMLoopUV_Type);
153         self->data = mloopuv;
154         return (PyObject *)self;
155 }
156
157 /* --- End Mesh Loop UV --- */
158
159 /* Mesh Loop Color
160  * *************** */
161
162 /* This simply provices a color wrapper for
163  * color which uses mathutils callbacks for mathutils.Color
164  */
165
166 #define MLOOPCOL_FROM_CAPSULE(color_capsule)  \
167         ((MLoopCol *)PyCapsule_GetPointer(color_capsule, NULL))
168
169 static void mloopcol_to_float(const MLoopCol *mloopcol, float col_r[3])
170 {
171         rgb_uchar_to_float(col_r, (unsigned char *)&mloopcol->r);
172 }
173
174 static void mloopcol_from_float(MLoopCol *mloopcol, const float col[3])
175 {
176         rgb_float_to_uchar((unsigned char *)&mloopcol->r, col);
177 }
178
179 static unsigned char mathutils_bmloopcol_cb_index = -1;
180
181 static int mathutils_bmloopcol_check(BaseMathObject *UNUSED(bmo))
182 {
183         /* always ok */
184         return 0;
185 }
186
187 static int mathutils_bmloopcol_get(BaseMathObject *bmo, int UNUSED(subtype))
188 {
189         MLoopCol *mloopcol = MLOOPCOL_FROM_CAPSULE(bmo->cb_user);
190         mloopcol_to_float(mloopcol, bmo->data);
191         return 0;
192 }
193
194 static int mathutils_bmloopcol_set(BaseMathObject *bmo, int UNUSED(subtype))
195 {
196         MLoopCol *mloopcol = MLOOPCOL_FROM_CAPSULE(bmo->cb_user);
197         mloopcol_from_float(mloopcol, bmo->data);
198         return 0;
199 }
200
201 static int mathutils_bmloopcol_get_index(BaseMathObject *bmo, int subtype, int UNUSED(index))
202 {
203         /* lazy, avoid repeteing the case statement */
204         if (mathutils_bmloopcol_get(bmo, subtype) == -1)
205                 return -1;
206         return 0;
207 }
208
209 static int mathutils_bmloopcol_set_index(BaseMathObject *bmo, int subtype, int index)
210 {
211         const float f = bmo->data[index];
212
213         /* lazy, avoid repeteing the case statement */
214         if (mathutils_bmloopcol_get(bmo, subtype) == -1)
215                 return -1;
216
217         bmo->data[index] = f;
218         return mathutils_bmloopcol_set(bmo, subtype);
219 }
220
221 Mathutils_Callback mathutils_bmloopcol_cb = {
222         mathutils_bmloopcol_check,
223         mathutils_bmloopcol_get,
224         mathutils_bmloopcol_set,
225         mathutils_bmloopcol_get_index,
226         mathutils_bmloopcol_set_index
227 };
228
229 static void bm_init_types_bmloopcol(void)
230 {
231         /* pass */
232         mathutils_bmloopcol_cb_index = Mathutils_RegisterCallback(&mathutils_bmloopcol_cb);
233 }
234
235 int BPy_BMLoopColor_AssignPyObject(struct MLoopCol *mloopcol, PyObject *value)
236 {
237         float tvec[3];
238         if (mathutils_array_parse(tvec, 3, 3, value, "BMLoopCol") != -1) {
239                 mloopcol_from_float(mloopcol, tvec);
240                 return 0;
241         }
242         else {
243                 return -1;
244         }
245 }
246
247 PyObject *BPy_BMLoopColor_CreatePyObject(struct MLoopCol *data)
248 {
249         PyObject *color_capsule;
250         color_capsule = PyCapsule_New(data, NULL, NULL);
251         return Color_CreatePyObject_cb(color_capsule, mathutils_bmloopcol_cb_index, 0);
252 }
253
254 #undef MLOOPCOL_FROM_CAPSULE
255
256 /* --- End Mesh Loop Color --- */
257
258
259 /* Mesh Deform Vert
260  * **************** */
261
262 /**
263  * This is python type wraps a deform vert as a python dictionary,
264  * hiding the #MDeformWeight on access, since the mapping is very close, eg:
265  *
266  * C:
267  *     weight = defvert_find_weight(dv, group_nr);
268  *     defvert_remove_group(dv, dw)
269  *
270  * Py:
271  *     weight = dv[group_nr]
272  *     del dv[group_nr]
273  *
274  * \note: there is nothing BMesh spesific here,
275  * its only that BMesh is the only part of blender that uses a hand written api like this.
276  * This type could eventually be used to access lattice weights.
277  *
278  * \note: Many of blender-api's dict-like-wrappers act like ordered dicts,
279  * This is intentional _not_ ordered, the weights can be in any order and it wont matter,
280  * the order should not be used in the api in any meaningful way (as with a python dict)
281  * only expose as mapping, not a sequence.
282  */
283
284 #define BPy_BMDeformVert_Check(v)  (Py_TYPE(v) == &BPy_BMDeformVert_Type)
285
286 typedef struct BPy_BMDeformVert {
287         PyObject_VAR_HEAD
288         MDeformVert *data;
289 } BPy_BMDeformVert;
290
291
292 /* Mapping Protocols
293  * ================= */
294
295 static int bpy_bmdeformvert_len(BPy_BMDeformVert *self)
296 {
297         return self->data->totweight;
298 }
299
300 static PyObject *bpy_bmdeformvert_subscript(BPy_BMDeformVert *self, PyObject *key)
301 {
302         if (PyIndex_Check(key)) {
303                 int i;
304                 i = PyNumber_AsSsize_t(key, PyExc_IndexError);
305                 if (i == -1 && PyErr_Occurred()) {
306                         return NULL;
307                 }
308                 else {
309                         MDeformWeight *dw = defvert_find_index(self->data, i);
310
311                         if (dw == NULL) {
312                                 PyErr_SetString(PyExc_KeyError, "BMDeformVert[key] = x: "
313                                                 "key not found");
314                                 return NULL;
315                         }
316                         else {
317                                 return PyFloat_FromDouble(dw->weight);
318                         }
319                 }
320         }
321         else {
322                 PyErr_Format(PyExc_TypeError,
323                              "BMDeformVert keys must be integers, not %.200s",
324                              Py_TYPE(key)->tp_name);
325                 return NULL;
326         }
327 }
328
329 static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key, PyObject *value)
330 {
331         if (PyIndex_Check(key)) {
332                 int i;
333
334                 i = PyNumber_AsSsize_t(key, PyExc_IndexError);
335                 if (i == -1 && PyErr_Occurred()) {
336                         return -1;
337                 }
338
339                 if (value) {
340                         /* dvert[group_index] = 0.5 */
341                         if (i < 0) {
342                                 PyErr_SetString(PyExc_KeyError, "BMDeformVert[key] = x: "
343                                                                 "weight keys can't be negative");
344                                 return -1;
345                         }
346                         else {
347                                 MDeformWeight *dw = defvert_verify_index(self->data, i);
348                                 const float f = PyFloat_AsDouble(value);
349                                 if (f == -1 && PyErr_Occurred()) { // parsed key not a number
350                                         PyErr_SetString(PyExc_TypeError,
351                                                                         "BMDeformVert[key] = x: "
352                                                                         "argument not a number");
353                                         return -1;
354                                 }
355
356                                 dw->weight = CLAMPIS(f, 0.0f, 1.0f);
357                         }
358                 }
359                 else {
360                         /* del dvert[group_index] */
361                         MDeformWeight *dw = defvert_find_index(self->data, i);
362
363                         if (dw == NULL) {
364                                 PyErr_SetString(PyExc_KeyError, "del BMDeformVert[key]: "
365                                                 "key not found");
366                         }
367                         defvert_remove_group(self->data, dw);
368                 }
369
370                 return 0;
371
372         }
373         else {
374                 PyErr_Format(PyExc_TypeError,
375                              "BMDeformVert keys must be integers, not %.200s",
376                              Py_TYPE(key)->tp_name);
377                 return -1;
378         }
379 }
380
381 static int bpy_bmdeformvert_contains(BPy_BMDeformVert *self, PyObject *value)
382 {
383         const int key = PyLong_AsSsize_t(value);
384
385         if (key == -1 && PyErr_Occurred()) {
386                 PyErr_SetString(PyExc_TypeError,
387                                 "BMDeformVert.__contains__: expected an int");
388                 return -1;
389         }
390
391         return (defvert_find_index(self->data, key) != NULL) ? 1 : 0;
392 }
393
394 /* only defined for __contains__ */
395 static PySequenceMethods bpy_bmdeformvert_as_sequence = {
396     (lenfunc)bpy_bmdeformvert_len,               /* sq_length */
397     NULL,                                        /* sq_concat */
398     NULL,                                        /* sq_repeat */
399
400     /* note: if this is set PySequence_Check() returns True,
401      * but in this case we dont want to be treated as a seq */
402     NULL,                                        /* sq_item */
403
404     NULL,                                        /* sq_slice */
405     NULL,                                        /* sq_ass_item */
406     NULL,                                        /* *was* sq_ass_slice */
407     (objobjproc)bpy_bmdeformvert_contains,  /* sq_contains */
408     (binaryfunc) NULL,                           /* sq_inplace_concat */
409     (ssizeargfunc) NULL,                         /* sq_inplace_repeat */
410 };
411
412 static PyMappingMethods bpy_bmdeformvert_as_mapping = {
413         (lenfunc)bpy_bmdeformvert_len,
414         (binaryfunc)bpy_bmdeformvert_subscript,
415         (objobjargproc)bpy_bmdeformvert_ass_subscript
416 };
417
418 /* Methods
419  * ======= */
420
421 PyDoc_STRVAR(bpy_bmdeformvert_keys_doc,
422 ".. method:: keys()\n"
423 "\n"
424 "   Return the group indices used by this vertex\n"
425 "   (matching pythons dict.keys() functionality).\n"
426 "\n"
427 "   :return: the deform group this vertex uses\n"
428 "   :rtype: list of ints\n"
429 );
430 static PyObject *bpy_bmdeformvert_keys(BPy_BMDeformVert *self)
431 {
432         PyObject *ret;
433         int i;
434         MDeformWeight *dw = self->data->dw;
435
436         ret = PyList_New(self->data->totweight);
437         for (i = 0; i < self->data->totweight; i++, dw++) {
438                 PyList_SET_ITEM(ret, i, PyLong_FromSsize_t(dw->def_nr));
439         }
440
441         return ret;
442 }
443
444 PyDoc_STRVAR(bpy_bmdeformvert_values_doc,
445 ".. method:: items()\n"
446 "\n"
447 "   Return (group, weight) pairs for this vertex\n"
448 "   (matching pythons dict.items() functionality).\n"
449 "\n"
450 "   :return: (key, value) pairs for each deform weight of this vertex.\n"
451 "   :rtype: list of tuples\n"
452 );
453 static PyObject *bpy_bmdeformvert_values(BPy_BMDeformVert *self)
454 {
455         PyObject *ret;
456         int i;
457         MDeformWeight *dw = self->data->dw;
458
459         ret = PyList_New(self->data->totweight);
460         for (i = 0; i < self->data->totweight; i++, dw++) {
461                 PyList_SET_ITEM(ret, i, PyFloat_FromDouble(dw->weight));
462         }
463
464         return ret;
465 }
466
467 PyDoc_STRVAR(bpy_bmdeformvert_items_doc,
468 ".. method:: values()\n"
469 "\n"
470 "   Return the weights of the deform vertex\n"
471 "   (matching pythons dict.values() functionality).\n"
472 "\n"
473 "   :return: The weights that influence this vertex\n"
474 "   :rtype: list of floats\n"
475 );
476 static PyObject *bpy_bmdeformvert_items(BPy_BMDeformVert *self)
477 {
478         PyObject *ret;
479         PyObject *item;
480         int i;
481         MDeformWeight *dw = self->data->dw;
482
483         ret = PyList_New(self->data->totweight);
484         for (i = 0; i < self->data->totweight; i++, dw++) {
485                 item = PyTuple_New(2);
486
487                 PyTuple_SET_ITEM(item, 0, PyLong_FromSsize_t(dw->def_nr));
488                 PyTuple_SET_ITEM(item, 1, PyFloat_FromDouble(dw->weight));
489
490                 PyList_SET_ITEM(ret, i, item);
491         }
492
493         return ret;
494 }
495
496 PyDoc_STRVAR(bpy_bmdeformvert_get_doc,
497 ".. method:: get(key, default=None)\n"
498 "\n"
499 "   Returns the deform weight matching the key or default\n"
500 "   when not found (matches pythons dictionary function of the same name).\n"
501 "\n"
502 "   :arg key: The key associated with deform weight.\n"
503 "   :type key: int\n"
504 "   :arg default: Optional argument for the value to return if\n"
505 "      *key* is not found.\n"
506 "   :type default: Undefined\n"
507 );
508 static PyObject *bpy_bmdeformvert_get(BPy_BMDeformVert *self, PyObject *args)
509 {
510         int key;
511         PyObject *def = Py_None;
512
513         if (!PyArg_ParseTuple(args, "i|O:get", &key, &def)) {
514                 return NULL;
515         }
516         else {
517                 MDeformWeight *dw = defvert_find_index(self->data, key);
518
519                 if (dw) {
520                         return PyFloat_FromDouble(dw->weight);
521                 }
522                 else {
523                         return Py_INCREF(def), def;
524                 }
525         }
526 }
527
528 static struct PyMethodDef bpy_bmdeformvert_methods[] = {
529     {"keys",    (PyCFunction)bpy_bmdeformvert_keys,    METH_NOARGS,  bpy_bmdeformvert_keys_doc},
530     {"values",  (PyCFunction)bpy_bmdeformvert_values,  METH_NOARGS,  bpy_bmdeformvert_values_doc},
531     {"items",   (PyCFunction)bpy_bmdeformvert_items,   METH_NOARGS,  bpy_bmdeformvert_items_doc},
532     {"get",     (PyCFunction)bpy_bmdeformvert_get,     METH_VARARGS, bpy_bmdeformvert_get_doc},
533     /* BMESH_TODO */
534     {NULL, NULL, 0, NULL}
535 };
536
537 PyTypeObject BPy_BMDeformVert_Type = {{{0}}}; /* bm.loops.layers.uv.active */
538
539 static void bm_init_types_bmdvert(void)
540 {
541         BPy_BMDeformVert_Type.tp_basicsize = sizeof(BPy_BMDeformVert);
542
543         BPy_BMDeformVert_Type.tp_name = "BMDeformVert";
544
545         BPy_BMDeformVert_Type.tp_doc = NULL; // todo
546
547         BPy_BMDeformVert_Type.tp_as_sequence = &bpy_bmdeformvert_as_sequence;
548         BPy_BMDeformVert_Type.tp_as_mapping = &bpy_bmdeformvert_as_mapping;
549
550         BPy_BMDeformVert_Type.tp_methods = bpy_bmdeformvert_methods;
551
552         BPy_BMDeformVert_Type.tp_flags = Py_TPFLAGS_DEFAULT;
553
554         PyType_Ready(&BPy_BMDeformVert_Type);
555 }
556
557 int BPy_BMDeformVert_AssignPyObject(struct MDeformVert *dvert, PyObject *value)
558 {
559         if (UNLIKELY(!BPy_BMDeformVert_Check(value))) {
560                 PyErr_Format(PyExc_TypeError, "expected BMDeformVert, not a %.200s", Py_TYPE(value)->tp_name);
561                 return -1;
562         }
563         else {
564                 MDeformVert *dvert_src = ((BPy_BMDeformVert *)value)->data;
565                 if (LIKELY(dvert != dvert_src)) {
566                         defvert_copy(dvert, dvert_src);
567                 }
568                 return 0;
569         }
570 }
571
572 PyObject *BPy_BMDeformVert_CreatePyObject(struct MDeformVert *dvert)
573 {
574         BPy_BMDeformVert *self = PyObject_New(BPy_BMDeformVert, &BPy_BMDeformVert_Type);
575         self->data = dvert;
576         return (PyObject *)self;
577 }
578
579 /* --- End Mesh Deform Vert --- */
580
581
582 /* call to init all types */
583 void BPy_BM_init_types_meshdata(void)
584 {
585         bm_init_types_bmloopuv();
586         bm_init_types_bmloopcol();
587         bm_init_types_bmdvert();
588 }