bmesh py api:
[blender.git] / source / blender / python / bmesh / bmesh_py_types_customdata.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_customdata.c
27  *  \ingroup pybmesh
28  *
29  * This file defines the types for 'BMesh.verts/edges/faces/loops.layers'
30  * customdata layer access.
31  */
32
33 #include <Python.h>
34
35 #include "BLI_string.h"
36 #include "BLI_math_vector.h"
37
38 #include "bmesh.h"
39
40 #include "bmesh_py_types.h"
41 #include "bmesh_py_types_customdata.h"
42
43 #include "../mathutils/mathutils.h"
44
45 #include "BKE_customdata.h"
46
47 #include "DNA_meshdata_types.h"
48
49 static CustomData *bpy_bm_customdata_get(BMesh *bm, char htype)
50 {
51         switch (htype) {
52                 case BM_VERT:  return &bm->vdata;
53                 case BM_EDGE:  return &bm->edata;
54                 case BM_FACE:  return &bm->pdata;
55                 case BM_LOOP:  return &bm->ldata;
56         }
57
58         BLI_assert(0);
59         return NULL;
60 }
61
62 static CustomDataLayer *bpy_bmlayeritem_get(BPy_BMLayerItem *self)
63 {
64         CustomData *data = bpy_bm_customdata_get(self->bm, self->htype);
65         return &data->layers[CustomData_get_layer_index_n(data, self->type, self->index)];
66 }
67
68 /* py-type definitions
69  * ******************* */
70
71 /* getseters
72  * ========= */
73
74 static PyObject *bpy_bmlayeraccess_collection_get(BPy_BMLayerAccess *self, void *flag)
75 {
76         const int type = (int)GET_INT_FROM_POINTER(flag);
77
78         BPY_BM_CHECK_OBJ(self);
79
80         return BPy_BMLayerCollection_CreatePyObject(self->bm, self->htype, type);
81 }
82
83 static PyObject *bpy_bmlayeritem_name_get(BPy_BMLayerItem *self, void *UNUSED(flag))
84 {
85         CustomDataLayer *layer;
86
87         BPY_BM_CHECK_OBJ(self);
88
89         layer = bpy_bmlayeritem_get(self);
90         return PyUnicode_FromString(layer->name);
91 }
92
93 static PyGetSetDef bpy_bmlayeraccess_getseters[] = {
94     {(char *)"deform", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_MDEFORMVERT},
95
96     {(char *)"float",  (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_PROP_FLT},
97     {(char *)"int",    (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_PROP_INT},
98     {(char *)"string", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_PROP_STR},
99
100     {(char *)"tex",   (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_MTEXPOLY},
101     {(char *)"uv",    (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_MLOOPUV},
102     {(char *)"color", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_MLOOPCOL},
103
104     {(char *)"shape",        (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_SHAPEKEY},
105     {(char *)"bevel_weight", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_BWEIGHT},
106     {(char *)"crease",       (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_CREASE},
107
108     {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
109 };
110
111 static PyGetSetDef bpy_bmlayeritem_getseters[] = {
112     /* BMESH_TODO, make writeable */
113     {(char *)"name", (getter)bpy_bmlayeritem_name_get, (setter)NULL, (char *)NULL, NULL},
114
115     {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
116 };
117
118
119 /* Methods
120  * ======= */
121
122 /* BMLayerCollection
123  * ----------------- */
124
125 PyDoc_STRVAR(bpy_bmlayercollection_keys_doc,
126 ".. method:: keys()\n"
127 "\n"
128 "   Return the identifiers of collection members\n"
129 "   (matching pythons dict.keys() functionality).\n"
130 "\n"
131 "   :return: the identifiers for each member of this collection.\n"
132 "   :rtype: list of strings\n"
133 );
134 static PyObject *bpy_bmlayercollection_keys(BPy_BMLayerCollection *self)
135 {
136         PyObject *ret = PyList_New(0);
137         PyObject *item;
138         int index;
139         CustomData *data;
140
141         BPY_BM_CHECK_OBJ(self);
142
143         data = bpy_bm_customdata_get(self->bm, self->htype);
144         index = CustomData_get_layer_index(data, self->type);
145
146         ret = PyList_New(0);
147
148         if (index != -1) {
149                 int tot = CustomData_number_of_layers(data, self->type);
150                 for ( ; tot-- > 0; index++) {
151                         item = PyUnicode_FromString(data->layers[index].name);
152                         PyList_Append(ret, item);
153                         Py_DECREF(item);
154                 }
155         }
156
157         return ret;
158 }
159
160 PyDoc_STRVAR(bpy_bmlayercollection_values_doc,
161 ".. method:: items()\n"
162 "\n"
163 "   Return the identifiers of collection members\n"
164 "   (matching pythons dict.items() functionality).\n"
165 "\n"
166 "   :return: (key, value) pairs for each member of this collection.\n"
167 "   :rtype: list of tuples\n"
168 );
169 static PyObject *bpy_bmlayercollection_values(BPy_BMLayerCollection *self)
170 {
171         PyObject *ret;
172         PyObject *item;
173         int index;
174         CustomData *data;
175
176         BPY_BM_CHECK_OBJ(self);
177
178         data = bpy_bm_customdata_get(self->bm, self->htype);
179         index = CustomData_get_layer_index(data, self->type);
180
181         ret = PyList_New(0);
182
183         if (index != -1) {
184                 int tot = CustomData_number_of_layers(data, self->type);
185                 for ( ; tot-- > 0; index++) {
186                         item = PyTuple_New(2);
187                         PyTuple_SET_ITEM(item, 0, PyUnicode_FromString(data->layers[index].name));
188                         PyTuple_SET_ITEM(item, 1, BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, index));
189                         PyList_Append(ret, item);
190                         Py_DECREF(item);
191                 }
192         }
193
194         return ret;
195 }
196
197 PyDoc_STRVAR(bpy_bmlayercollection_items_doc,
198 ".. method:: values()\n"
199 "\n"
200 "   Return the values of collection\n"
201 "   (matching pythons dict.values() functionality).\n"
202 "\n"
203 "   :return: the members of this collection.\n"
204 "   :rtype: list\n"
205 );
206 static PyObject *bpy_bmlayercollection_items(BPy_BMLayerCollection *self)
207 {
208         PyObject *ret;
209         PyObject *item;
210         int index;
211         CustomData *data;
212
213         BPY_BM_CHECK_OBJ(self);
214
215         data = bpy_bm_customdata_get(self->bm, self->htype);
216         index = CustomData_get_layer_index(data, self->type);
217
218         ret = PyList_New(0);
219
220         if (index != -1) {
221                 int tot = CustomData_number_of_layers(data, self->type);
222                 for ( ; tot-- > 0; index++) {
223                         item = BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, index);
224                         PyList_Append(ret, item);
225                         Py_DECREF(item);
226                 }
227         }
228
229         return ret;
230 }
231
232 PyDoc_STRVAR(bpy_bmlayercollection_get_doc,
233 ".. method:: get(key, default=None)\n"
234 "\n"
235 "   Returns the value of the layer matching the key or default\n"
236 "   when not found (matches pythons dictionary function of the same name).\n"
237 "\n"
238 "   :arg key: The key associated with the layer.\n"
239 "   :type key: string\n"
240 "   :arg default: Optional argument for the value to return if\n"
241 "      *key* is not found.\n"
242 "   :type default: Undefined\n"
243 );
244 static PyObject *bpy_bmlayercollection_get(BPy_BMLayerCollection *self, PyObject *args)
245 {
246         const char *key;
247         PyObject* def = Py_None;
248
249         BPY_BM_CHECK_OBJ(self);
250
251         if (!PyArg_ParseTuple(args, "s|O:get", &key, &def)) {
252                 return NULL;
253         }
254         else {
255                 CustomData *data;
256                 int index;
257
258                 data = bpy_bm_customdata_get(self->bm, self->htype);
259                 index = CustomData_get_named_layer_index(data, self->type, key);
260
261                 if (index != -1) {
262                         return BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, index);
263                 }
264         }
265
266         return Py_INCREF(def), def;
267 }
268
269 static struct PyMethodDef bpy_bmelemseq_methods[] = {
270     {"keys",     (PyCFunction)bpy_bmlayercollection_keys,     METH_NOARGS,  bpy_bmlayercollection_keys_doc},
271     {"values",   (PyCFunction)bpy_bmlayercollection_values,   METH_NOARGS,  bpy_bmlayercollection_values_doc},
272     {"items",    (PyCFunction)bpy_bmlayercollection_items,    METH_NOARGS,  bpy_bmlayercollection_items_doc},
273     {"get",      (PyCFunction)bpy_bmlayercollection_get,      METH_VARARGS, bpy_bmlayercollection_get_doc},
274
275     /* for later! */
276 #if 0
277
278         {"new",    (PyCFunction)bpy_bmlayercollection_new,    METH_O, bpy_bmlayercollection_new_doc},
279     {"remove", (PyCFunction)bpy_bmlayercollection_new,    METH_O, bpy_bmlayercollection_remove_doc},
280 #endif
281     {NULL, NULL, 0, NULL}
282 };
283
284
285
286 /* Sequences
287  * ========= */
288
289 static Py_ssize_t bpy_bmlayercollection_length(BPy_BMLayerCollection *self)
290 {
291         CustomData *data;
292
293         BPY_BM_CHECK_INT(self);
294
295         data = bpy_bm_customdata_get(self->bm, self->htype);
296
297         return CustomData_number_of_layers(data, self->type);
298 }
299
300 static PyObject *bpy_bmlayercollection_subscript_str(BPy_BMLayerCollection *self, const char *keyname)
301 {
302         CustomData *data;
303         int index;
304
305         BPY_BM_CHECK_OBJ(self);
306
307         data = bpy_bm_customdata_get(self->bm, self->htype);
308         index = CustomData_get_named_layer_index(data, self->type, keyname);
309
310         if (index != -1) {
311                 return BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, index);
312         }
313         else {
314                 PyErr_Format(PyExc_KeyError,
315                              "BMLayerCollection[key]: key \"%.200s\" not found", keyname);
316                 return NULL;
317         }
318 }
319
320 static PyObject *bpy_bmlayercollection_subscript_int(BPy_BMLayerCollection *self, int keynum)
321 {
322         Py_ssize_t len;
323         BPY_BM_CHECK_OBJ(self);
324
325         len = bpy_bmlayercollection_length(self);
326
327         if (keynum < 0) keynum += len;
328         if (keynum >= 0) {
329                 if (keynum < len) {
330                         return BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, keynum);
331                 }
332         }
333
334         PyErr_Format(PyExc_IndexError,
335                      "BMLayerCollection[index]: index %d out of range", keynum);
336         return NULL;
337 }
338
339 static PyObject *bpy_bmlayercollection_subscript_slice(BPy_BMLayerCollection *self, Py_ssize_t start, Py_ssize_t stop)
340 {
341         Py_ssize_t len = bpy_bmlayercollection_length(self);
342         int count = 0;
343
344         PyObject *tuple;
345
346         BPY_BM_CHECK_OBJ(self);
347
348         if (start >= start) start = len - 1;
349         if (stop >= stop)   stop  = len - 1;
350
351         tuple = PyTuple_New(stop - start);
352
353         for (count = start; count < stop; count++) {
354                 PyTuple_SET_ITEM(tuple, count - start, BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, count));
355         }
356
357         return tuple;
358 }
359
360 static PyObject *bpy_bmlayercollection_subscript(BPy_BMLayerCollection *self, PyObject *key)
361 {
362         /* dont need error check here */
363         if (PyUnicode_Check(key)) {
364                 return bpy_bmlayercollection_subscript_str(self, _PyUnicode_AsString(key));
365         }
366         else if (PyIndex_Check(key)) {
367                 Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
368                 if (i == -1 && PyErr_Occurred())
369                         return NULL;
370                 return bpy_bmlayercollection_subscript_int(self, i);
371         }
372         else if (PySlice_Check(key)) {
373                 PySliceObject *key_slice = (PySliceObject *)key;
374                 Py_ssize_t step = 1;
375
376                 if (key_slice->step != Py_None && !_PyEval_SliceIndex(key, &step)) {
377                         return NULL;
378                 }
379                 else if (step != 1) {
380                         PyErr_SetString(PyExc_TypeError,
381                                         "BMLayerCollection[slice]: slice steps not supported");
382                         return NULL;
383                 }
384                 else if (key_slice->start == Py_None && key_slice->stop == Py_None) {
385                         return bpy_bmlayercollection_subscript_slice(self, 0, PY_SSIZE_T_MAX);
386                 }
387                 else {
388                         Py_ssize_t start = 0, stop = PY_SSIZE_T_MAX;
389
390                         /* avoid PySlice_GetIndicesEx because it needs to know the length ahead of time. */
391                         if (key_slice->start != Py_None && !_PyEval_SliceIndex(key_slice->start, &start)) return NULL;
392                         if (key_slice->stop != Py_None && !_PyEval_SliceIndex(key_slice->stop, &stop))    return NULL;
393
394                         if (start < 0 || stop < 0) {
395                                 /* only get the length for negative values */
396                                 Py_ssize_t len = bpy_bmlayercollection_length(self);
397                                 if (start < 0) start += len;
398                                 if (stop < 0) start += len;
399                         }
400
401                         if (stop - start <= 0) {
402                                 return PyTuple_New(0);
403                         }
404                         else {
405                                 return bpy_bmlayercollection_subscript_slice(self, start, stop);
406                         }
407                 }
408         }
409         else {
410                 PyErr_SetString(PyExc_AttributeError,
411                                 "BMLayerCollection[key]: invalid key, key must be an int");
412                 return NULL;
413         }
414 }
415
416 static int bpy_bmlayercollection_contains(BPy_BMLayerCollection *self, PyObject *value)
417 {
418         const char *keyname = _PyUnicode_AsString(value);
419         CustomData *data;
420         int index;
421
422         BPY_BM_CHECK_INT(self);
423
424         if (keyname == NULL) {
425                 PyErr_SetString(PyExc_TypeError,
426                                 "BMLayerCollection.__contains__: expected a string");
427                 return -1;
428         }
429
430         data = bpy_bm_customdata_get(self->bm, self->htype);
431         index = CustomData_get_named_layer_index(data, self->type, keyname);
432
433         return (index != -1) ? 1 : 0;
434 }
435
436 static PySequenceMethods bpy_bmlayercollection_as_sequence = {
437     (lenfunc)bpy_bmlayercollection_length,       /* sq_length */
438     NULL,                                        /* sq_concat */
439     NULL,                                        /* sq_repeat */
440     (ssizeargfunc)bpy_bmlayercollection_subscript_int, /* sq_item */ /* Only set this so PySequence_Check() returns True */
441     NULL,                                        /* sq_slice */
442     (ssizeobjargproc)NULL,                       /* sq_ass_item */
443     NULL,                                        /* *was* sq_ass_slice */
444     (objobjproc)bpy_bmlayercollection_contains,  /* sq_contains */
445     (binaryfunc) NULL,                           /* sq_inplace_concat */
446     (ssizeargfunc) NULL,                         /* sq_inplace_repeat */
447 };
448
449 static PyMappingMethods bpy_bmlayercollection_as_mapping = {
450     (lenfunc)bpy_bmlayercollection_length,       /* mp_length */
451     (binaryfunc)bpy_bmlayercollection_subscript, /* mp_subscript */
452     (objobjargproc)NULL,                         /* mp_ass_subscript */
453 };
454
455 /* Iterator
456  * -------- */
457
458 static PyObject *bpy_bmlayercollection_iter(BPy_BMLayerCollection *self)
459 {
460         /* fake it with a list iterator */
461         PyObject *ret;
462         PyObject *iter = NULL;
463
464         BPY_BM_CHECK_OBJ(self);
465
466         ret = bpy_bmlayercollection_subscript_slice(self, 0, PY_SSIZE_T_MIN);
467
468         if (ret) {
469                 iter = PyObject_GetIter(ret);
470                 Py_DECREF(ret);
471         }
472
473         return iter;
474 }
475
476 PyTypeObject BPy_BMLayerAccess_Type     = {{{0}}}; /* bm.verts.layers */
477 PyTypeObject BPy_BMLayerCollection_Type = {{{0}}}; /* bm.verts.layers.uv */
478 PyTypeObject BPy_BMLayerItem_Type       = {{{0}}}; /* bm.verts.layers.uv["UVMap"] */
479
480
481 PyObject *BPy_BMLayerAccess_CreatePyObject(BMesh *bm, const char htype)
482 {
483         BPy_BMLayerAccess *self = PyObject_New(BPy_BMLayerAccess, &BPy_BMLayerAccess_Type);
484         self->bm = bm;
485         self->htype = htype;
486         return (PyObject *)self;
487 }
488
489 PyObject *BPy_BMLayerCollection_CreatePyObject(BMesh *bm, const char htype, int type)
490 {
491         BPy_BMLayerCollection *self = PyObject_New(BPy_BMLayerCollection, &BPy_BMLayerCollection_Type);
492         self->bm = bm;
493         self->htype = htype;
494         self->type = type;
495         return (PyObject *)self;
496 }
497
498 PyObject *BPy_BMLayerItem_CreatePyObject(BMesh *bm, const char htype, int type, int index)
499 {
500         BPy_BMLayerItem *self = PyObject_New(BPy_BMLayerItem, &BPy_BMLayerItem_Type);
501         self->bm = bm;
502         self->htype = htype;
503         self->type = type;
504         self->index = index;
505         return (PyObject *)self;
506 }
507
508
509 void BPy_BM_init_types_customdata(void)
510 {
511         BPy_BMLayerAccess_Type.tp_basicsize     = sizeof(BPy_BMLayerAccess);
512         BPy_BMLayerCollection_Type.tp_basicsize = sizeof(BPy_BMLayerCollection);
513         BPy_BMLayerItem_Type.tp_basicsize       = sizeof(BPy_BMLayerItem);
514
515         BPy_BMLayerAccess_Type.tp_name     = "BMLayerAccess";
516         BPy_BMLayerCollection_Type.tp_name = "BMLayerCollection";
517         BPy_BMLayerItem_Type.tp_name       = "BMLayerItem";
518
519         BPy_BMLayerAccess_Type.tp_doc     = NULL; // todo
520         BPy_BMLayerCollection_Type.tp_doc = NULL;
521         BPy_BMLayerItem_Type.tp_doc       = NULL;
522
523         BPy_BMLayerAccess_Type.tp_repr  = (reprfunc)NULL;
524         BPy_BMLayerCollection_Type.tp_repr = (reprfunc)NULL;
525         BPy_BMLayerItem_Type.tp_repr = (reprfunc)NULL;
526
527         BPy_BMLayerAccess_Type.tp_getset     = bpy_bmlayeraccess_getseters;
528         BPy_BMLayerCollection_Type.tp_getset = NULL;
529         BPy_BMLayerItem_Type.tp_getset       = bpy_bmlayeritem_getseters;
530
531
532 //      BPy_BMLayerAccess_Type.tp_methods     = bpy_bmeditselseq_methods;
533         BPy_BMLayerCollection_Type.tp_methods = bpy_bmelemseq_methods;
534
535         BPy_BMLayerCollection_Type.tp_as_sequence = &bpy_bmlayercollection_as_sequence;
536
537         BPy_BMLayerCollection_Type.tp_as_mapping = &bpy_bmlayercollection_as_mapping;
538
539         BPy_BMLayerCollection_Type.tp_iter = (getiterfunc)bpy_bmlayercollection_iter;
540
541         BPy_BMLayerAccess_Type.tp_dealloc     = NULL; //(destructor)bpy_bmeditselseq_dealloc;
542         BPy_BMLayerCollection_Type.tp_dealloc = NULL; //(destructor)bpy_bmvert_dealloc;
543         BPy_BMLayerItem_Type.tp_dealloc       = NULL; //(destructor)bpy_bmvert_dealloc;
544
545
546
547         BPy_BMLayerAccess_Type.tp_flags     = Py_TPFLAGS_DEFAULT;
548         BPy_BMLayerCollection_Type.tp_flags = Py_TPFLAGS_DEFAULT;
549         BPy_BMLayerItem_Type.tp_flags       = Py_TPFLAGS_DEFAULT;
550
551         PyType_Ready(&BPy_BMLayerAccess_Type);
552         PyType_Ready(&BPy_BMLayerCollection_Type);
553         PyType_Ready(&BPy_BMLayerItem_Type);
554 }
555
556
557 /* Per Element Get/Set
558  * ******************* */
559
560 /**
561  * helper function for get/set, NULL return means the error is set
562 */
563 static void *bpy_bmlayeritem_ptr_get(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer)
564 {
565         void *value;
566         BMElem *ele = py_ele->ele;
567         CustomData *data;
568
569         /* error checking */
570         if (UNLIKELY(!BPy_BMLayerItem_Check(py_layer))) {
571                 PyErr_SetString(PyExc_AttributeError,
572                                 "BMElem[key]: invalid key, must be a BMLayerItem");
573                 return NULL;
574         }
575         else if (UNLIKELY(py_ele->bm != py_layer->bm)) {
576                 PyErr_SetString(PyExc_ValueError,
577                                 "BMElem[layer]: layer is from another mesh");
578                 return NULL;
579         }
580         else if (UNLIKELY(ele->head.htype != py_layer->htype)) {
581                 char namestr_1[32], namestr_2[32];
582                 PyErr_Format(PyExc_ValueError,
583                              "Layer/Element type mismatch, expected %.200s got layer type %.200s",
584                              BPy_BMElem_StringFromHType_ex(ele->head.htype, namestr_1),
585                              BPy_BMElem_StringFromHType_ex(py_layer->htype, namestr_2));
586                 return NULL;
587         }
588
589         data = bpy_bm_customdata_get(py_layer->bm, py_layer->htype);
590
591         value = CustomData_bmesh_get_n(data, ele->head.data, py_layer->type, py_layer->index);
592
593         if (UNLIKELY(value == NULL)) {
594                 /* this should be fairly unlikely but possible if layers move about after we get them */
595                 PyErr_SetString(PyExc_KeyError,
596                              "BMElem[key]: layer not found");
597                 return NULL;
598         }
599         else {
600                 return value;
601         }
602 }
603
604
605 /**
606  *\brief BMElem.__getitem__()
607  *
608  * assume all error checks are done, eg:
609  *
610  *     uv = vert[uv_layer]
611  */
612 PyObject *BPy_BMLayerItem_GetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer)
613 {
614         void *value = bpy_bmlayeritem_ptr_get(py_ele, py_layer);
615         PyObject *ret;
616
617         if (UNLIKELY(value == NULL)) {
618                 return NULL;
619         }
620
621         switch (py_layer->type) {
622                 case CD_MDEFORMVERT:
623                 {
624                         ret = Py_NotImplemented; /* TODO */
625                         Py_INCREF(ret);
626                         break;
627                 }
628                 case CD_PROP_FLT:
629                 {
630                         ret = PyFloat_FromDouble(*(float *)value);
631                         break;
632                 }
633                 case CD_PROP_INT:
634                 {
635                         ret = PyLong_FromSsize_t((Py_ssize_t)(*(int *)value));
636                         break;
637                 }
638                 case CD_PROP_STR:
639                 {
640                         MStringProperty *mstring = value;
641                         ret = PyBytes_FromStringAndSize(mstring->s, BLI_strnlen(mstring->s, sizeof(mstring->s)));
642                         break;
643                 }
644                 case CD_MTEXPOLY:
645                 {
646                         ret = Py_NotImplemented; /* TODO */
647                         Py_INCREF(ret);
648                         break;
649                 }
650                 case CD_MLOOPUV:
651                 {
652                         ret = Py_NotImplemented; /* TODO */
653                         Py_INCREF(ret);
654                         break;
655                 }
656                 case CD_MLOOPCOL:
657                 {
658                         ret = Py_NotImplemented; /* TODO */
659                         Py_INCREF(ret);
660                         break;
661                 }
662                 case CD_SHAPEKEY:
663                 {
664                         ret = Vector_CreatePyObject((float *)value, 3, Py_WRAP, NULL);
665                         break;
666                 }
667                 case CD_BWEIGHT:
668                 {
669                         ret = PyFloat_FromDouble(*(float *)value);
670                         break;
671                 }
672                 case CD_CREASE:
673                 {
674                         ret = PyFloat_FromDouble(*(float *)value);
675                         break;
676                 }
677                 default:
678                 {
679                         ret = Py_NotImplemented; /* TODO */
680                         Py_INCREF(ret);
681                         break;
682                 }
683         }
684
685         return ret;
686 }
687
688 int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObject *py_value)
689 {
690         int ret = 0;
691         void *value = bpy_bmlayeritem_ptr_get(py_ele, py_layer);
692
693         if (UNLIKELY(value == NULL)) {
694                 return -1;
695         }
696
697         switch (py_layer->type) {
698                 case CD_MDEFORMVERT:
699                 {
700                         PyErr_SetString(PyExc_AttributeError, "readonly"); /* could make this writeable later */
701                         ret = -1;
702                         break;
703                 }
704                 case CD_PROP_FLT:
705                 {
706                         float tmp_val = PyFloat_AsDouble(py_value);
707                         if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
708                                 PyErr_Format(PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
709                                 ret = -1;
710                         }
711                         else {
712                                 *(float *)value = tmp_val;
713                         }
714                         break;
715                 }
716                 case CD_PROP_INT:
717                 {
718                         int tmp_val = PyLong_AsSsize_t(py_value);
719                         if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
720                                 PyErr_Format(PyExc_TypeError, "expected an int, not a %.200s", Py_TYPE(py_value)->tp_name);
721                                 ret = -1;
722                         }
723                         else {
724                                 *(int *)value = tmp_val;
725                         }
726                         break;
727                 }
728                 case CD_PROP_STR:
729                 {
730                         MStringProperty *mstring = value;
731                         const char *tmp_val = PyBytes_AsString(py_value);
732                         if (UNLIKELY(tmp_val == NULL)) {
733                                 PyErr_Format(PyExc_TypeError, "expected bytes, not a %.200s", Py_TYPE(py_value)->tp_name);
734                                 ret = -1;
735                         }
736                         else {
737                                 BLI_strncpy(mstring->s, tmp_val, sizeof(mstring->s));
738                         }
739                         break;
740                 }
741                 case CD_MTEXPOLY:
742                 {
743                         PyErr_SetString(PyExc_AttributeError, "readonly"); /* could make this writeable later */
744                         ret = -1;
745                         break;
746                 }
747                 case CD_MLOOPUV:
748                 {
749                         PyErr_SetString(PyExc_AttributeError, "readonly"); /* could make this writeable later */
750                         ret = -1;
751                         break;
752                 }
753                 case CD_MLOOPCOL:
754                 {
755                         PyErr_SetString(PyExc_AttributeError, "readonly");
756                         ret = -1;
757                         break;
758                 }
759                 case CD_SHAPEKEY:
760                 {
761                         float tmp_val[3];
762                         if (UNLIKELY(mathutils_array_parse(tmp_val, 3, 3, py_value, "BMVert[shape] = value") == -1)) {
763                                 ret = -1;
764                         }
765                         else {
766                                 copy_v3_v3((float *)value,tmp_val);
767                         }
768                         break;
769                 }
770                 case CD_BWEIGHT:
771                 {
772                         float tmp_val = PyFloat_AsDouble(py_value);
773                         if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
774                                 PyErr_Format(PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
775                                 ret = -1;
776                         }
777                         else {
778                                 *(float *)value = CLAMPIS(tmp_val, 0.0f, 1.0f);
779                         }
780                         break;
781                 }
782                 case CD_CREASE:
783                 {
784                         float tmp_val = PyFloat_AsDouble(py_value);
785                         if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
786                                 PyErr_Format(PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
787                                 ret = -1;
788                         }
789                         else {
790                                 *(float *)value = CLAMPIS(tmp_val, 0.0f, 1.0f);
791                         }
792                         break;
793                 }
794                 default:
795                 {
796                         PyErr_SetString(PyExc_AttributeError, "readonly / unsupported type");
797                         ret = -1;
798                         break;
799                 }
800         }
801
802         return ret;
803 }