py/bmesh api - add support for single item buffers (odd feature but used quite a...
[blender.git] / source / blender / python / bmesh / bmesh_py_ops.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_ops.c
27  *  \ingroup pybmesh
28  *
29  * This file defines the 'bmesh.ops' module.
30  * Operators from 'opdefines' are wrapped.
31  */
32
33 #include <Python.h>
34
35 #include "BLI_utildefines.h"
36
37 #include "MEM_guardedalloc.h"
38
39 #include "../generic/py_capi_utils.h"
40
41 #include "../mathutils/mathutils.h"
42
43 #include "bmesh.h"
44
45 #include "bmesh_py_ops.h"  /* own include */
46
47 #include "bmesh_py_types.h"
48
49 #include "bmesh_py_utils.h" /* own include */
50
51 static int bpy_bm_op_as_py_error(BMesh *bm)
52 {
53         if (BMO_error_occurred(bm)) {
54                 const char *errmsg;
55                 if (BMO_error_get(bm, &errmsg, NULL)) {
56                         PyErr_Format(PyExc_RuntimeError,
57                                      "bmesh operator: %.200s",
58                                      errmsg);
59                         return -1;
60                 }
61         }
62         return 0;
63 }
64
65 /* bmesh operator 'bmesh.ops.*' callable types
66  * ******************************************* */
67 PyTypeObject bmesh_op_Type;
68
69 typedef struct {
70         PyObject_HEAD /* required python macro   */
71         const char *opname;
72 } BPy_BMeshOpFunc;
73
74 static PyObject *bpy_bmesh_op_CreatePyObject(const char *opname)
75 {
76         BPy_BMeshOpFunc *self = PyObject_New(BPy_BMeshOpFunc, &bmesh_op_Type);
77
78         self->opname = opname;
79
80         return (PyObject *)self;
81 }
82
83 static PyObject *bpy_bmesh_op_repr(BPy_BMeshOpFunc *self)
84 {
85         return PyUnicode_FromFormat("<%.200s bmesh.ops.%.200s()>",
86                                     Py_TYPE(self)->tp_name,
87                                     self->opname);
88 }
89
90
91 static PyObject *pyrna_op_call(BPy_BMeshOpFunc *self, PyObject *args, PyObject *kw)
92 {
93         PyObject *ret;
94         BPy_BMesh *py_bm;
95         BMesh *bm;
96
97         BMOperator bmop;
98
99         if ((PyTuple_GET_SIZE(args) == 1) &&
100             (py_bm = (BPy_BMesh *)PyTuple_GET_ITEM(args, 0)) &&
101             (BPy_BMesh_Check(py_bm))
102                 )
103         {
104                 BPY_BM_CHECK_OBJ(py_bm);
105                 bm = py_bm->bm;
106         }
107         else {
108                 PyErr_SetString(PyExc_TypeError,
109                                 "calling a bmesh operator expects a single BMesh (non keyword) "
110                                 "as the first argument");
111                 return NULL;
112         }
113
114         /* TODO - error check this!, though we do the error check on attribute access */
115         /* TODO - make flags optional */
116         BMO_op_init(bm, &bmop, BMO_FLAG_DEFAULTS, self->opname);
117
118         if (kw && PyDict_Size(kw) > 0) {
119                 /* setup properties, see bpy_rna.c: pyrna_py_to_prop()
120                  * which shares this logic for parsing properties */
121
122                 PyObject *key, *value;
123                 Py_ssize_t pos = 0;
124                 while (PyDict_Next(kw, &pos, &key, &value)) {
125                         const char *slot_name = _PyUnicode_AsString(key);
126                         BMOpSlot *slot = BMO_slot_get(bmop.slots_in, slot_name);
127
128                         if (slot == NULL) {
129                                 PyErr_Format(PyExc_TypeError,
130                                              "%.200s: keyword \"%.200s\" is invalid for this operator",
131                                              self->opname, slot_name);
132                                 return NULL;
133                         }
134
135                         /* now assign the value */
136                         switch (slot->slot_type) {
137                                 case BMO_OP_SLOT_BOOL:
138                                 {
139                                         int param;
140
141                                         param = PyLong_AsLong(value);
142
143                                         if (param < 0) {
144                                                 PyErr_Format(PyExc_TypeError,
145                                                              "%.200s: keyword \"%.200s\" expected True/False or 0/1, not %.200s",
146                                                              self->opname, slot_name, Py_TYPE(value)->tp_name);
147                                                 return NULL;
148                                         }
149                                         else {
150                                                 BMO_SLOT_AS_BOOL(slot) = param;
151                                         }
152
153                                         break;
154                                 }
155                                 case BMO_OP_SLOT_INT:
156                                 {
157                                         int overflow;
158                                         long param = PyLong_AsLongAndOverflow(value, &overflow);
159                                         if (overflow || (param > INT_MAX) || (param < INT_MIN)) {
160                                                 PyErr_Format(PyExc_ValueError,
161                                                              "%.200s: keyword \"%.200s\" value not in 'int' range "
162                                                              "(" STRINGIFY(INT_MIN) ", " STRINGIFY(INT_MAX) ")",
163                                                              self->opname, slot_name, Py_TYPE(value)->tp_name);
164                                                 return NULL;
165                                         }
166                                         else if (param == -1 && PyErr_Occurred()) {
167                                                 PyErr_Format(PyExc_TypeError,
168                                                              "%.200s: keyword \"%.200s\" expected an int, not %.200s",
169                                                              self->opname, slot_name, Py_TYPE(value)->tp_name);
170                                                 return NULL;
171                                         }
172                                         else {
173                                                 BMO_SLOT_AS_INT(slot) = (int)param;
174                                         }
175                                         break;
176                                 }
177                                 case BMO_OP_SLOT_FLT:
178                                 {
179                                         float param = PyFloat_AsDouble(value);
180                                         if (param == -1 && PyErr_Occurred()) {
181                                                 PyErr_Format(PyExc_TypeError,
182                                                              "%.200s: keyword \"%.200s\" expected a float, not %.200s",
183                                                              self->opname, slot_name, Py_TYPE(value)->tp_name);
184                                                 return NULL;
185                                         }
186                                         else {
187                                                 BMO_SLOT_AS_FLOAT(slot) = param;
188                                         }
189                                         break;
190                                 }
191                                 case BMO_OP_SLOT_MAT:
192                                 {
193                                         /* XXX - BMesh operator design is crappy here, operator slot should define matrix size,
194                                          * not the caller! */
195                                         unsigned short size;
196                                         if (!MatrixObject_Check(value)) {
197                                                 PyErr_Format(PyExc_TypeError,
198                                                              "%.200s: keyword \"%.200s\" expected a Matrix, not %.200s",
199                                                              self->opname, slot_name, Py_TYPE(value)->tp_name);
200                                                 return NULL;
201                                         }
202                                         else if (BaseMath_ReadCallback((MatrixObject *)value) == -1) {
203                                                 return NULL;
204                                         }
205                                         else if (((size = ((MatrixObject *)value)->num_col) != ((MatrixObject *)value)->num_row) ||
206                                                  (ELEM(size, 3, 4) == FALSE))
207                                         {
208                                                 PyErr_Format(PyExc_TypeError,
209                                                              "%.200s: keyword \"%.200s\" expected a 3x3 or 4x4 matrix Matrix",
210                                                              self->opname, slot_name);
211                                                 return NULL;
212                                         }
213
214                                         BMO_slot_mat_set(&bmop, bmop.slots_in, slot_name, ((MatrixObject *)value)->matrix, size);
215                                         break;
216                                 }
217                                 case BMO_OP_SLOT_VEC:
218                                 {
219                                         /* passing slot name here is a bit non-descriptive */
220                                         if (mathutils_array_parse(BMO_SLOT_AS_VECTOR(slot), 3, 3, value, slot_name) == -1) {
221                                                 return NULL;
222                                         }
223                                         break;
224                                 }
225                                 case BMO_OP_SLOT_ELEMENT_BUF:
226                                 {
227                                         if (slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) {
228                                                 if (!BPy_BMElem_Check(value) ||
229                                                     !(((BPy_BMElem *)value)->ele->head.htype & slot->slot_subtype.elem))
230                                                 {
231                                                         PyErr_Format(PyExc_TypeError,
232                                                                      "%.200s: keyword \"%.200s\" expected a %.200s not *.200s",
233                                                                      self->opname, slot_name,
234                                                                      BPy_BMElem_StringFromHType(slot->slot_subtype.elem & BM_ALL_NOLOOP),
235                                                                      Py_TYPE(value)->tp_name);
236                                                         return NULL;
237                                                 }
238                                                 else if (((BPy_BMElem *)value)->bm == NULL) {
239                                                         PyErr_Format(PyExc_TypeError,
240                                                                      "%.200s: keyword \"%.200s\" invalidated element",
241                                                                      self->opname, slot_name);
242                                                         return NULL;
243                                                 }
244
245                                                 BMO_slot_buffer_from_single(&bmop, slot, &((BPy_BMElem *)value)->ele->head);
246                                         }
247                                         else {
248                                                 /* there are many ways we could interpret arguments, for now...
249                                                  * - verts/edges/faces from the mesh direct,
250                                                  *   this way the operator takes every item.
251                                                  * - `TODO` a plain python sequence (list) of elements.
252                                                  * - `TODO`  an iterator. eg.
253                                                  *   face.verts
254                                                  * - `TODO`  (type, flag) pair, eg.
255                                                  *   ('VERT', {'TAG'})
256                                                  */
257
258 #define BPY_BM_GENERIC_MESH_TEST(type_string)  \
259         if (((BPy_BMGeneric *)value)->bm != bm) {                                             \
260             PyErr_Format(PyExc_NotImplementedError,                                           \
261                          "%.200s: keyword \"%.200s\" " type_string " are from another bmesh", \
262                          self->opname, slot_name, slot->slot_type);                           \
263             return NULL;                                                                      \
264         } (void)0
265
266 #define BPY_BM_ELEM_TYPE_TEST(type_string)  \
267         if ((slot->slot_subtype.elem & BM_VERT) == 0) { \
268             PyErr_Format(PyExc_TypeError, \
269                          "%.200s: keyword \"%.200s\" expected " \
270                          "a list of %.200s not " type_string, \
271                          self->opname, slot_name, \
272                          BPy_BMElem_StringFromHType(slot->slot_subtype.elem & BM_ALL_NOLOOP)); \
273             return NULL; \
274         } (void)0
275
276                                                 if (BPy_BMVertSeq_Check(value)) {
277                                                         BPY_BM_GENERIC_MESH_TEST("verts");
278                                                         BPY_BM_ELEM_TYPE_TEST("verts");
279
280                                                         BMO_slot_buffer_from_all(bm, &bmop, bmop.slots_in, slot_name, BM_VERT);
281                                                 }
282                                                 else if (BPy_BMEdgeSeq_Check(value)) {
283                                                         BPY_BM_GENERIC_MESH_TEST("edges");
284                                                         BPY_BM_ELEM_TYPE_TEST("edges");
285                                                         BMO_slot_buffer_from_all(bm, &bmop, bmop.slots_in, slot_name, BM_EDGE);
286                                                 }
287                                                 else if (BPy_BMFaceSeq_Check(value)) {
288                                                         BPY_BM_GENERIC_MESH_TEST("faces");
289                                                         BPY_BM_ELEM_TYPE_TEST("faces");
290                                                         BMO_slot_buffer_from_all(bm, &bmop, bmop.slots_in, slot_name, BM_FACE);
291                                                 }
292
293 #undef BPY_BM_ELEM_TYPE_TEST
294
295                                                 else if (BPy_BMElemSeq_Check(value)) {
296                                                         BMIter iter;
297                                                         BMHeader *ele;
298                                                         int tot;
299                                                         unsigned int i;
300
301                                                         BPY_BM_GENERIC_MESH_TEST("elements");
302
303                                                         /* this will loop over all elements which is a shame but
304                                                          * we need to know this before alloc */
305                                                         /* calls bpy_bmelemseq_length() */
306                                                         tot = Py_TYPE(value)->tp_as_sequence->sq_length((PyObject *)self);
307
308                                                         BMO_slot_buffer_alloc(&bmop, bmop.slots_in, slot_name, tot);
309
310                                                         i = 0;
311                                                         BM_ITER_BPY_BM_SEQ (ele, &iter, ((BPy_BMElemSeq *)value)) {
312                                                                 slot->data.buf[i] = ele;
313                                                                 i++;
314                                                         }
315                                                 }
316                                                 /* keep this last */
317                                                 else if (PySequence_Check(value)) {
318                                                         BMElem **elem_array = NULL;
319                                                         Py_ssize_t elem_array_len;
320
321                                                         elem_array = BPy_BMElem_PySeq_As_Array(&bm, value, 0, PY_SSIZE_T_MAX,
322                                                                                                &elem_array_len, (slot->slot_subtype.elem & BM_ALL_NOLOOP),
323                                                                                                TRUE, TRUE, slot_name);
324
325                                                         /* error is set above */
326                                                         if (elem_array == NULL) {
327                                                                 return NULL;
328                                                         }
329
330                                                         BMO_slot_buffer_alloc(&bmop, bmop.slots_in, slot_name, elem_array_len);
331                                                         memcpy(slot->data.buf, elem_array, sizeof(void *) * elem_array_len);
332                                                         PyMem_FREE(elem_array);
333                                                 }
334                                                 else {
335                                                         PyErr_Format(PyExc_TypeError,
336                                                                      "%.200s: keyword \"%.200s\" expected "
337                                                                      "a bmesh sequence, list, (htype, flag) pair, not %.200s",
338                                                                      self->opname, slot_name, Py_TYPE(value)->tp_name);
339                                                         return NULL;
340                                                 }
341                                         }
342 #undef BPY_BM_GENERIC_MESH_TEST
343
344                                         break;
345                                 }
346                                 case BMO_OP_SLOT_MAPPING:
347                                 {
348                                         /* first check types */
349                                         if (slot->slot_subtype.map != BMO_OP_SLOT_SUBTYPE_MAP_EMPTY) {
350                                                 if (!PyDict_Check(value)) {
351                                                         PyErr_Format(PyExc_TypeError,
352                                                                      "%.200s: keyword \"%.200s\" expected "
353                                                                      "a dict, not %.200s",
354                                                                      self->opname, slot_name, Py_TYPE(value)->tp_name);
355                                                         return NULL;
356                                                 }
357                                         }
358                                         else {
359                                                 if (!PySet_Check(value)) {
360                                                         PyErr_Format(PyExc_TypeError,
361                                                                      "%.200s: keyword \"%.200s\" expected "
362                                                                      "a set, not %.200s",
363                                                                      self->opname, slot_name, Py_TYPE(value)->tp_name);
364                                                         return NULL;
365                                                 }
366                                         }
367
368                                         switch (slot->slot_subtype.map) {
369
370                                                 /* this could be a static function */
371 #define BPY_BM_MAPPING_KEY_CHECK(arg_key)  \
372                                                 if (!BPy_BMElem_Check(arg_key)) { \
373                                                         PyErr_Format(PyExc_TypeError, \
374                                                                      "%.200s: keyword \"%.200s\" expected " \
375                                                                      "a dict with bmesh element keys, not %.200s", \
376                                                                      self->opname, slot_name, Py_TYPE(arg_key)->tp_name); \
377                                                         return NULL; \
378                                                 } \
379                                                 else if (((BPy_BMGeneric *)arg_key)->bm == NULL) { \
380                                                         PyErr_Format(PyExc_TypeError, \
381                                                                      "%.200s: keyword \"%.200s\" invalidated element key in dict", \
382                                                                      self->opname, slot_name); \
383                                                         return NULL; \
384                                                 } (void)0
385
386
387                                                 case BMO_OP_SLOT_SUBTYPE_MAP_ELEM:
388                                                 {
389                                                         if (PyDict_Size(value) > 0) {
390                                                                 PyObject *arg_key, *arg_value;
391                                                                 Py_ssize_t arg_pos = 0;
392                                                                 while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
393                                                                         /* TODO, check the elements come from the right mesh? */
394                                                                         BPY_BM_MAPPING_KEY_CHECK(arg_key);
395
396                                                                         if (!BPy_BMElem_Check(arg_value)) {
397                                                                                 PyErr_Format(PyExc_TypeError,
398                                                                                              "%.200s: keyword \"%.200s\" expected "
399                                                                                              "a dict with bmesh element values, not %.200s",
400                                                                                              self->opname, slot_name, Py_TYPE(arg_value)->tp_name);
401                                                                                 return NULL;
402                                                                         }
403                                                                         else if (((BPy_BMGeneric *)arg_value)->bm == NULL) {
404                                                                                 PyErr_Format(PyExc_TypeError,
405                                                                                              "%.200s: keyword \"%.200s\" invalidated element value in dict",
406                                                                                              self->opname, slot_name);
407                                                                                 return NULL;
408                                                                         }
409
410                                                                         BMO_slot_map_elem_insert(&bmop, slot,
411                                                                                                  ((BPy_BMElem *)arg_key)->ele, ((BPy_BMElem *)arg_value)->ele);
412                                                                 }
413                                                         }
414                                                         break;
415                                                 }
416                                                 case BMO_OP_SLOT_SUBTYPE_MAP_FLOAT:
417                                                 {
418                                                         if (PyDict_Size(value) > 0) {
419                                                                 PyObject *arg_key, *arg_value;
420                                                                 Py_ssize_t arg_pos = 0;
421                                                                 while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
422                                                                         float value_f;
423                                                                         /* TODO, check the elements come from the right mesh? */
424                                                                         BPY_BM_MAPPING_KEY_CHECK(arg_key);
425                                                                         value_f = PyFloat_AsDouble(arg_value);
426
427                                                                         if (value_f == -1.0f && PyErr_Occurred()) {
428                                                                                 PyErr_Format(PyExc_TypeError,
429                                                                                              "%.200s: keyword \"%.200s\" expected "
430                                                                                              "a dict with float values, not %.200s",
431                                                                                              self->opname, slot_name, Py_TYPE(arg_value)->tp_name);
432                                                                                 return NULL;
433                                                                         }
434
435                                                                         BMO_slot_map_float_insert(&bmop, slot,
436                                                                                                   ((BPy_BMElem *)arg_key)->ele, value_f);
437                                                                 }
438                                                         }
439                                                         break;
440                                                 }
441                                                 case BMO_OP_SLOT_SUBTYPE_MAP_INT:
442                                                 {
443                                                         if (PyDict_Size(value) > 0) {
444                                                                 PyObject *arg_key, *arg_value;
445                                                                 Py_ssize_t arg_pos = 0;
446                                                                 while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
447                                                                         int value_i;
448                                                                         /* TODO, check the elements come from the right mesh? */
449                                                                         BPY_BM_MAPPING_KEY_CHECK(arg_key);
450                                                                         value_i = PyLong_AsLong(arg_value);
451
452                                                                         if (value_i == -1 && PyErr_Occurred()) {
453                                                                                 PyErr_Format(PyExc_TypeError,
454                                                                                              "%.200s: keyword \"%.200s\" expected "
455                                                                                              "a dict with int values, not %.200s",
456                                                                                              self->opname, slot_name, Py_TYPE(arg_value)->tp_name);
457                                                                                 return NULL;
458                                                                         }
459
460                                                                         BMO_slot_map_int_insert(&bmop, slot,
461                                                                                                 ((BPy_BMElem *)arg_key)->ele, value_i);
462                                                                 }
463                                                         }
464                                                         break;
465                                                 }
466                                                 case BMO_OP_SLOT_SUBTYPE_MAP_BOOL:
467                                                 {
468                                                         if (PyDict_Size(value) > 0) {
469                                                                 PyObject *arg_key, *arg_value;
470                                                                 Py_ssize_t arg_pos = 0;
471                                                                 while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
472                                                                         int value_i;
473                                                                         /* TODO, check the elements come from the right mesh? */
474                                                                         BPY_BM_MAPPING_KEY_CHECK(arg_key);
475                                                                         value_i = PyLong_AsLong(arg_value);
476
477                                                                         if (value_i == -1 && PyErr_Occurred()) {
478                                                                                 PyErr_Format(PyExc_TypeError,
479                                                                                              "%.200s: keyword \"%.200s\" expected "
480                                                                                              "a dict with bool values, not %.200s",
481                                                                                              self->opname, slot_name, Py_TYPE(arg_value)->tp_name);
482                                                                                 return NULL;
483                                                                         }
484
485                                                                         BMO_slot_map_bool_insert(&bmop, slot,
486                                                                                                  ((BPy_BMElem *)arg_key)->ele, value_i != 0);
487                                                                 }
488                                                         }
489                                                         break;
490                                                 }
491                                                 case BMO_OP_SLOT_SUBTYPE_MAP_EMPTY:
492                                                 {
493                                                         if (PySet_Size(value) > 0) {
494                                                                 PyObject *arg_key;
495                                                                 Py_ssize_t arg_pos = 0;
496                                                                 Py_ssize_t arg_hash = 0;
497                                                                 while (_PySet_NextEntry(value, &arg_pos, &arg_key, &arg_hash)) {
498                                                                         /* TODO, check the elements come from the right mesh? */
499                                                                         BPY_BM_MAPPING_KEY_CHECK(arg_key);
500
501                                                                         BMO_slot_map_empty_insert(&bmop, slot,
502                                                                                                  ((BPy_BMElem *)arg_key)->ele);
503                                                                 }
504                                                         }
505                                                         break;
506                                                 }
507                                                 case BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL:
508                                                 {
509                                                         /* can't convert from these */
510                                                         PyErr_Format(PyExc_NotImplementedError,
511                                                                      "This arguments mapping subtype %d is not supported", slot->slot_subtype);
512                                                         break;
513                                                 }
514 #undef BPY_BM_MAPPING_KEY_CHECK
515
516                                         }
517                                 }
518                                 default:
519                                         /* TODO --- many others */
520                                         PyErr_Format(PyExc_NotImplementedError,
521                                                      "%.200s: keyword \"%.200s\" type %d not working yet!",
522                                                      self->opname, slot_name, slot->slot_type);
523                                         return NULL;
524                                         break;
525                         }
526                 }
527         }
528
529         BMO_op_exec(bm, &bmop);
530
531         /* from here until the end of the function, no returns, just set 'ret' */
532         if (UNLIKELY(bpy_bm_op_as_py_error(bm) == -1)) {
533                 ret = NULL;  /* exception raised above */
534         }
535         else if (bmop.slots_out[0].slot_name == NULL) {
536                 ret = (Py_INCREF(Py_None), Py_None);
537         }
538         else {
539                 /* build return value */
540                 int i;
541                 ret = PyDict_New();
542
543                 for (i = 0; bmop.slots_out[i].slot_name; i++) {
544                         // BMOpDefine *op_def = opdefines[bmop.type];
545                         // BMOSlotType *slot_type = op_def->slot_types_out[i];
546                         BMOpSlot *slot = &bmop.slots_out[i];
547                         PyObject *item = NULL;
548
549                         /* keep switch in same order as above */
550                         switch (slot->slot_type) {
551                                 case BMO_OP_SLOT_BOOL:
552                                         item = PyBool_FromLong((BMO_SLOT_AS_BOOL(slot)));
553                                         break;
554                                 case BMO_OP_SLOT_INT:
555                                         item = PyLong_FromLong(BMO_SLOT_AS_INT(slot));
556                                         break;
557                                 case BMO_OP_SLOT_FLT:
558                                         item = PyFloat_FromDouble((double)BMO_SLOT_AS_FLOAT(slot));
559                                         break;
560                                 case BMO_OP_SLOT_MAT:
561                                         item = Matrix_CreatePyObject((float *)BMO_SLOT_AS_MATRIX(slot), 4, 4, Py_NEW, NULL);
562                                         break;
563                                 case BMO_OP_SLOT_VEC:
564                                         item = Vector_CreatePyObject(BMO_SLOT_AS_VECTOR(slot), slot->len, Py_NEW, NULL);
565                                         break;
566                                 case BMO_OP_SLOT_PTR:
567                                         BLI_assert(0);  /* currently we don't have any pointer return values in use */
568                                         item = (Py_INCREF(Py_None), Py_None);
569                                         break;
570                                 case BMO_OP_SLOT_ELEMENT_BUF:
571                                 {
572                                         if (slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) {
573                                                 BMHeader *ele = BMO_slot_buffer_get_single(slot);
574                                                 item = ele ? BPy_BMElem_CreatePyObject(bm, ele) : (Py_INCREF(Py_None), Py_None);
575                                         }
576                                         else {
577                                                 const int size = slot->len;
578                                                 void **buffer = BMO_SLOT_AS_BUFFER(slot);
579                                                 int j;
580
581                                                 item = PyList_New(size);
582                                                 for (j = 0; j < size; j++) {
583                                                         BMHeader *ele = buffer[i];
584                                                         PyList_SET_ITEM(item, j, BPy_BMElem_CreatePyObject(bm, ele));
585                                                 }
586                                         }
587                                         break;
588                                 }
589                                 case BMO_OP_SLOT_MAPPING:
590                                 {
591                                         GHash *slot_hash = BMO_SLOT_AS_GHASH(slot);
592                                         GHashIterator hash_iter;
593
594                                         switch (slot->slot_subtype.map) {
595                                                 case BMO_OP_SLOT_SUBTYPE_MAP_ELEM:
596                                                 {
597                                                         item = PyDict_New();
598                                                         if (slot_hash) {
599                                                                 GHASH_ITER (hash_iter, slot_hash) {
600                                                                         BMHeader       *ele_key = BLI_ghashIterator_getKey(&hash_iter);
601                                                                         BMOElemMapping *ele_val = BLI_ghashIterator_getValue(&hash_iter);
602
603                                                                         PyObject *py_key =  BPy_BMElem_CreatePyObject(bm,  ele_key);
604                                                                         PyObject *py_val =  BPy_BMElem_CreatePyObject(bm, *(void **)BMO_OP_SLOT_MAPPING_DATA(ele_val));
605
606                                                                         BLI_assert(slot->slot_subtype.elem & ((BPy_BMElem *)py_val)->ele->head.htype);
607
608                                                                         PyDict_SetItem(ret, py_key, py_val);
609                                                                         Py_DECREF(py_key);
610                                                                         Py_DECREF(py_val);
611                                                                 }
612                                                         }
613                                                         break;
614                                                 }
615                                                 case BMO_OP_SLOT_SUBTYPE_MAP_FLOAT:
616                                                 {
617                                                         item = PyDict_New();
618                                                         if (slot_hash) {
619                                                                 GHASH_ITER (hash_iter, slot_hash) {
620                                                                         BMHeader       *ele_key = BLI_ghashIterator_getKey(&hash_iter);
621                                                                         BMOElemMapping *ele_val = BLI_ghashIterator_getValue(&hash_iter);
622
623                                                                         PyObject *py_key =  BPy_BMElem_CreatePyObject(bm,  ele_key);
624                                                                         PyObject *py_val =  PyFloat_FromDouble(*(float *)BMO_OP_SLOT_MAPPING_DATA(ele_val));
625
626                                                                         PyDict_SetItem(ret, py_key, py_val);
627                                                                         Py_DECREF(py_key);
628                                                                         Py_DECREF(py_val);
629                                                                 }
630                                                         }
631                                                         break;
632                                                 }
633                                                 case BMO_OP_SLOT_SUBTYPE_MAP_INT:
634                                                 {
635                                                         item = PyDict_New();
636                                                         if (slot_hash) {
637                                                                 GHASH_ITER (hash_iter, slot_hash) {
638                                                                         BMHeader       *ele_key = BLI_ghashIterator_getKey(&hash_iter);
639                                                                         BMOElemMapping *ele_val = BLI_ghashIterator_getValue(&hash_iter);
640
641                                                                         PyObject *py_key =  BPy_BMElem_CreatePyObject(bm,  ele_key);
642                                                                         PyObject *py_val =  PyLong_FromLong(*(int *)BMO_OP_SLOT_MAPPING_DATA(ele_val));
643
644                                                                         PyDict_SetItem(ret, py_key, py_val);
645                                                                         Py_DECREF(py_key);
646                                                                         Py_DECREF(py_val);
647                                                                 }
648                                                         }
649                                                         break;
650                                                 }
651                                                 case BMO_OP_SLOT_SUBTYPE_MAP_BOOL:
652                                                 {
653                                                         item = PyDict_New();
654                                                         if (slot_hash) {
655                                                                 GHASH_ITER (hash_iter, slot_hash) {
656                                                                         BMHeader       *ele_key = BLI_ghashIterator_getKey(&hash_iter);
657                                                                         BMOElemMapping *ele_val = BLI_ghashIterator_getValue(&hash_iter);
658
659                                                                         PyObject *py_key =  BPy_BMElem_CreatePyObject(bm,  ele_key);
660                                                                         PyObject *py_val =  PyBool_FromLong(*(int *)BMO_OP_SLOT_MAPPING_DATA(ele_val));
661
662                                                                         PyDict_SetItem(ret, py_key, py_val);
663                                                                         Py_DECREF(py_key);
664                                                                         Py_DECREF(py_val);
665                                                                 }
666                                                         }
667                                                         break;
668                                                 }
669                                                 case BMO_OP_SLOT_SUBTYPE_MAP_EMPTY:
670                                                 {
671                                                         item = PySet_New(NULL);
672                                                         if (slot_hash) {
673                                                                 GHASH_ITER (hash_iter, slot_hash) {
674                                                                         BMHeader       *ele_key = BLI_ghashIterator_getKey(&hash_iter);
675
676                                                                         PyObject *py_key =  BPy_BMElem_CreatePyObject(bm,  ele_key);
677
678                                                                         PySet_Add(item, py_key);
679
680                                                                         Py_DECREF(py_key);
681                                                                 }
682                                                         }
683                                                         break;
684                                                 }
685                                                 case BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL:
686                                                         /* can't convert from these */
687                                                         item = (Py_INCREF(Py_None), Py_None);
688                                                         break;
689                                         }
690                                         break;
691                                 }
692                         }
693                         BLI_assert(item != NULL);
694                         if (item == NULL) {
695                                 item = (Py_INCREF(Py_None), Py_None);
696                         }
697
698 #if 1
699                         /* temp code, strip off '.out' while we keep this convention */
700                         {
701                                 char slot_name_strip[MAX_SLOTNAME];
702                                 char *ch = strchr(slot->slot_name, '.');  /* can't fail! */
703                                 int tot = ch - slot->slot_name;
704                                 BLI_assert(ch != NULL);
705                                 memcpy(slot_name_strip, slot->slot_name, tot);
706                                 slot_name_strip[tot] = '\0';
707                                 PyDict_SetItemString(ret, slot_name_strip, item);
708                         }
709 #else
710                         PyDict_SetItemString(ret, slot->slot_name, item);
711 #endif
712                         Py_DECREF(item);
713                 }
714         }
715
716         BMO_op_finish(bm, &bmop);
717         return ret;
718 }
719
720
721 PyTypeObject bmesh_op_Type = {
722         PyVarObject_HEAD_INIT(NULL, 0)
723         "BMeshOpFunc",              /* tp_name */
724         sizeof(BPy_BMeshOpFunc),    /* tp_basicsize */
725         0,                          /* tp_itemsize */
726         /* methods */
727         NULL,                       /* tp_dealloc */
728         NULL,                       /* printfunc tp_print; */
729         NULL,                       /* getattrfunc tp_getattr; */
730         NULL,                       /* setattrfunc tp_setattr; */
731         NULL,                       /* tp_compare */ /* DEPRECATED in python 3.0! */
732         (reprfunc) bpy_bmesh_op_repr, /* tp_repr */
733
734         /* Method suites for standard classes */
735
736         NULL,                       /* PyNumberMethods *tp_as_number; */
737         NULL,                       /* PySequenceMethods *tp_as_sequence; */
738         NULL,                       /* PyMappingMethods *tp_as_mapping; */
739
740         /* More standard operations (here for binary compatibility) */
741
742         NULL,                       /* hashfunc tp_hash; */
743         (ternaryfunc)pyrna_op_call, /* ternaryfunc tp_call; */
744         NULL,                       /* reprfunc tp_str; */
745
746         /* will only use these if this is a subtype of a py class */
747         NULL,                       /* getattrofunc tp_getattro; */
748         NULL,                       /* setattrofunc tp_setattro; */
749
750         /* Functions to access object as input/output buffer */
751         NULL,                       /* PyBufferProcs *tp_as_buffer; */
752
753         /*** Flags to define presence of optional/expanded features ***/
754         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
755
756         NULL,                       /*  char *tp_doc;  Documentation string */
757         /*** Assigned meaning in release 2.0 ***/
758         /* call function for all accessible objects */
759         NULL,                       /* traverseproc tp_traverse; */
760
761         /* delete references to contained objects */
762         NULL,                       /* inquiry tp_clear; */
763
764         /***  Assigned meaning in release 2.1 ***/
765         /*** rich comparisons ***/
766         NULL,                       /* richcmpfunc tp_richcompare; */
767
768         /***  weak reference enabler ***/
769         0,
770         /*** Added in release 2.2 ***/
771         /*   Iterators */
772         NULL,                       /* getiterfunc tp_iter; */
773         NULL,                       /* iternextfunc tp_iternext; */
774
775         /*** Attribute descriptor and subclassing stuff ***/
776         NULL,                       /* struct PyMethodDef *tp_methods; */
777         NULL,                       /* struct PyMemberDef *tp_members; */
778         NULL,                       /* struct PyGetSetDef *tp_getset; */
779         NULL,                       /* struct _typeobject *tp_base; */
780         NULL,                       /* PyObject *tp_dict; */
781         NULL,                       /* descrgetfunc tp_descr_get; */
782         NULL,                       /* descrsetfunc tp_descr_set; */
783         0,                          /* long tp_dictoffset; */
784         NULL,                       /* initproc tp_init; */
785         NULL,                       /* allocfunc tp_alloc; */
786         NULL,                       /* newfunc tp_new; */
787         /*  Low-level free-memory routine */
788         NULL,                       /* freefunc tp_free;  */
789         /* For PyObject_IS_GC */
790         NULL,                       /* inquiry tp_is_gc;  */
791         NULL,                       /* PyObject *tp_bases; */
792         /* method resolution order */
793         NULL,                       /* PyObject *tp_mro;  */
794         NULL,                       /* PyObject *tp_cache; */
795         NULL,                       /* PyObject *tp_subclasses; */
796         NULL,                       /* PyObject *tp_weaklist; */
797         NULL
798 };
799
800
801 /* bmesh fake module 'bmesh.ops'
802  * ***************************** */
803
804 static PyObject *bpy_bmesh_ops_fakemod_getattro(PyObject *UNUSED(self), PyObject *pyname)
805 {
806         const unsigned int tot = bmo_opdefines_total;
807         unsigned int i;
808         const char *opname = _PyUnicode_AsString(pyname);
809
810         for (i = 0; i < tot; i++) {
811                 if (strcmp(bmo_opdefines[i]->opname, opname) == 0) {
812                         return bpy_bmesh_op_CreatePyObject(opname);
813                 }
814         }
815
816         PyErr_Format(PyExc_AttributeError,
817                      "BMeshOpsModule: operator \"%.200s\" doesn't exist",
818                      opname);
819         return NULL;
820 }
821
822 static PyObject *bpy_bmesh_ops_fakemod_dir(PyObject *UNUSED(self))
823 {
824         const unsigned int tot = bmo_opdefines_total;
825         unsigned int i;
826         PyObject *ret;
827
828         ret = PyList_New(bmo_opdefines_total);
829
830         for (i = 0; i < tot; i++) {
831                 PyList_SET_ITEM(ret, i, PyUnicode_FromString(bmo_opdefines[i]->opname));
832         }
833
834         return ret;
835 }
836
837 static struct PyMethodDef bpy_bmesh_ops_fakemod_methods[] = {
838         {"__dir__", (PyCFunction)bpy_bmesh_ops_fakemod_dir, METH_NOARGS, NULL},
839         {NULL, NULL, 0, NULL}
840 };
841
842 static PyTypeObject bmesh_ops_fakemod_Type = {
843         PyVarObject_HEAD_INIT(NULL, 0)
844         "BMeshOpsModule",           /* tp_name */
845         0,                          /* tp_basicsize */
846         0,                          /* tp_itemsize */
847         /* methods */
848         NULL,                       /* tp_dealloc */
849         NULL,                       /* printfunc tp_print; */
850         NULL,                       /* getattrfunc tp_getattr; */
851         NULL,                       /* setattrfunc tp_setattr; */
852         NULL,                       /* tp_compare */ /* DEPRECATED in python 3.0! */
853         NULL,                       /* tp_repr */
854
855         /* Method suites for standard classes */
856
857         NULL,                       /* PyNumberMethods *tp_as_number; */
858         NULL,                       /* PySequenceMethods *tp_as_sequence; */
859         NULL,                       /* PyMappingMethods *tp_as_mapping; */
860
861         /* More standard operations (here for binary compatibility) */
862
863         NULL,                       /* hashfunc tp_hash; */
864         NULL,                       /* ternaryfunc tp_call; */
865         NULL,                       /* reprfunc tp_str; */
866
867         /* will only use these if this is a subtype of a py class */
868         bpy_bmesh_ops_fakemod_getattro,    /* getattrofunc tp_getattro; */
869         NULL,                       /* setattrofunc tp_setattro; */
870
871         /* Functions to access object as input/output buffer */
872         NULL,                       /* PyBufferProcs *tp_as_buffer; */
873
874         /*** Flags to define presence of optional/expanded features ***/
875         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
876
877         NULL,                       /*  char *tp_doc;  Documentation string */
878         /*** Assigned meaning in release 2.0 ***/
879         /* call function for all accessible objects */
880         NULL,                       /* traverseproc tp_traverse; */
881
882         /* delete references to contained objects */
883         NULL,                       /* inquiry tp_clear; */
884
885         /***  Assigned meaning in release 2.1 ***/
886         /*** rich comparisons ***/
887         NULL, /* subclassed */          /* richcmpfunc tp_richcompare; */
888
889         /***  weak reference enabler ***/
890         0,
891         /*** Added in release 2.2 ***/
892         /*   Iterators */
893         NULL,                       /* getiterfunc tp_iter; */
894         NULL,                       /* iternextfunc tp_iternext; */
895
896         /*** Attribute descriptor and subclassing stuff ***/
897         bpy_bmesh_ops_fakemod_methods,  /* struct PyMethodDef *tp_methods; */
898         NULL,                       /* struct PyMemberDef *tp_members; */
899         NULL,                       /* struct PyGetSetDef *tp_getset; */
900         NULL,                       /* struct _typeobject *tp_base; */
901         NULL,                       /* PyObject *tp_dict; */
902         NULL,                       /* descrgetfunc tp_descr_get; */
903         NULL,                       /* descrsetfunc tp_descr_set; */
904         0,                          /* long tp_dictoffset; */
905         NULL,                       /* initproc tp_init; */
906         NULL,                       /* allocfunc tp_alloc; */
907         NULL,                       /* newfunc tp_new; */
908         /*  Low-level free-memory routine */
909         NULL,                       /* freefunc tp_free;  */
910         /* For PyObject_IS_GC */
911         NULL,                       /* inquiry tp_is_gc;  */
912         NULL,                       /* PyObject *tp_bases; */
913         /* method resolution order */
914         NULL,                       /* PyObject *tp_mro;  */
915         NULL,                       /* PyObject *tp_cache; */
916         NULL,                       /* PyObject *tp_subclasses; */
917         NULL,                       /* PyObject *tp_weaklist; */
918         NULL
919 };
920
921 PyObject *BPyInit_bmesh_ops(void)
922 {
923         PyObject *submodule;
924
925         if (PyType_Ready(&bmesh_ops_fakemod_Type) < 0)
926                 return NULL;
927
928         if (PyType_Ready(&bmesh_op_Type) < 0)
929                 return NULL;
930
931         submodule = PyObject_New(PyObject, &bmesh_ops_fakemod_Type);
932
933         /* prevent further creation of instances */
934         bmesh_ops_fakemod_Type.tp_init = NULL;
935         bmesh_ops_fakemod_Type.tp_new = NULL;
936
937         return submodule;
938 }