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