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