bmesh py api:
authorCampbell Barton <ideasman42@gmail.com>
Fri, 16 Mar 2012 08:26:22 +0000 (08:26 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 16 Mar 2012 08:26:22 +0000 (08:26 +0000)
initial support for editing bmesh customdata per vert/edge/face/loop

shapes, crease, bevel weights working, missing UVs and Vertex Colors still.

source/blender/bmesh/bmesh_error.h
source/blender/python/bmesh/bmesh_py_types.c
source/blender/python/bmesh/bmesh_py_types.h
source/blender/python/bmesh/bmesh_py_types_customdata.c
source/blender/python/bmesh/bmesh_py_types_customdata.h

index b88b1ff890fdc279a0631e2ee2c63d597c894701..84065fe88f6841783452a2489a57d735420886ca 100644 (file)
@@ -49,7 +49,7 @@ void BMO_error_clear(BMesh *bm);
  * errorcode is either the errorcode, or BMERR_ALL for any
  * error.*/
 
-/* not yet implimented.
+/* not yet implemented.
  * int BMO_error_catch_op(BMesh *bm, BMOperator *catchop, int errorcode, char **msg);
  */
 
index d9344c3b6908d2b719f7590224210cfb8cc5bfcf..8b3a32ea9233f1872dc0ff250d100c6fbc539812 100644 (file)
@@ -2097,6 +2097,9 @@ static struct PyMethodDef bpy_bmelemseq_methods[] = {
 /* Sequences
  * ========= */
 
+/* BMElemSeq / Iter
+ * ---------------- */
+
 static PyTypeObject *bpy_bm_itype_as_pytype(const char itype)
 {
        /* should cover all types */
@@ -2305,6 +2308,23 @@ static int bpy_bmelemseq_contains(BPy_BMElemSeq *self, PyObject *value)
        return 0;
 }
 
+/* BMElem (customdata)
+ * ------------------- */
+
+static PyObject *bpy_bmelem_subscript(BPy_BMElem *self, BPy_BMLayerItem *key)
+{
+       BPY_BM_CHECK_OBJ(self);
+
+       return BPy_BMLayerItem_GetItem(self, key);
+}
+
+static int bpy_bmelem_ass_subscript(BPy_BMElem *self, BPy_BMLayerItem *key, PyObject *value)
+{
+       BPY_BM_CHECK_INT(self);
+
+       return BPy_BMLayerItem_SetItem(self, key, value);
+}
+
 static PySequenceMethods bpy_bmelemseq_as_sequence = {
     (lenfunc)bpy_bmelemseq_length,                  /* sq_length */
     NULL,                                        /* sq_concat */
@@ -2324,6 +2344,13 @@ static PyMappingMethods bpy_bmelemseq_as_mapping = {
     (objobjargproc)NULL,                         /* mp_ass_subscript */
 };
 
+/* for customdata access */
+static PyMappingMethods bpy_bm_elem_as_mapping = {
+    (lenfunc)NULL,                           /* mp_length */ /* keep this empty, messes up 'if elem: ...' test */
+    (binaryfunc)bpy_bmelem_subscript,        /* mp_subscript */
+    (objobjargproc)bpy_bmelem_ass_subscript, /* mp_ass_subscript */
+};
+
 /* Iterator
  * -------- */
 
@@ -2613,6 +2640,10 @@ void BPy_BM_init_types(void)
 
        BPy_BMElemSeq_Type.tp_as_sequence = &bpy_bmelemseq_as_sequence;
 
+       BPy_BMVert_Type.tp_as_mapping    = &bpy_bm_elem_as_mapping;
+       BPy_BMEdge_Type.tp_as_mapping    = &bpy_bm_elem_as_mapping;
+       BPy_BMFace_Type.tp_as_mapping    = &bpy_bm_elem_as_mapping;
+       BPy_BMLoop_Type.tp_as_mapping    = &bpy_bm_elem_as_mapping;
        BPy_BMElemSeq_Type.tp_as_mapping = &bpy_bmelemseq_as_mapping;
 
        BPy_BMElemSeq_Type.tp_iter = (getiterfunc)bpy_bmelemseq_iter;
@@ -3015,10 +3046,9 @@ int BPy_BMElem_CheckHType(PyTypeObject *type, const char htype)
  *
  * \return a sting like '(BMVert/BMEdge/BMFace/BMLoop)'
  */
-char *BPy_BMElem_StringFromHType(const char htype)
+char *BPy_BMElem_StringFromHType_ex(const char htype, char ret[32])
 {
        /* zero to ensure string is always NULL terminated */
-       static char ret[32];
        char *ret_ptr = ret;
        if (htype & BM_VERT) ret_ptr += sprintf(ret_ptr, "/%s", BPy_BMVert_Type.tp_name);
        if (htype & BM_EDGE) ret_ptr += sprintf(ret_ptr, "/%s", BPy_BMEdge_Type.tp_name);
@@ -3028,3 +3058,9 @@ char *BPy_BMElem_StringFromHType(const char htype)
        *ret_ptr = ')';
        return ret;
 }
+char *BPy_BMElem_StringFromHType(const char htype)
+{
+       /* zero to ensure string is always NULL terminated */
+       static char ret[32];
+       return BPy_BMElem_StringFromHType_ex(htype, ret);
+}
index 452c8b2103fe9240f06d27be3cf92cd3e495c723..cb4646330a7dc42d5d2a0b715a63b458574b748c 100644 (file)
@@ -145,6 +145,7 @@ void *BPy_BMElem_PySeq_As_Array(BMesh **r_bm, PyObject *seq, Py_ssize_t min, Py_
 
 PyObject *BPy_BMElem_Array_As_Tuple(BMesh *bm, BMHeader **elem, Py_ssize_t elem_len);
 int       BPy_BMElem_CheckHType(PyTypeObject *type, const char htype);
+char     *BPy_BMElem_StringFromHType_ex(const char htype, char ret[32]);
 char     *BPy_BMElem_StringFromHType(const char htype);
 
 
index 4b53b6e11d0b4bf3b0cb3ec123d704ddb5512c4c..a44cc78088ded6ac09a9725086f1979640efd8b4 100644 (file)
 
 #include <Python.h>
 
+#include "BLI_string.h"
+#include "BLI_math_vector.h"
+
 #include "bmesh.h"
 
 #include "bmesh_py_types.h"
 #include "bmesh_py_types_customdata.h"
 
+#include "../mathutils/mathutils.h"
+
 #include "BKE_customdata.h"
 
+#include "DNA_meshdata_types.h"
+
 static CustomData *bpy_bm_customdata_get(BMesh *bm, char htype)
 {
        switch (htype) {
@@ -95,7 +102,7 @@ static PyGetSetDef bpy_bmlayeraccess_getseters[] = {
     {(char *)"color", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_MLOOPCOL},
 
     {(char *)"shape",        (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_SHAPEKEY},
-    {(char *)"bevel_weight", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_SHAPEKEY},
+    {(char *)"bevel_weight", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_BWEIGHT},
     {(char *)"crease",       (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)NULL, (void *)CD_CREASE},
 
     {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
@@ -545,3 +552,252 @@ void BPy_BM_init_types_customdata(void)
        PyType_Ready(&BPy_BMLayerCollection_Type);
        PyType_Ready(&BPy_BMLayerItem_Type);
 }
+
+
+/* Per Element Get/Set
+ * ******************* */
+
+/**
+ * helper function for get/set, NULL return means the error is set
+*/
+static void *bpy_bmlayeritem_ptr_get(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer)
+{
+       void *value;
+       BMElem *ele = py_ele->ele;
+       CustomData *data;
+
+       /* error checking */
+       if (UNLIKELY(!BPy_BMLayerItem_Check(py_layer))) {
+               PyErr_SetString(PyExc_AttributeError,
+                               "BMElem[key]: invalid key, must be a BMLayerItem");
+               return NULL;
+       }
+       else if (UNLIKELY(py_ele->bm != py_layer->bm)) {
+               PyErr_SetString(PyExc_ValueError,
+                               "BMElem[layer]: layer is from another mesh");
+               return NULL;
+       }
+       else if (UNLIKELY(ele->head.htype != py_layer->htype)) {
+               char namestr_1[32], namestr_2[32];
+               PyErr_Format(PyExc_ValueError,
+                            "Layer/Element type mismatch, expected %.200s got layer type %.200s",
+                            BPy_BMElem_StringFromHType_ex(ele->head.htype, namestr_1),
+                            BPy_BMElem_StringFromHType_ex(py_layer->htype, namestr_2));
+               return NULL;
+       }
+
+       data = bpy_bm_customdata_get(py_layer->bm, py_layer->htype);
+
+       value = CustomData_bmesh_get_n(data, ele->head.data, py_layer->type, py_layer->index);
+
+       if (UNLIKELY(value == NULL)) {
+               /* this should be fairly unlikely but possible if layers move about after we get them */
+               PyErr_SetString(PyExc_KeyError,
+                            "BMElem[key]: layer not found");
+               return NULL;
+       }
+       else {
+               return value;
+       }
+}
+
+
+/**
+ *\brief BMElem.__getitem__()
+ *
+ * assume all error checks are done, eg:
+ *
+ *     uv = vert[uv_layer]
+ */
+PyObject *BPy_BMLayerItem_GetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer)
+{
+       void *value = bpy_bmlayeritem_ptr_get(py_ele, py_layer);
+       PyObject *ret;
+
+       if (UNLIKELY(value == NULL)) {
+               return NULL;
+       }
+
+       switch (py_layer->type) {
+               case CD_MDEFORMVERT:
+               {
+                       ret = Py_NotImplemented; /* TODO */
+                       Py_INCREF(ret);
+                       break;
+               }
+               case CD_PROP_FLT:
+               {
+                       ret = PyFloat_FromDouble(*(float *)value);
+                       break;
+               }
+               case CD_PROP_INT:
+               {
+                       ret = PyLong_FromSsize_t((Py_ssize_t)(*(int *)value));
+                       break;
+               }
+               case CD_PROP_STR:
+               {
+                       MStringProperty *mstring = value;
+                       ret = PyBytes_FromStringAndSize(mstring->s, BLI_strnlen(mstring->s, sizeof(mstring->s)));
+                       break;
+               }
+               case CD_MTEXPOLY:
+               {
+                       ret = Py_NotImplemented; /* TODO */
+                       Py_INCREF(ret);
+                       break;
+               }
+               case CD_MLOOPUV:
+               {
+                       ret = Py_NotImplemented; /* TODO */
+                       Py_INCREF(ret);
+                       break;
+               }
+               case CD_MLOOPCOL:
+               {
+                       ret = Py_NotImplemented; /* TODO */
+                       Py_INCREF(ret);
+                       break;
+               }
+               case CD_SHAPEKEY:
+               {
+                       ret = Vector_CreatePyObject((float *)value, 3, Py_WRAP, NULL);
+                       break;
+               }
+               case CD_BWEIGHT:
+               {
+                       ret = PyFloat_FromDouble(*(float *)value);
+                       break;
+               }
+               case CD_CREASE:
+               {
+                       ret = PyFloat_FromDouble(*(float *)value);
+                       break;
+               }
+               default:
+               {
+                       ret = Py_NotImplemented; /* TODO */
+                       Py_INCREF(ret);
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObject *py_value)
+{
+       int ret = 0;
+       void *value = bpy_bmlayeritem_ptr_get(py_ele, py_layer);
+
+       if (UNLIKELY(value == NULL)) {
+               return -1;
+       }
+
+       switch (py_layer->type) {
+               case CD_MDEFORMVERT:
+               {
+                       PyErr_SetString(PyExc_AttributeError, "readonly"); /* could make this writeable later */
+                       ret = -1;
+                       break;
+               }
+               case CD_PROP_FLT:
+               {
+                       float tmp_val = PyFloat_AsDouble(py_value);
+                       if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
+                               PyErr_Format(PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
+                               ret = -1;
+                       }
+                       else {
+                               *(float *)value = tmp_val;
+                       }
+                       break;
+               }
+               case CD_PROP_INT:
+               {
+                       int tmp_val = PyLong_AsSsize_t(py_value);
+                       if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
+                               PyErr_Format(PyExc_TypeError, "expected an int, not a %.200s", Py_TYPE(py_value)->tp_name);
+                               ret = -1;
+                       }
+                       else {
+                               *(int *)value = tmp_val;
+                       }
+                       break;
+               }
+               case CD_PROP_STR:
+               {
+                       MStringProperty *mstring = value;
+                       const char *tmp_val = PyBytes_AsString(py_value);
+                       if (UNLIKELY(tmp_val == NULL)) {
+                               PyErr_Format(PyExc_TypeError, "expected bytes, not a %.200s", Py_TYPE(py_value)->tp_name);
+                               ret = -1;
+                       }
+                       else {
+                               BLI_strncpy(mstring->s, tmp_val, sizeof(mstring->s));
+                       }
+                       break;
+               }
+               case CD_MTEXPOLY:
+               {
+                       PyErr_SetString(PyExc_AttributeError, "readonly"); /* could make this writeable later */
+                       ret = -1;
+                       break;
+               }
+               case CD_MLOOPUV:
+               {
+                       PyErr_SetString(PyExc_AttributeError, "readonly"); /* could make this writeable later */
+                       ret = -1;
+                       break;
+               }
+               case CD_MLOOPCOL:
+               {
+                       PyErr_SetString(PyExc_AttributeError, "readonly");
+                       ret = -1;
+                       break;
+               }
+               case CD_SHAPEKEY:
+               {
+                       float tmp_val[3];
+                       if (UNLIKELY(mathutils_array_parse(tmp_val, 3, 3, py_value, "BMVert[shape] = value") == -1)) {
+                               ret = -1;
+                       }
+                       else {
+                               copy_v3_v3((float *)value,tmp_val);
+                       }
+                       break;
+               }
+               case CD_BWEIGHT:
+               {
+                       float tmp_val = PyFloat_AsDouble(py_value);
+                       if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
+                               PyErr_Format(PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
+                               ret = -1;
+                       }
+                       else {
+                               *(float *)value = CLAMPIS(tmp_val, 0.0f, 1.0f);
+                       }
+                       break;
+               }
+               case CD_CREASE:
+               {
+                       float tmp_val = PyFloat_AsDouble(py_value);
+                       if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
+                               PyErr_Format(PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name);
+                               ret = -1;
+                       }
+                       else {
+                               *(float *)value = CLAMPIS(tmp_val, 0.0f, 1.0f);
+                       }
+                       break;
+               }
+               default:
+               {
+                       PyErr_SetString(PyExc_AttributeError, "readonly / unsupported type");
+                       ret = -1;
+                       break;
+               }
+       }
+
+       return ret;
+}
index 0de79d7bd21066c2e2d841a38238e8d24b2254e1..6ec947f98fa58011d4fd189aa003f0c106ce489b 100644 (file)
@@ -68,4 +68,9 @@ PyObject *BPy_BMLayerItem_CreatePyObject(BMesh *bm, const char htype, int type,
 
 void BPy_BM_init_types_customdata(void);
 
+/* __getitem__ / __setitem__ */
+PyObject *BPy_BMLayerItem_GetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer);
+int       BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObject *value);
+
+
 #endif /* __BMESH_PY_TYPES_CUSTOMDATA_H__ */