PyAPI: correct int range checking
[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_utildefines.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 #include "../generic/python_utildefines.h"
46
47 #include "BKE_customdata.h"
48
49 #include "DNA_meshdata_types.h"
50
51 static CustomData *bpy_bm_customdata_get(BMesh *bm, char htype)
52 {
53         switch (htype) {
54                 case BM_VERT:  return &bm->vdata;
55                 case BM_EDGE:  return &bm->edata;
56                 case BM_FACE:  return &bm->pdata;
57                 case BM_LOOP:  return &bm->ldata;
58         }
59
60         BLI_assert(0);
61         return NULL;
62 }
63
64 static CustomDataLayer *bpy_bmlayeritem_get(BPy_BMLayerItem *self)
65 {
66         CustomData *data = bpy_bm_customdata_get(self->bm, self->htype);
67         const int index_absolute = CustomData_get_layer_index_n(data, self->type, self->index);
68         if (index_absolute != -1) {
69                 return &data->layers[index_absolute];
70         }
71         else {
72                 PyErr_SetString(PyExc_RuntimeError,
73                                 "layer has become invalid");
74                 return NULL;
75         }
76 }
77
78 /* py-type definitions
79  * ******************* */
80
81 /* getseters
82  * ========= */
83
84 /* used for many different types  */
85
86 PyDoc_STRVAR(bpy_bmlayeraccess_collection__float_doc,
87 "Generic float custom-data layer.\n\ntype: :class:`BMLayerCollection`"
88 );
89 PyDoc_STRVAR(bpy_bmlayeraccess_collection__int_doc,
90 "Generic int custom-data layer.\n\ntype: :class:`BMLayerCollection`"
91 );
92 PyDoc_STRVAR(bpy_bmlayeraccess_collection__string_doc,
93 "Generic string custom-data layer (exposed as bytes, 255 max length).\n\ntype: :class:`BMLayerCollection`"
94 );
95 PyDoc_STRVAR(bpy_bmlayeraccess_collection__deform_doc,
96 "Vertex deform weight :class:`BMDeformVert` (TODO).\n\ntype: :class:`BMLayerCollection`" // TYPE DOESN'T EXIST YET
97 );
98 PyDoc_STRVAR(bpy_bmlayeraccess_collection__shape_doc,
99 "Vertex shapekey absolute location (as a 3D Vector).\n\n:type: :class:`BMLayerCollection`"
100 );
101 PyDoc_STRVAR(bpy_bmlayeraccess_collection__bevel_weight_doc,
102 "Bevel weight float in [0 - 1].\n\n:type: :class:`BMLayerCollection`"
103 );
104 PyDoc_STRVAR(bpy_bmlayeraccess_collection__crease_doc,
105 "Edge crease for subsurf - float in [0 - 1].\n\n:type: :class:`BMLayerCollection`"
106 );
107 PyDoc_STRVAR(bpy_bmlayeraccess_collection__uv_doc,
108 "Accessor for :class:`BMLoopUV` UV (as a 2D Vector).\n\ntype: :class:`BMLayerCollection`"
109 );
110 PyDoc_STRVAR(bpy_bmlayeraccess_collection__color_doc,
111 "Accessor for vertex color layer.\n\ntype: :class:`BMLayerCollection`"
112 );
113 PyDoc_STRVAR(bpy_bmlayeraccess_collection__skin_doc,
114 "Accessor for skin layer.\n\ntype: :class:`BMLayerCollection`"
115 );
116 PyDoc_STRVAR(bpy_bmlayeraccess_collection__paint_mask_doc,
117 "Accessor for paint mask layer.\n\ntype: :class:`BMLayerCollection`"
118 );
119 PyDoc_STRVAR(bpy_bmlayeraccess_collection__face_map_doc,
120 "FaceMap custom-data layer.\n\ntype: :class:`BMLayerCollection`"
121 );
122 #ifdef WITH_FREESTYLE
123 PyDoc_STRVAR(bpy_bmlayeraccess_collection__freestyle_edge_doc,
124 "Accessor for Freestyle edge layer.\n\ntype: :class:`BMLayerCollection`"
125 );
126 PyDoc_STRVAR(bpy_bmlayeraccess_collection__freestyle_face_doc,
127 "Accessor for Freestyle face layer.\n\ntype: :class:`BMLayerCollection`"
128 );
129 #endif
130
131 static PyObject *bpy_bmlayeraccess_collection_get(BPy_BMLayerAccess *self, void *flag)
132 {
133         const int type = (int)GET_INT_FROM_POINTER(flag);
134
135         BPY_BM_CHECK_OBJ(self);
136
137         return BPy_BMLayerCollection_CreatePyObject(self->bm, self->htype, type);
138 }
139
140
141 PyDoc_STRVAR(bpy_bmlayercollection_active_doc,
142 "The active layer of this type (read-only).\n\n:type: :class:`BMLayerItem`"
143 );
144 static PyObject *bpy_bmlayercollection_active_get(BPy_BMLayerItem *self, void *UNUSED(flag))
145 {
146         CustomData *data;
147         int index;
148
149         BPY_BM_CHECK_OBJ(self);
150
151         data = bpy_bm_customdata_get(self->bm, self->htype);
152         index = CustomData_get_active_layer(data, self->type);  /* type relative */
153
154         if (index != -1) {
155                 return BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, index);
156         }
157         else {
158                 Py_RETURN_NONE;
159         }
160 }
161
162
163 PyDoc_STRVAR(bpy_bmlayercollection_is_singleton_doc,
164 "True if there can exists only one layer of this type (read-only).\n\n:type: boolean"
165 );
166 static PyObject *bpy_bmlayercollection_is_singleton_get(BPy_BMLayerItem *self, void *UNUSED(flag))
167 {
168         BPY_BM_CHECK_OBJ(self);
169
170         return PyBool_FromLong(CustomData_layertype_is_singleton(self->type));
171 }
172
173 PyDoc_STRVAR(bpy_bmlayercollection_name_doc,
174 "The layers unique name (read-only).\n\n:type: string"
175 );
176 static PyObject *bpy_bmlayeritem_name_get(BPy_BMLayerItem *self, void *UNUSED(flag))
177 {
178         CustomDataLayer *layer;
179
180         BPY_BM_CHECK_OBJ(self);
181
182         layer = bpy_bmlayeritem_get(self);
183         if (layer) {
184                 return PyUnicode_FromString(layer->name);
185         }
186         else {
187                 return NULL;
188         }
189 }
190
191 static PyGetSetDef bpy_bmlayeraccess_vert_getseters[] = {
192         {(char *)"deform", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__deform_doc, (void *)CD_MDEFORMVERT},
193
194         {(char *)"float",  (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__float_doc, (void *)CD_PROP_FLT},
195         {(char *)"int",    (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__int_doc, (void *)CD_PROP_INT},
196         {(char *)"string", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__string_doc, (void *)CD_PROP_STR},
197
198         {(char *)"shape",        (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__shape_doc, (void *)CD_SHAPEKEY},
199         {(char *)"bevel_weight", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__bevel_weight_doc, (void *)CD_BWEIGHT},
200         {(char *)"skin",         (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__skin_doc, (void *)CD_MVERT_SKIN},
201         {(char *)"paint_mask",   (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__paint_mask_doc, (void *)CD_PAINT_MASK},
202
203         {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
204 };
205
206 static PyGetSetDef bpy_bmlayeraccess_edge_getseters[] = {
207         {(char *)"float",  (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__float_doc, (void *)CD_PROP_FLT},
208         {(char *)"int",    (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__int_doc, (void *)CD_PROP_INT},
209         {(char *)"string", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__string_doc, (void *)CD_PROP_STR},
210
211         {(char *)"bevel_weight", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__bevel_weight_doc, (void *)CD_BWEIGHT},
212         {(char *)"crease",       (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__crease_doc, (void *)CD_CREASE},
213 #ifdef WITH_FREESTYLE
214         {(char *)"freestyle", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__freestyle_edge_doc, (void *)CD_FREESTYLE_EDGE},
215 #endif
216
217         {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
218 };
219
220 static PyGetSetDef bpy_bmlayeraccess_face_getseters[] = {
221         {(char *)"float",  (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__float_doc, (void *)CD_PROP_FLT},
222         {(char *)"int",    (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__int_doc, (void *)CD_PROP_INT},
223         {(char *)"string", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__string_doc, (void *)CD_PROP_STR},
224         {(char *)"face_map", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__face_map_doc, (void *)CD_FACEMAP},
225
226 #ifdef WITH_FREESTYLE
227         {(char *)"freestyle", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__freestyle_face_doc, (void *)CD_FREESTYLE_FACE},
228 #endif
229
230         {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
231 };
232
233 static PyGetSetDef bpy_bmlayeraccess_loop_getseters[] = {
234         {(char *)"float",  (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__float_doc, (void *)CD_PROP_FLT},
235         {(char *)"int",    (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__int_doc, (void *)CD_PROP_INT},
236         {(char *)"string", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__string_doc, (void *)CD_PROP_STR},
237
238         {(char *)"uv",    (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__uv_doc, (void *)CD_MLOOPUV},
239         {(char *)"color", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__color_doc, (void *)CD_MLOOPCOL},
240
241         {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
242 };
243
244
245 static PyGetSetDef bpy_bmlayercollection_getseters[] = {
246         /* BMESH_TODO, make writeable */
247         {(char *)"active",       (getter)bpy_bmlayercollection_active_get,       (setter)NULL, (char *)bpy_bmlayercollection_active_doc, NULL},
248         {(char *)"is_singleton", (getter)bpy_bmlayercollection_is_singleton_get, (setter)NULL, (char *)bpy_bmlayercollection_is_singleton_doc, NULL},
249
250         {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
251 };
252
253 static PyGetSetDef bpy_bmlayeritem_getseters[] = {
254         /* BMESH_TODO, make writeable */
255         {(char *)"name", (getter)bpy_bmlayeritem_name_get, (setter)NULL, (char *)bpy_bmlayercollection_name_doc, NULL},
256
257         {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
258 };
259
260
261 /* Methods
262  * ======= */
263
264 /* BMLayerCollection
265  * ----------------- */
266
267 PyDoc_STRVAR(bpy_bmlayeritem_copy_from_doc,
268 ".. method:: copy_from(other)\n"
269 "\n"
270 "   Return a copy of the layer\n"
271 "\n"
272 "   :arg other: Another layer to copy from.\n"
273 "   :arg other: :class:`BMLayerItem`\n"
274 );
275 static PyObject *bpy_bmlayeritem_copy_from(BPy_BMLayerItem *self, BPy_BMLayerItem *value)
276 {
277         CustomData *data;
278
279         if (!BPy_BMLayerItem_Check(value)) {
280                 PyErr_Format(PyExc_TypeError,
281                              "layer.copy_from(x): expected BMLayerItem, not '%.200s'",
282                              Py_TYPE(value)->tp_name);
283                 return NULL;
284         }
285
286         BPY_BM_CHECK_OBJ(self);
287         BPY_BM_CHECK_SOURCE_OBJ(self->bm, "layer.copy_from()", value);
288
289         if ((self->htype != value->htype) ||
290             (self->type  != value->type))
291         {
292                 PyErr_SetString(PyExc_ValueError,
293                                 "layer.copy_from(other): layer type mismatch");
294         }
295
296         else if (self->index == value->index) {
297                 Py_RETURN_NONE;
298         }
299
300         data = bpy_bm_customdata_get(self->bm, self->htype);
301
302         if ((bpy_bmlayeritem_get(self) == NULL) ||
303             (bpy_bmlayeritem_get(value) == NULL))
304         {
305                 return NULL;
306         }
307
308         BM_data_layer_copy(self->bm, data, self->type, value->index, self->index);
309
310         Py_RETURN_NONE;
311 }
312
313 /* similar to new(), but no name arg. */
314 PyDoc_STRVAR(bpy_bmlayercollection_verify_doc,
315 ".. method:: verify()\n"
316 "\n"
317 "   Create a new layer or return an existing active layer\n"
318 "\n"
319 "   :return: The newly verified layer.\n"
320 "   :rtype: :class:`BMLayerItem`\n"
321 );
322 static PyObject *bpy_bmlayercollection_verify(BPy_BMLayerCollection *self)
323 {
324         int index;
325         CustomData *data;
326
327         BPY_BM_CHECK_OBJ(self);
328
329         data = bpy_bm_customdata_get(self->bm, self->htype);
330
331         index = CustomData_get_active_layer(data, self->type);  /* type relative */
332
333         if (index == -1) {
334                 BM_data_layer_add(self->bm, data, self->type);
335                 index = 0;
336         }
337
338         BLI_assert(index >= 0);
339
340         return BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, index);
341 }
342
343 PyDoc_STRVAR(bpy_bmlayercollection_new_doc,
344 ".. method:: new(name)\n"
345 "\n"
346 "   Create a new layer\n"
347 "\n"
348 "   :arg name: Optional name argument (will be made unique).\n"
349 "   :type name: string\n"
350 "   :return: The newly created layer.\n"
351 "   :rtype: :class:`BMLayerItem`\n"
352 );
353 static PyObject *bpy_bmlayercollection_new(BPy_BMLayerCollection *self, PyObject *args)
354 {
355         const char *name = NULL;
356         int index;
357         CustomData *data;
358
359         BPY_BM_CHECK_OBJ(self);
360
361         if (!PyArg_ParseTuple(args, "|s:new", &name)) {
362                 return NULL;
363         }
364
365         data = bpy_bm_customdata_get(self->bm, self->htype);
366
367         if (CustomData_layertype_is_singleton(self->type) &&
368             CustomData_has_layer(data, self->type))
369         {
370                 PyErr_SetString(PyExc_ValueError,
371                                 "layers.new(): is a singleton, use verify() instead");
372                 return NULL;
373         }
374
375         if (name) {
376                 BM_data_layer_add_named(self->bm, data, self->type, name);
377         }
378         else {
379                 BM_data_layer_add(self->bm, data, self->type);
380         }
381
382         index = CustomData_number_of_layers(data, self->type) - 1;
383         BLI_assert(index >= 0);
384
385         return BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, index);
386 }
387
388 PyDoc_STRVAR(bpy_bmlayercollection_remove_doc,
389 ".. method:: remove(layer)\n"
390 "\n"
391 "   Remove a layer\n"
392 "\n"
393 "   :arg layer: The layer to remove.\n"
394 "   :type layer: :class:`BMLayerItem`\n"
395 );
396 static PyObject *bpy_bmlayercollection_remove(BPy_BMLayerCollection *self, BPy_BMLayerItem *value)
397 {
398         CustomData *data;
399
400         BPY_BM_CHECK_OBJ(self);
401
402         if (!BPy_BMLayerItem_Check(value)) {
403                 PyErr_Format(PyExc_TypeError,
404                              "layers.remove(x): expected BMLayerItem, not '%.200s'",
405                              Py_TYPE(value)->tp_name);
406                 return NULL;
407         }
408
409         BPY_BM_CHECK_OBJ(value);
410
411         if ((self->bm != value->bm) ||
412             (self->type != value->type) ||
413             (self->htype != value->htype))
414         {
415                 PyErr_SetString(PyExc_ValueError,
416                                 "layers.remove(x): x not in layers");
417         }
418
419         data = bpy_bm_customdata_get(self->bm, self->htype);
420         BM_data_layer_free_n(self->bm, data, self->type, value->index);
421
422         Py_RETURN_NONE;
423 }
424
425
426 PyDoc_STRVAR(bpy_bmlayercollection_keys_doc,
427 ".. method:: keys()\n"
428 "\n"
429 "   Return the identifiers of collection members\n"
430 "   (matching pythons dict.keys() functionality).\n"
431 "\n"
432 "   :return: the identifiers for each member of this collection.\n"
433 "   :rtype: list of strings\n"
434 );
435 static PyObject *bpy_bmlayercollection_keys(BPy_BMLayerCollection *self)
436 {
437         PyObject *ret;
438         PyObject *item;
439         int index;
440         CustomData *data;
441         int tot, i;
442
443         BPY_BM_CHECK_OBJ(self);
444
445         data = bpy_bm_customdata_get(self->bm, self->htype);
446         index = CustomData_get_layer_index(data, self->type); /* absolute, but no need to make relative */
447         tot = (index != -1) ? CustomData_number_of_layers(data, self->type) : 0;
448
449         ret = PyList_New(tot);
450
451         for (i = 0; tot-- > 0; index++) {
452                 item = PyUnicode_FromString(data->layers[index].name);
453                 PyList_SET_ITEM(ret, i++, item);
454         }
455
456         return ret;
457 }
458
459 PyDoc_STRVAR(bpy_bmlayercollection_items_doc,
460 ".. method:: items()\n"
461 "\n"
462 "   Return the identifiers of collection members\n"
463 "   (matching pythons dict.items() functionality).\n"
464 "\n"
465 "   :return: (key, value) pairs for each member of this collection.\n"
466 "   :rtype: list of tuples\n"
467 );
468 static PyObject *bpy_bmlayercollection_items(BPy_BMLayerCollection *self)
469 {
470         PyObject *ret;
471         PyObject *item;
472         int index;
473         CustomData *data;
474         int tot, i;
475
476         BPY_BM_CHECK_OBJ(self);
477
478         data = bpy_bm_customdata_get(self->bm, self->htype);
479         index = CustomData_get_layer_index(data, self->type);
480         tot = (index != -1) ? CustomData_number_of_layers(data, self->type) : 0;
481
482         ret = PyList_New(tot);
483
484         for (i = 0; tot-- > 0; index++) {
485                 item = PyTuple_New(2);
486                 PyTuple_SET_ITEMS(item,
487                         PyUnicode_FromString(data->layers[index].name),
488                         BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, i));
489                 PyList_SET_ITEM(ret, i++, item);
490         }
491
492         return ret;
493 }
494
495 PyDoc_STRVAR(bpy_bmlayercollection_values_doc,
496 ".. method:: values()\n"
497 "\n"
498 "   Return the values of collection\n"
499 "   (matching pythons dict.values() functionality).\n"
500 "\n"
501 "   :return: the members of this collection.\n"
502 "   :rtype: list\n"
503 );
504 static PyObject *bpy_bmlayercollection_values(BPy_BMLayerCollection *self)
505 {
506         PyObject *ret;
507         PyObject *item;
508         int index;
509         CustomData *data;
510         int tot, i;
511
512         BPY_BM_CHECK_OBJ(self);
513
514         data = bpy_bm_customdata_get(self->bm, self->htype);
515         index = CustomData_get_layer_index(data, self->type);
516         tot = (index != -1) ? CustomData_number_of_layers(data, self->type) : 0;
517
518         ret = PyList_New(tot);
519
520         for (i = 0; tot-- > 0; index++) {
521                 item = BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, i);
522                 PyList_SET_ITEM(ret, i++, item);
523         }
524
525         return ret;
526 }
527
528 PyDoc_STRVAR(bpy_bmlayercollection_get_doc,
529 ".. method:: get(key, default=None)\n"
530 "\n"
531 "   Returns the value of the layer matching the key or default\n"
532 "   when not found (matches pythons dictionary function of the same name).\n"
533 "\n"
534 "   :arg key: The key associated with the layer.\n"
535 "   :type key: string\n"
536 "   :arg default: Optional argument for the value to return if\n"
537 "      *key* is not found.\n"
538 "   :type default: Undefined\n"
539 );
540 static PyObject *bpy_bmlayercollection_get(BPy_BMLayerCollection *self, PyObject *args)
541 {
542         const char *key;
543         PyObject *def = Py_None;
544
545         BPY_BM_CHECK_OBJ(self);
546
547         if (!PyArg_ParseTuple(args, "s|O:get", &key, &def)) {
548                 return NULL;
549         }
550         else {
551                 CustomData *data;
552                 int index;
553
554                 data = bpy_bm_customdata_get(self->bm, self->htype);
555                 index = CustomData_get_named_layer(data, self->type, key);  /* type relative */
556
557                 if (index != -1) {
558                         return BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, index);
559                 }
560         }
561
562         return Py_INCREF_RET(def);
563 }
564
565 static struct PyMethodDef bpy_bmlayeritem_methods[] = {
566         {"copy_from", (PyCFunction)bpy_bmlayeritem_copy_from,    METH_O,       bpy_bmlayeritem_copy_from_doc},
567         {NULL, NULL, 0, NULL}
568 };
569
570 static struct PyMethodDef bpy_bmelemseq_methods[] = {
571         {"verify",  (PyCFunction)bpy_bmlayercollection_verify,   METH_NOARGS,  bpy_bmlayercollection_verify_doc},
572         {"new",     (PyCFunction)bpy_bmlayercollection_new,      METH_VARARGS, bpy_bmlayercollection_new_doc},
573         {"remove",  (PyCFunction)bpy_bmlayercollection_remove,   METH_O,       bpy_bmlayercollection_remove_doc},
574
575         {"keys",    (PyCFunction)bpy_bmlayercollection_keys,     METH_NOARGS,  bpy_bmlayercollection_keys_doc},
576         {"values",  (PyCFunction)bpy_bmlayercollection_values,   METH_NOARGS,  bpy_bmlayercollection_values_doc},
577         {"items",   (PyCFunction)bpy_bmlayercollection_items,    METH_NOARGS,  bpy_bmlayercollection_items_doc},
578         {"get",     (PyCFunction)bpy_bmlayercollection_get,      METH_VARARGS, bpy_bmlayercollection_get_doc},
579         {NULL, NULL, 0, NULL}
580 };
581
582 /* Sequences
583  * ========= */
584
585 static Py_ssize_t bpy_bmlayercollection_length(BPy_BMLayerCollection *self)
586 {
587         CustomData *data;
588
589         BPY_BM_CHECK_INT(self);
590
591         data = bpy_bm_customdata_get(self->bm, self->htype);
592
593         return CustomData_number_of_layers(data, self->type);
594 }
595
596 static PyObject *bpy_bmlayercollection_subscript_str(BPy_BMLayerCollection *self, const char *keyname)
597 {
598         CustomData *data;
599         int index;
600
601         BPY_BM_CHECK_OBJ(self);
602
603         data = bpy_bm_customdata_get(self->bm, self->htype);
604         index = CustomData_get_named_layer(data, self->type, keyname);  /* type relative */
605
606         if (index != -1) {
607                 return BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, index);
608         }
609         else {
610                 PyErr_Format(PyExc_KeyError,
611                              "BMLayerCollection[key]: key \"%.200s\" not found", keyname);
612                 return NULL;
613         }
614 }
615
616 static PyObject *bpy_bmlayercollection_subscript_int(BPy_BMLayerCollection *self, int keynum)
617 {
618         Py_ssize_t len;
619         BPY_BM_CHECK_OBJ(self);
620
621         len = bpy_bmlayercollection_length(self);
622
623         if (keynum < 0) keynum += len;
624         if (keynum >= 0) {
625                 if (keynum < len) {
626                         return BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, keynum);
627                 }
628         }
629
630         PyErr_Format(PyExc_IndexError,
631                      "BMLayerCollection[index]: index %d out of range", keynum);
632         return NULL;
633 }
634
635 static PyObject *bpy_bmlayercollection_subscript_slice(BPy_BMLayerCollection *self, Py_ssize_t start, Py_ssize_t stop)
636 {
637         Py_ssize_t len = bpy_bmlayercollection_length(self);
638         int count = 0;
639
640         PyObject *tuple;
641
642         BPY_BM_CHECK_OBJ(self);
643
644         if (start >= len) start = len - 1;
645         if (stop  >= len) stop  = len - 1;
646
647         tuple = PyTuple_New(stop - start);
648
649         for (count = start; count < stop; count++) {
650                 PyTuple_SET_ITEM(tuple, count - start, BPy_BMLayerItem_CreatePyObject(self->bm, self->htype, self->type, count));
651         }
652
653         return tuple;
654 }
655
656 static PyObject *bpy_bmlayercollection_subscript(BPy_BMLayerCollection *self, PyObject *key)
657 {
658         /* don't need error check here */
659         if (PyUnicode_Check(key)) {
660                 return bpy_bmlayercollection_subscript_str(self, _PyUnicode_AsString(key));
661         }
662         else if (PyIndex_Check(key)) {
663                 Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
664                 if (i == -1 && PyErr_Occurred())
665                         return NULL;
666                 return bpy_bmlayercollection_subscript_int(self, i);
667         }
668         else if (PySlice_Check(key)) {
669                 PySliceObject *key_slice = (PySliceObject *)key;
670                 Py_ssize_t step = 1;
671
672                 if (key_slice->step != Py_None && !_PyEval_SliceIndex(key, &step)) {
673                         return NULL;
674                 }
675                 else if (step != 1) {
676                         PyErr_SetString(PyExc_TypeError,
677                                         "BMLayerCollection[slice]: slice steps not supported");
678                         return NULL;
679                 }
680                 else if (key_slice->start == Py_None && key_slice->stop == Py_None) {
681                         return bpy_bmlayercollection_subscript_slice(self, 0, PY_SSIZE_T_MAX);
682                 }
683                 else {
684                         Py_ssize_t start = 0, stop = PY_SSIZE_T_MAX;
685
686                         /* avoid PySlice_GetIndicesEx because it needs to know the length ahead of time. */
687                         if (key_slice->start != Py_None && !_PyEval_SliceIndex(key_slice->start, &start)) return NULL;
688                         if (key_slice->stop != Py_None && !_PyEval_SliceIndex(key_slice->stop, &stop))    return NULL;
689
690                         if (start < 0 || stop < 0) {
691                                 /* only get the length for negative values */
692                                 Py_ssize_t len = bpy_bmlayercollection_length(self);
693                                 if (start < 0) start += len;
694                                 if (stop  < 0) stop  += len;
695                         }
696
697                         if (stop - start <= 0) {
698                                 return PyTuple_New(0);
699                         }
700                         else {
701                                 return bpy_bmlayercollection_subscript_slice(self, start, stop);
702                         }
703                 }
704         }
705         else {
706                 PyErr_SetString(PyExc_AttributeError,
707                                 "BMLayerCollection[key]: invalid key, key must be an int");
708                 return NULL;
709         }
710 }
711
712 static int bpy_bmlayercollection_contains(BPy_BMLayerCollection *self, PyObject *value)
713 {
714         const char *keyname = _PyUnicode_AsString(value);
715         CustomData *data;
716         int index;
717
718         BPY_BM_CHECK_INT(self);
719
720         if (keyname == NULL) {
721                 PyErr_SetString(PyExc_TypeError,
722                                 "BMLayerCollection.__contains__: expected a string");
723                 return -1;
724         }
725
726         data = bpy_bm_customdata_get(self->bm, self->htype);
727         index = CustomData_get_named_layer_index(data, self->type, keyname);
728
729         return (index != -1) ? 1 : 0;
730 }
731
732 static PySequenceMethods bpy_bmlayercollection_as_sequence = {
733         (lenfunc)bpy_bmlayercollection_length,       /* sq_length */
734         NULL,                                        /* sq_concat */
735         NULL,                                        /* sq_repeat */
736         (ssizeargfunc)bpy_bmlayercollection_subscript_int, /* sq_item */ /* Only set this so PySequence_Check() returns True */
737         NULL,                                        /* sq_slice */
738         (ssizeobjargproc)NULL,                       /* sq_ass_item */
739         NULL,                                        /* *was* sq_ass_slice */
740         (objobjproc)bpy_bmlayercollection_contains,  /* sq_contains */
741         (binaryfunc) NULL,                           /* sq_inplace_concat */
742         (ssizeargfunc) NULL,                         /* sq_inplace_repeat */
743 };
744
745 static PyMappingMethods bpy_bmlayercollection_as_mapping = {
746         (lenfunc)bpy_bmlayercollection_length,       /* mp_length */
747         (binaryfunc)bpy_bmlayercollection_subscript, /* mp_subscript */
748         (objobjargproc)NULL,                         /* mp_ass_subscript */
749 };
750
751 /* Iterator
752  * -------- */
753
754 static PyObject *bpy_bmlayercollection_iter(BPy_BMLayerCollection *self)
755 {
756         /* fake it with a list iterator */
757         PyObject *ret;
758         PyObject *iter = NULL;
759
760         BPY_BM_CHECK_OBJ(self);
761
762         ret = bpy_bmlayercollection_subscript_slice(self, 0, PY_SSIZE_T_MIN);
763
764         if (ret) {
765                 iter = PyObject_GetIter(ret);
766                 Py_DECREF(ret);
767         }
768
769         return iter;
770 }
771
772 PyDoc_STRVAR(bpy_bmlayeraccess_type_doc,
773 "Exposes custom-data layer attributes."
774 );
775
776 PyDoc_STRVAR(bpy_bmlayercollection_type_doc,
777 "Gives access to a collection of custom-data layers of the same type and behaves like python dictionaries, "
778 "except for the ability to do list like index access."
779 );
780
781 PyDoc_STRVAR(bpy_bmlayeritem_type_doc,
782 "Exposes a single custom data layer, "
783 "their main purpose is for use as item accessors to custom-data when used with vert/edge/face/loop data."
784 );
785
786
787 PyTypeObject BPy_BMLayerAccessVert_Type; /* bm.verts.layers */
788 PyTypeObject BPy_BMLayerAccessEdge_Type; /* bm.edges.layers */
789 PyTypeObject BPy_BMLayerAccessFace_Type; /* bm.faces.layers */
790 PyTypeObject BPy_BMLayerAccessLoop_Type; /* bm.loops.layers */
791 PyTypeObject BPy_BMLayerCollection_Type; /* bm.loops.layers.uv */
792 PyTypeObject BPy_BMLayerItem_Type;       /* bm.loops.layers.uv["UVMap"] */
793
794
795 PyObject *BPy_BMLayerAccess_CreatePyObject(BMesh *bm, const char htype)
796 {
797         BPy_BMLayerAccess *self;
798         PyTypeObject *type;
799
800         switch (htype) {
801                 case BM_VERT:  type = &BPy_BMLayerAccessVert_Type;  break;
802                 case BM_EDGE:  type = &BPy_BMLayerAccessEdge_Type;  break;
803                 case BM_FACE:  type = &BPy_BMLayerAccessFace_Type;  break;
804                 case BM_LOOP:  type = &BPy_BMLayerAccessLoop_Type;  break;
805                 default:
806                 {
807                         BLI_assert(0);
808                         type = NULL;
809                         break;
810                 }
811         }
812
813         self = PyObject_New(BPy_BMLayerAccess, type);
814         self->bm = bm;
815         self->htype = htype;
816         return (PyObject *)self;
817 }
818
819 PyObject *BPy_BMLayerCollection_CreatePyObject(BMesh *bm, const char htype, int type)
820 {
821         BPy_BMLayerCollection *self = PyObject_New(BPy_BMLayerCollection, &BPy_BMLayerCollection_Type);
822         self->bm = bm;
823         self->htype = htype;
824         self->type = type;
825         return (PyObject *)self;
826 }
827
828 PyObject *BPy_BMLayerItem_CreatePyObject(BMesh *bm, const char htype, int type, int index)
829 {
830         BPy_BMLayerItem *self = PyObject_New(BPy_BMLayerItem, &BPy_BMLayerItem_Type);
831         self->bm = bm;
832         self->htype = htype;
833         self->type = type;
834         self->index = index;
835         return (PyObject *)self;
836 }
837
838
839 void BPy_BM_init_types_customdata(void)
840 {
841         BPy_BMLayerAccessVert_Type.tp_basicsize = sizeof(BPy_BMLayerAccess);
842         BPy_BMLayerAccessEdge_Type.tp_basicsize = sizeof(BPy_BMLayerAccess);
843         BPy_BMLayerAccessFace_Type.tp_basicsize = sizeof(BPy_BMLayerAccess);
844         BPy_BMLayerAccessLoop_Type.tp_basicsize = sizeof(BPy_BMLayerAccess);
845         BPy_BMLayerCollection_Type.tp_basicsize = sizeof(BPy_BMLayerCollection);
846         BPy_BMLayerItem_Type.tp_basicsize       = sizeof(BPy_BMLayerItem);
847
848         BPy_BMLayerAccessVert_Type.tp_name = "BMLayerAccessVert";
849         BPy_BMLayerAccessEdge_Type.tp_name = "BMLayerAccessEdge";
850         BPy_BMLayerAccessFace_Type.tp_name = "BMLayerAccessFace";
851         BPy_BMLayerAccessLoop_Type.tp_name = "BMLayerAccessLoop";
852         BPy_BMLayerCollection_Type.tp_name = "BMLayerCollection";
853         BPy_BMLayerItem_Type.tp_name       = "BMLayerItem";
854
855         /* todo */
856         BPy_BMLayerAccessVert_Type.tp_doc = bpy_bmlayeraccess_type_doc;
857         BPy_BMLayerAccessEdge_Type.tp_doc = bpy_bmlayeraccess_type_doc;
858         BPy_BMLayerAccessFace_Type.tp_doc = bpy_bmlayeraccess_type_doc;
859         BPy_BMLayerAccessLoop_Type.tp_doc = bpy_bmlayeraccess_type_doc;
860         BPy_BMLayerCollection_Type.tp_doc = bpy_bmlayercollection_type_doc;
861         BPy_BMLayerItem_Type.tp_doc       = bpy_bmlayeritem_type_doc;
862
863         BPy_BMLayerAccessVert_Type.tp_repr = (reprfunc)NULL;
864         BPy_BMLayerAccessEdge_Type.tp_repr = (reprfunc)NULL;
865         BPy_BMLayerAccessFace_Type.tp_repr = (reprfunc)NULL;
866         BPy_BMLayerAccessLoop_Type.tp_repr = (reprfunc)NULL;
867         BPy_BMLayerCollection_Type.tp_repr = (reprfunc)NULL;
868         BPy_BMLayerItem_Type.tp_repr = (reprfunc)NULL;
869
870         BPy_BMLayerAccessVert_Type.tp_getset = bpy_bmlayeraccess_vert_getseters;
871         BPy_BMLayerAccessEdge_Type.tp_getset = bpy_bmlayeraccess_edge_getseters;
872         BPy_BMLayerAccessFace_Type.tp_getset = bpy_bmlayeraccess_face_getseters;
873         BPy_BMLayerAccessLoop_Type.tp_getset = bpy_bmlayeraccess_loop_getseters;
874         BPy_BMLayerCollection_Type.tp_getset = bpy_bmlayercollection_getseters;
875         BPy_BMLayerItem_Type.tp_getset       = bpy_bmlayeritem_getseters;
876
877
878 //      BPy_BMLayerAccess_Type.tp_methods     = bpy_bmeditselseq_methods;
879         BPy_BMLayerCollection_Type.tp_methods = bpy_bmelemseq_methods;
880         BPy_BMLayerItem_Type.tp_methods       = bpy_bmlayeritem_methods;
881
882         BPy_BMLayerCollection_Type.tp_as_sequence = &bpy_bmlayercollection_as_sequence;
883
884         BPy_BMLayerCollection_Type.tp_as_mapping = &bpy_bmlayercollection_as_mapping;
885
886         BPy_BMLayerCollection_Type.tp_iter = (getiterfunc)bpy_bmlayercollection_iter;
887
888         BPy_BMLayerAccessVert_Type.tp_dealloc = NULL;
889         BPy_BMLayerAccessEdge_Type.tp_dealloc = NULL;
890         BPy_BMLayerAccessFace_Type.tp_dealloc = NULL;
891         BPy_BMLayerAccessLoop_Type.tp_dealloc = NULL;
892         BPy_BMLayerCollection_Type.tp_dealloc = NULL;
893         BPy_BMLayerItem_Type.tp_dealloc       = NULL;
894
895
896
897         BPy_BMLayerAccessVert_Type.tp_flags = Py_TPFLAGS_DEFAULT;
898         BPy_BMLayerAccessEdge_Type.tp_flags = Py_TPFLAGS_DEFAULT;
899         BPy_BMLayerAccessFace_Type.tp_flags = Py_TPFLAGS_DEFAULT;
900         BPy_BMLayerAccessLoop_Type.tp_flags = Py_TPFLAGS_DEFAULT;
901         BPy_BMLayerCollection_Type.tp_flags = Py_TPFLAGS_DEFAULT;
902         BPy_BMLayerItem_Type.tp_flags       = Py_TPFLAGS_DEFAULT;
903
904         PyType_Ready(&BPy_BMLayerAccessVert_Type);
905         PyType_Ready(&BPy_BMLayerAccessEdge_Type);
906         PyType_Ready(&BPy_BMLayerAccessFace_Type);
907         PyType_Ready(&BPy_BMLayerAccessLoop_Type);
908         PyType_Ready(&BPy_BMLayerCollection_Type);
909         PyType_Ready(&BPy_BMLayerItem_Type);
910 }
911
912
913 /* Per Element Get/Set
914  * ******************* */
915
916 /**
917  * helper function for get/set, NULL return means the error is set
918  */
919 static void *bpy_bmlayeritem_ptr_get(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer)
920 {
921         void *value;
922         BMElem *ele = py_ele->ele;
923         CustomData *data;
924
925         /* error checking */
926         if (UNLIKELY(!BPy_BMLayerItem_Check(py_layer))) {
927                 PyErr_SetString(PyExc_AttributeError,
928                                 "BMElem[key]: invalid key, must be a BMLayerItem");
929                 return NULL;
930         }
931         else if (UNLIKELY(py_ele->bm != py_layer->bm)) {
932                 PyErr_SetString(PyExc_ValueError,
933                                 "BMElem[layer]: layer is from another mesh");
934                 return NULL;
935         }
936         else if (UNLIKELY(ele->head.htype != py_layer->htype)) {
937                 char namestr_1[32], namestr_2[32];
938                 PyErr_Format(PyExc_ValueError,
939                              "Layer/Element type mismatch, expected %.200s got layer type %.200s",
940                              BPy_BMElem_StringFromHType_ex(ele->head.htype, namestr_1),
941                              BPy_BMElem_StringFromHType_ex(py_layer->htype, namestr_2));
942                 return NULL;
943         }
944
945         data = bpy_bm_customdata_get(py_layer->bm, py_layer->htype);
946
947         value = CustomData_bmesh_get_n(data, ele->head.data, py_layer->type, py_layer->index);
948
949         if (UNLIKELY(value == NULL)) {
950                 /* this should be fairly unlikely but possible if layers move about after we get them */
951                 PyErr_SetString(PyExc_KeyError,
952                                 "BMElem[key]: layer not found");
953                 return NULL;
954         }
955         else {
956                 return value;
957         }
958 }
959
960
961 /**
962  *\brief BMElem.__getitem__()
963  *
964  * assume all error checks are done, eg:
965  *
966  *     uv = vert[uv_layer]
967  */
968 PyObject *BPy_BMLayerItem_GetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer)
969 {
970         void *value = bpy_bmlayeritem_ptr_get(py_ele, py_layer);
971         PyObject *ret;
972
973         if (UNLIKELY(value == NULL)) {
974                 return NULL;
975         }
976
977         switch (py_layer->type) {
978                 case CD_MDEFORMVERT:
979                 {
980                         ret = BPy_BMDeformVert_CreatePyObject(value);
981                         break;
982                 }
983                 case CD_PROP_FLT:
984                 case CD_PAINT_MASK:
985                 {
986                         ret = PyFloat_FromDouble(*(float *)value);
987                         break;
988                 }
989                 case CD_PROP_INT:
990                 case CD_FACEMAP:
991                 {
992                         ret = PyLong_FromLong(*(int *)value);
993                         break;
994                 }
995                 case CD_PROP_STR:
996                 {
997                         MStringProperty *mstring = value;
998                         ret = PyBytes_FromStringAndSize(mstring->s, mstring->s_len);
999                         break;
1000                 }
1001                 case CD_MLOOPUV:
1002                 {
1003                         ret = BPy_BMLoopUV_CreatePyObject(value);
1004                         break;
1005                 }
1006                 case CD_MLOOPCOL:
1007                 {
1008                         ret = BPy_BMLoopColor_CreatePyObject(value);
1009                         break;
1010                 }
1011                 case CD_SHAPEKEY:
1012                 {
1013                         ret = Vector_CreatePyObject_wrap((float *)value, 3, NULL);
1014                         break;
1015                 }
1016                 case CD_BWEIGHT:
1017                 {
1018                         ret = PyFloat_FromDouble(*(float *)value);
1019                         break;
1020                 }
1021                 case CD_CREASE:
1022                 {
1023                         ret = PyFloat_FromDouble(*(float *)value);
1024                         break;
1025                 }
1026                 case CD_MVERT_SKIN:
1027                 {
1028                         ret = BPy_BMVertSkin_CreatePyObject(value);
1029                         break;
1030                 }
1031                 default:
1032                 {
1033                         ret = Py_NotImplemented; /* TODO */
1034                         Py_INCREF(ret);
1035                         break;
1036                 }
1037         }
1038
1039         return ret;
1040 }
1041
1042 int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObject *py_value)
1043 {
1044         int ret = 0;
1045         void *value = bpy_bmlayeritem_ptr_get(py_ele, py_layer);
1046
1047         if (UNLIKELY(value == NULL)) {
1048                 return -1;
1049         }
1050
1051         switch (py_layer->type) {
1052                 case CD_MDEFORMVERT:
1053                 {
1054                         ret = BPy_BMDeformVert_AssignPyObject(value, py_value);
1055                         break;
1056                 }
1057                 case CD_PROP_FLT:
1058                 case CD_PAINT_MASK:
1059                 {
1060                         float tmp_val = PyFloat_AsDouble(py_value);
1061                         if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
1062                                 PyErr_Format(PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
1063                                 ret = -1;
1064                         }
1065                         else {
1066                                 *(float *)value = tmp_val;
1067                         }
1068                         break;
1069                 }
1070                 case CD_PROP_INT:
1071                 case CD_FACEMAP:
1072                 {
1073                         int tmp_val = PyLong_AsLong(py_value);
1074                         if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
1075                                 PyErr_Format(PyExc_TypeError, "expected an int, not a %.200s", Py_TYPE(py_value)->tp_name);
1076                                 ret = -1;
1077                         }
1078                         else {
1079                                 *(int *)value = tmp_val;
1080                         }
1081                         break;
1082                 }
1083                 case CD_PROP_STR:
1084                 {
1085                         MStringProperty *mstring = value;
1086                         char *tmp_val;
1087                         Py_ssize_t tmp_val_len;
1088                         if (UNLIKELY(PyBytes_AsStringAndSize(py_value, &tmp_val, &tmp_val_len) == -1)) {
1089                                 PyErr_Format(PyExc_TypeError, "expected bytes, not a %.200s", Py_TYPE(py_value)->tp_name);
1090                                 ret = -1;
1091                         }
1092                         else {
1093                                 if (tmp_val_len > sizeof(mstring->s))
1094                                         tmp_val_len = sizeof(mstring->s);
1095                                 memcpy(mstring->s, tmp_val, tmp_val_len);
1096                                 mstring->s_len = tmp_val_len;
1097                         }
1098                         break;
1099                 }
1100                 case CD_MLOOPUV:
1101                 {
1102                         ret = BPy_BMLoopUV_AssignPyObject(value, py_value);
1103                         break;
1104                 }
1105                 case CD_MLOOPCOL:
1106                 {
1107                         ret = BPy_BMLoopColor_AssignPyObject(value, py_value);
1108                         break;
1109                 }
1110                 case CD_SHAPEKEY:
1111                 {
1112                         float tmp_val[3];
1113                         if (UNLIKELY(mathutils_array_parse(tmp_val, 3, 3, py_value, "BMVert[shape] = value") == -1)) {
1114                                 ret = -1;
1115                         }
1116                         else {
1117                                 copy_v3_v3((float *)value, tmp_val);
1118                         }
1119                         break;
1120                 }
1121                 case CD_BWEIGHT:
1122                 {
1123                         float tmp_val = PyFloat_AsDouble(py_value);
1124                         if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
1125                                 PyErr_Format(PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
1126                                 ret = -1;
1127                         }
1128                         else {
1129                                 *(float *)value = CLAMPIS(tmp_val, 0.0f, 1.0f);
1130                         }
1131                         break;
1132                 }
1133                 case CD_CREASE:
1134                 {
1135                         float tmp_val = PyFloat_AsDouble(py_value);
1136                         if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
1137                                 PyErr_Format(PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
1138                                 ret = -1;
1139                         }
1140                         else {
1141                                 *(float *)value = CLAMPIS(tmp_val, 0.0f, 1.0f);
1142                         }
1143                         break;
1144                 }
1145                 case CD_MVERT_SKIN:
1146                 {
1147                         ret = BPy_BMVertSkin_AssignPyObject(value, py_value);
1148                         break;
1149                 }
1150                 default:
1151                 {
1152                         PyErr_SetString(PyExc_AttributeError, "readonly / unsupported type");
1153                         ret = -1;
1154                         break;
1155                 }
1156         }
1157
1158         return ret;
1159 }