2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2012 Blender Foundation.
19 * All rights reserved.
21 * Contributor(s): Campbell Barton
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/python/bmesh/bmesh_py_ops.c
29 * This file defines the 'bmesh.ops' module.
30 * Operators from 'opdefines' are wrapped.
35 #include "BLI_utildefines.h"
37 #include "MEM_guardedalloc.h"
39 #include "../generic/py_capi_utils.h"
41 #include "../mathutils/mathutils.h"
45 #include "bmesh_py_types.h"
47 #include "bmesh_py_utils.h" /* own include */
49 static int bpy_bm_op_as_py_error(BMesh *bm)
51 if (BMO_error_occurred(bm)) {
53 if (BMO_error_get(bm, &errmsg, NULL)) {
54 PyErr_Format(PyExc_RuntimeError,
55 "bmesh operator: %.200s",
63 /* bmesh operator 'bmesh.ops.*' callable types
64 * ******************************************* */
65 PyTypeObject bmesh_op_Type;
68 PyObject_HEAD /* required python macro */
72 PyObject *bpy_bmesh_op_CreatePyObject(const char *opname)
74 BPy_BMeshOpFunc *self = PyObject_New(BPy_BMeshOpFunc, &bmesh_op_Type);
76 self->opname = opname;
78 return (PyObject *)self;
81 static PyObject *bpy_bmesh_op_repr(BPy_BMeshOpFunc *self)
83 return PyUnicode_FromFormat("<%.200s bmesh.ops.%.200s()>",
84 Py_TYPE(self)->tp_name,
89 static PyObject *pyrna_op_call(BPy_BMeshOpFunc *self, PyObject *args, PyObject *kw)
96 if ((PyTuple_GET_SIZE(args) == 1) &&
97 (py_bm = (BPy_BMesh *)PyTuple_GET_ITEM(args, 0)) &&
98 (BPy_BMesh_Check(py_bm))
101 BPY_BM_CHECK_OBJ(py_bm);
105 PyErr_SetString(PyExc_TypeError,
106 "calling a bmesh operator expects a single BMesh (non keyword) "
107 "as the first argument");
111 /* TODO - error check this!, though we do the error check on attribute access */
112 BMO_op_init(bm, &bmop, self->opname);
114 if (kw && PyDict_Size(kw) > 0) {
115 /* setup properties, see bpy_rna.c: pyrna_py_to_prop()
116 * which shares this logic for parsing properties */
118 PyObject *key, *value;
120 while (PyDict_Next(kw, &pos, &key, &value)) {
121 const char *slot_name = _PyUnicode_AsString(key);
122 BMOpSlot *slot = BMO_slot_get(&bmop, slot_name);
125 PyErr_Format(PyExc_TypeError,
126 "%.200s: keyword \"%.200s\" is invalid for this operator",
127 self->opname, slot_name);
131 /* now assign the value */
132 switch (slot->slot_type) {
133 case BMO_OP_SLOT_BOOL:
137 param = PyLong_AsLong(value);
140 PyErr_Format(PyExc_TypeError,
141 "%.200s: keyword \"%.200s\" expected True/False or 0/1, not %.200s",
142 self->opname, slot_name, Py_TYPE(value)->tp_name);
146 slot->data.i = param;
151 case BMO_OP_SLOT_INT:
154 long param = PyLong_AsLongAndOverflow(value, &overflow);
155 if (overflow || (param > INT_MAX) || (param < INT_MIN)) {
156 PyErr_Format(PyExc_ValueError,
157 "%.200s: keyword \"%.200s\" value not in 'int' range "
158 "(" STRINGIFY(INT_MIN) ", " STRINGIFY(INT_MAX) ")",
159 self->opname, slot_name, Py_TYPE(value)->tp_name);
162 else if (param == -1 && PyErr_Occurred()) {
163 PyErr_Format(PyExc_TypeError,
164 "%.200s: keyword \"%.200s\" expected an int, not %.200s",
165 self->opname, slot_name, Py_TYPE(value)->tp_name);
168 slot->data.i = (int)param;
172 case BMO_OP_SLOT_FLT:
174 float param = PyFloat_AsDouble(value);
175 if (param == -1 && PyErr_Occurred()) {
176 PyErr_Format(PyExc_TypeError,
177 "%.200s: keyword \"%.200s\" expected a float, not %.200s",
178 self->opname, slot_name, Py_TYPE(value)->tp_name);
181 slot->data.f = param;
185 case BMO_OP_SLOT_ELEMENT_BUF:
187 /* there are many ways we could interpret arguments, for now...
188 * - verts/edges/faces from the mesh direct,
189 * this way the operator takes every item.
190 * - `TODO` a plain python sequence (list) of elements.
191 * - `TODO` an iterator. eg.
193 * - `TODO` (type, flag) pair, eg.
197 #define BPY_BM_GENERIC_MESH_TEST(type_string) \
198 if (((BPy_BMGeneric *)value)->bm != bm) { \
199 PyErr_Format(PyExc_NotImplementedError, \
200 "%.200s: keyword \"%.200s\" " type_string " are from another bmesh", \
201 self->opname, slot_name, slot->slot_type); \
205 if (BPy_BMVertSeq_Check(value)) {
206 BPY_BM_GENERIC_MESH_TEST("verts");
207 BMO_slot_buffer_from_all(bm, &bmop, slot_name, BM_VERT);
209 else if (BPy_BMEdgeSeq_Check(value)) {
210 BPY_BM_GENERIC_MESH_TEST("edges");
211 BMO_slot_buffer_from_all(bm, &bmop, slot_name, BM_EDGE);
213 else if (BPy_BMFaceSeq_Check(value)) {
214 BPY_BM_GENERIC_MESH_TEST("faces");
215 BMO_slot_buffer_from_all(bm, &bmop, slot_name, BM_FACE);
217 else if (BPy_BMElemSeq_Check(value)) {
223 BPY_BM_GENERIC_MESH_TEST("elements");
225 /* this will loop over all elements which is a shame but
226 * we need to know this before alloc */
227 /* calls bpy_bmelemseq_length() */
228 tot = Py_TYPE(value)->tp_as_sequence->sq_length((PyObject *)self);
230 BMO_slot_buffer_alloc(&bmop, slot_name, tot);
233 BM_ITER_BPY_BM_SEQ (ele, &iter, ((BPy_BMElemSeq *)value)) {
234 ((void **)slot->data.buf)[i] = (void *)ele;
239 else if (PySequence_Check(value)) {
240 BMElem **elem_array = NULL;
243 elem_array = BPy_BMElem_PySeq_As_Array(&bm, value, 0, PY_SSIZE_T_MAX,
244 &elem_array_len, BM_VERT | BM_EDGE | BM_FACE,
245 TRUE, TRUE, slot_name);
247 /* error is set above */
248 if (elem_array == NULL) {
252 BMO_slot_buffer_alloc(&bmop, slot_name, elem_array_len);
253 memcpy(slot->data.buf, elem_array, sizeof(void *) * elem_array_len);
254 PyMem_FREE(elem_array);
257 PyErr_Format(PyExc_TypeError,
258 "%.200s: keyword \"%.200s\" expected "
259 "a bmesh sequence, list, (htype, flag) pair, not %.200s",
260 self->opname, slot_name, Py_TYPE(value)->tp_name);
263 #undef BPY_BM_GENERIC_MESH_TEST
268 /* TODO --- many others */
269 PyErr_Format(PyExc_NotImplementedError,
270 "%.200s: keyword \"%.200s\" type %d not working yet!",
271 self->opname, slot_name, slot->slot_type);
278 BMO_op_exec(bm, &bmop);
279 BMO_op_finish(bm, &bmop);
281 if (bpy_bm_op_as_py_error(bm) == -1) {
289 PyTypeObject bmesh_op_Type = {
290 PyVarObject_HEAD_INIT(NULL, 0)
291 "BMeshOpFunc", /* tp_name */
292 sizeof(BPy_BMeshOpFunc), /* tp_basicsize */
295 NULL, /* tp_dealloc */
296 NULL, /* printfunc tp_print; */
297 NULL, /* getattrfunc tp_getattr; */
298 NULL, /* setattrfunc tp_setattr; */
299 NULL, /* tp_compare */ /* DEPRECATED in python 3.0! */
300 (reprfunc) bpy_bmesh_op_repr, /* tp_repr */
302 /* Method suites for standard classes */
304 NULL, /* PyNumberMethods *tp_as_number; */
305 NULL, /* PySequenceMethods *tp_as_sequence; */
306 NULL, /* PyMappingMethods *tp_as_mapping; */
308 /* More standard operations (here for binary compatibility) */
310 NULL, /* hashfunc tp_hash; */
311 (ternaryfunc)pyrna_op_call, /* ternaryfunc tp_call; */
312 NULL, /* reprfunc tp_str; */
314 /* will only use these if this is a subtype of a py class */
315 NULL, /* getattrofunc tp_getattro; */
316 NULL, /* setattrofunc tp_setattro; */
318 /* Functions to access object as input/output buffer */
319 NULL, /* PyBufferProcs *tp_as_buffer; */
321 /*** Flags to define presence of optional/expanded features ***/
322 Py_TPFLAGS_DEFAULT, /* long tp_flags; */
324 NULL, /* char *tp_doc; Documentation string */
325 /*** Assigned meaning in release 2.0 ***/
326 /* call function for all accessible objects */
327 NULL, /* traverseproc tp_traverse; */
329 /* delete references to contained objects */
330 NULL, /* inquiry tp_clear; */
332 /*** Assigned meaning in release 2.1 ***/
333 /*** rich comparisons ***/
334 NULL, /* richcmpfunc tp_richcompare; */
336 /*** weak reference enabler ***/
338 /*** Added in release 2.2 ***/
340 NULL, /* getiterfunc tp_iter; */
341 NULL, /* iternextfunc tp_iternext; */
343 /*** Attribute descriptor and subclassing stuff ***/
344 NULL, /* struct PyMethodDef *tp_methods; */
345 NULL, /* struct PyMemberDef *tp_members; */
346 NULL, /* struct PyGetSetDef *tp_getset; */
347 NULL, /* struct _typeobject *tp_base; */
348 NULL, /* PyObject *tp_dict; */
349 NULL, /* descrgetfunc tp_descr_get; */
350 NULL, /* descrsetfunc tp_descr_set; */
351 0, /* long tp_dictoffset; */
352 NULL, /* initproc tp_init; */
353 NULL, /* allocfunc tp_alloc; */
354 NULL, /* newfunc tp_new; */
355 /* Low-level free-memory routine */
356 NULL, /* freefunc tp_free; */
357 /* For PyObject_IS_GC */
358 NULL, /* inquiry tp_is_gc; */
359 NULL, /* PyObject *tp_bases; */
360 /* method resolution order */
361 NULL, /* PyObject *tp_mro; */
362 NULL, /* PyObject *tp_cache; */
363 NULL, /* PyObject *tp_subclasses; */
364 NULL, /* PyObject *tp_weaklist; */
369 /* bmesh fake module 'bmesh.ops'
370 * ***************************** */
372 static PyObject *bpy_bmesh_fmod_getattro(PyObject *UNUSED(self), PyObject *pyname)
374 const unsigned int tot = bmesh_total_ops;
376 const char *name = _PyUnicode_AsString(pyname);
378 for (i = 0; i < tot; i++) {
379 if (strcmp(opdefines[i]->name, name) == 0) {
380 return bpy_bmesh_op_CreatePyObject(opdefines[i]->name);
384 PyErr_Format(PyExc_AttributeError,
385 "BMeshOpsModule: , operator \"%.200s\" doesn't exist",
390 static PyObject *bpy_bmesh_fmod_dir(PyObject *UNUSED(self))
392 const unsigned int tot = bmesh_total_ops;
396 ret = PyList_New(bmesh_total_ops);
398 for (i = 0; i < tot; i++) {
399 PyList_SET_ITEM(ret, i, PyUnicode_FromString(opdefines[i]->name));
405 static struct PyMethodDef bpy_bmesh_fmod_methods[] = {
406 {"__dir__", (PyCFunction)bpy_bmesh_fmod_dir, METH_NOARGS, NULL},
407 {NULL, NULL, 0, NULL}
410 static PyTypeObject bmesh_ops_fakemod_Type = {
411 PyVarObject_HEAD_INIT(NULL, 0)
412 "BMeshOpsModule", /* tp_name */
413 0, /* tp_basicsize */
416 NULL, /* tp_dealloc */
417 NULL, /* printfunc tp_print; */
418 NULL, /* getattrfunc tp_getattr; */
419 NULL, /* setattrfunc tp_setattr; */
420 NULL, /* tp_compare */ /* DEPRECATED in python 3.0! */
423 /* Method suites for standard classes */
425 NULL, /* PyNumberMethods *tp_as_number; */
426 NULL, /* PySequenceMethods *tp_as_sequence; */
427 NULL, /* PyMappingMethods *tp_as_mapping; */
429 /* More standard operations (here for binary compatibility) */
431 NULL, /* hashfunc tp_hash; */
432 NULL, /* ternaryfunc tp_call; */
433 NULL, /* reprfunc tp_str; */
435 /* will only use these if this is a subtype of a py class */
436 bpy_bmesh_fmod_getattro, /* getattrofunc tp_getattro; */
437 NULL, /* setattrofunc tp_setattro; */
439 /* Functions to access object as input/output buffer */
440 NULL, /* PyBufferProcs *tp_as_buffer; */
442 /*** Flags to define presence of optional/expanded features ***/
443 Py_TPFLAGS_DEFAULT, /* long tp_flags; */
445 NULL, /* char *tp_doc; Documentation string */
446 /*** Assigned meaning in release 2.0 ***/
447 /* call function for all accessible objects */
448 NULL, /* traverseproc tp_traverse; */
450 /* delete references to contained objects */
451 NULL, /* inquiry tp_clear; */
453 /*** Assigned meaning in release 2.1 ***/
454 /*** rich comparisons ***/
455 NULL, /* subclassed */ /* richcmpfunc tp_richcompare; */
457 /*** weak reference enabler ***/
459 /*** Added in release 2.2 ***/
461 NULL, /* getiterfunc tp_iter; */
462 NULL, /* iternextfunc tp_iternext; */
464 /*** Attribute descriptor and subclassing stuff ***/
465 bpy_bmesh_fmod_methods, /* struct PyMethodDef *tp_methods; */
466 NULL, /* struct PyMemberDef *tp_members; */
467 NULL, /* struct PyGetSetDef *tp_getset; */
468 NULL, /* struct _typeobject *tp_base; */
469 NULL, /* PyObject *tp_dict; */
470 NULL, /* descrgetfunc tp_descr_get; */
471 NULL, /* descrsetfunc tp_descr_set; */
472 0, /* long tp_dictoffset; */
473 NULL, /* initproc tp_init; */
474 NULL, /* allocfunc tp_alloc; */
475 NULL, /* newfunc tp_new; */
476 /* Low-level free-memory routine */
477 NULL, /* freefunc tp_free; */
478 /* For PyObject_IS_GC */
479 NULL, /* inquiry tp_is_gc; */
480 NULL, /* PyObject *tp_bases; */
481 /* method resolution order */
482 NULL, /* PyObject *tp_mro; */
483 NULL, /* PyObject *tp_cache; */
484 NULL, /* PyObject *tp_subclasses; */
485 NULL, /* PyObject *tp_weaklist; */
489 PyObject *BPyInit_bmesh_ops(void)
493 if (PyType_Ready(&bmesh_ops_fakemod_Type) < 0)
496 if (PyType_Ready(&bmesh_op_Type) < 0)
499 submodule = PyObject_New(PyObject, &bmesh_ops_fakemod_Type);
501 /* prevent further creation of instances */
502 bmesh_ops_fakemod_Type.tp_init = NULL;
503 bmesh_ops_fakemod_Type.tp_new = NULL;