diff options
Diffstat (limited to 'source/blender/python')
-rw-r--r-- | source/blender/python/bmesh/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/python/bmesh/bmesh_py_api.c | 6 | ||||
-rw-r--r-- | source/blender/python/bmesh/bmesh_py_ops.c | 543 | ||||
-rw-r--r-- | source/blender/python/bmesh/bmesh_py_ops.h | 35 | ||||
-rw-r--r-- | source/blender/python/bmesh/bmesh_py_types.h | 9 | ||||
-rw-r--r-- | source/blender/python/bmesh/bmesh_py_utils.c | 9 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_operator.c | 6 | ||||
-rw-r--r-- | source/blender/python/intern/stubs.c | 2 | ||||
-rw-r--r-- | source/blender/python/mathutils/mathutils.c | 10 | ||||
-rw-r--r-- | source/blender/python/mathutils/mathutils_Vector.c | 47 | ||||
-rw-r--r-- | source/blender/python/mathutils/mathutils_geometry.c | 104 |
11 files changed, 739 insertions, 34 deletions
diff --git a/source/blender/python/bmesh/CMakeLists.txt b/source/blender/python/bmesh/CMakeLists.txt index 40bde7161b6..032a914fb70 100644 --- a/source/blender/python/bmesh/CMakeLists.txt +++ b/source/blender/python/bmesh/CMakeLists.txt @@ -33,6 +33,7 @@ set(INC_SYS set(SRC bmesh_py_api.c + bmesh_py_ops.c bmesh_py_types.c bmesh_py_types_customdata.c bmesh_py_types_meshdata.c @@ -40,6 +41,7 @@ set(SRC bmesh_py_utils.c bmesh_py_api.h + bmesh_py_ops.h bmesh_py_types.h bmesh_py_types_customdata.h bmesh_py_types_meshdata.h diff --git a/source/blender/python/bmesh/bmesh_py_api.c b/source/blender/python/bmesh/bmesh_py_api.c index 4d8d4e3bc23..b2fd1d3d61c 100644 --- a/source/blender/python/bmesh/bmesh_py_api.c +++ b/source/blender/python/bmesh/bmesh_py_api.c @@ -40,6 +40,7 @@ #include "bmesh_py_types_customdata.h" #include "bmesh_py_types_meshdata.h" +#include "bmesh_py_ops.h" #include "bmesh_py_utils.h" #include "BKE_tessmesh.h" @@ -143,6 +144,11 @@ PyObject *BPyInit_bmesh(void) PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); Py_INCREF(submodule); + PyModule_AddObject(mod, "ops", (submodule = BPyInit_bmesh_ops())); + /* PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); */ + PyDict_SetItemString(sys_modules, "bmesh.ops", submodule); /* fake module */ + Py_INCREF(submodule); + PyModule_AddObject(mod, "utils", (submodule = BPyInit_bmesh_utils())); PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); Py_INCREF(submodule); diff --git a/source/blender/python/bmesh/bmesh_py_ops.c b/source/blender/python/bmesh/bmesh_py_ops.c new file mode 100644 index 00000000000..53ddcecd7a8 --- /dev/null +++ b/source/blender/python/bmesh/bmesh_py_ops.c @@ -0,0 +1,543 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/bmesh/bmesh_py_ops.c + * \ingroup pybmesh + * + * This file defines the 'bmesh.ops' module. + * Operators from 'opdefines' are wrapped. + */ + +#include <Python.h> + +#include "BLI_utildefines.h" + +#include "MEM_guardedalloc.h" + +#include "../generic/py_capi_utils.h" + +#include "../mathutils/mathutils.h" + +#include "bmesh.h" + +#include "bmesh_py_types.h" + +#include "bmesh_py_utils.h" /* own include */ + +static int bpy_bm_op_as_py_error(BMesh *bm) +{ + if (BMO_error_occurred(bm)) { + const char *errmsg; + if (BMO_error_get(bm, &errmsg, NULL)) { + PyErr_Format(PyExc_RuntimeError, + "bmesh operator: %.200s", + errmsg); + return -1; + } + } + return 0; +} + +/* bmesh operator 'bmesh.ops.*' callable types + * ******************************************* */ +PyTypeObject bmesh_op_Type; + +typedef struct { + PyObject_HEAD /* required python macro */ + const char *opname; +} BPy_BMeshOpFunc; + +PyObject *bpy_bmesh_op_CreatePyObject(const char *opname) +{ + BPy_BMeshOpFunc *self = PyObject_New(BPy_BMeshOpFunc, &bmesh_op_Type); + + self->opname = opname; + + return (PyObject *)self; +} + +static PyObject *bpy_bmesh_op_repr(BPy_BMeshOpFunc *self) +{ + return PyUnicode_FromFormat("<%.200s bmesh.ops.%.200s()>", + Py_TYPE(self)->tp_name, + self->opname); +} + + +static PyObject *pyrna_op_call(BPy_BMeshOpFunc *self, PyObject *args, PyObject *kw) +{ + BPy_BMesh *py_bm; + BMesh *bm; + + BMOperator bmop; + + if ((PyTuple_GET_SIZE(args) == 1) && + (py_bm = (BPy_BMesh *)PyTuple_GET_ITEM(args, 0)) && + (BPy_BMesh_Check(py_bm)) + ) + { + BPY_BM_CHECK_OBJ(py_bm); + bm = py_bm->bm; + } + else { + PyErr_SetString(PyExc_TypeError, + "calling a bmesh operator expects a single BMesh (non keyword) " + "as the first argument"); + return NULL; + } + + /* TODO - error check this!, though we do the error check on attribute access */ + BMO_op_init(bm, &bmop, self->opname); + + if (kw && PyDict_Size(kw) > 0) { + /* setup properties, see bpy_rna.c: pyrna_py_to_prop() + * which shares this logic for parsing properties */ + + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(kw, &pos, &key, &value)) { + const char *slot_name = _PyUnicode_AsString(key); + BMOpSlot *slot = BMO_slot_get(&bmop, slot_name); + + if (slot == NULL) { + PyErr_Format(PyExc_TypeError, + "%.200s: keyword \"%.200s\" is invalid for this operator", + self->opname, slot_name); + return NULL; + } + + /* now assign the value */ + switch (slot->slot_type) { + case BMO_OP_SLOT_BOOL: + { + int param; + + param = PyLong_AsLong(value); + + if (param < 0) { + PyErr_Format(PyExc_TypeError, + "%.200s: keyword \"%.200s\" expected True/False or 0/1, not %.200s", + self->opname, slot_name, Py_TYPE(value)->tp_name); + return NULL; + } + else { + slot->data.i = param; + } + + break; + } + case BMO_OP_SLOT_INT: + { + int overflow; + long param = PyLong_AsLongAndOverflow(value, &overflow); + if (overflow || (param > INT_MAX) || (param < INT_MIN)) { + PyErr_Format(PyExc_ValueError, + "%.200s: keyword \"%.200s\" value not in 'int' range " + "(" STRINGIFY(INT_MIN) ", " STRINGIFY(INT_MAX) ")", + self->opname, slot_name, Py_TYPE(value)->tp_name); + return NULL; + } + else if (param == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, + "%.200s: keyword \"%.200s\" expected an int, not %.200s", + self->opname, slot_name, Py_TYPE(value)->tp_name); + return NULL; + } + else { + slot->data.i = (int)param; + } + break; + } + case BMO_OP_SLOT_FLT: + { + float param = PyFloat_AsDouble(value); + if (param == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, + "%.200s: keyword \"%.200s\" expected a float, not %.200s", + self->opname, slot_name, Py_TYPE(value)->tp_name); + return NULL; + } + else { + slot->data.f = param; + } + break; + } + case BMO_OP_SLOT_MAT: + { + /* XXX - BMesh operator design is crappy here, operator slot should define matrix size, + * not the caller! */ + unsigned short size; + if (!MatrixObject_Check(value)) { + PyErr_Format(PyExc_TypeError, + "%.200s: keyword \"%.200s\" expected a Matrix, not %.200s", + self->opname, slot_name, Py_TYPE(value)->tp_name); + return NULL; + } + else if (BaseMath_ReadCallback((MatrixObject *)value) == -1) { + return NULL; + } + else if (((size = ((MatrixObject *)value)->num_col) != ((MatrixObject *)value)->num_row) || + (ELEM(size, 3, 4) == FALSE)) + { + PyErr_Format(PyExc_TypeError, + "%.200s: keyword \"%.200s\" expected a 3x3 or 4x4 matrix Matrix", + self->opname, slot_name); + return NULL; + } + + BMO_slot_mat_set(&bmop, slot_name, ((MatrixObject *)value)->matrix, size); + break; + } + case BMO_OP_SLOT_VEC: + { + /* passing slot name here is a bit non-descriptive */ + if (mathutils_array_parse(slot->data.vec, 3, 3, value, slot_name) == -1) { + return NULL; + } + break; + } + case BMO_OP_SLOT_ELEMENT_BUF: + { + /* there are many ways we could interpret arguments, for now... + * - verts/edges/faces from the mesh direct, + * this way the operator takes every item. + * - `TODO` a plain python sequence (list) of elements. + * - `TODO` an iterator. eg. + * face.verts + * - `TODO` (type, flag) pair, eg. + * ('VERT', {'TAG'}) + */ + +#define BPY_BM_GENERIC_MESH_TEST(type_string) \ + if (((BPy_BMGeneric *)value)->bm != bm) { \ + PyErr_Format(PyExc_NotImplementedError, \ + "%.200s: keyword \"%.200s\" " type_string " are from another bmesh", \ + self->opname, slot_name, slot->slot_type); \ + return NULL; \ + } (void)0 + + if (BPy_BMVertSeq_Check(value)) { + BPY_BM_GENERIC_MESH_TEST("verts"); + BMO_slot_buffer_from_all(bm, &bmop, slot_name, BM_VERT); + } + else if (BPy_BMEdgeSeq_Check(value)) { + BPY_BM_GENERIC_MESH_TEST("edges"); + BMO_slot_buffer_from_all(bm, &bmop, slot_name, BM_EDGE); + } + else if (BPy_BMFaceSeq_Check(value)) { + BPY_BM_GENERIC_MESH_TEST("faces"); + BMO_slot_buffer_from_all(bm, &bmop, slot_name, BM_FACE); + } + else if (BPy_BMElemSeq_Check(value)) { + BMIter iter; + BMHeader *ele; + int tot; + unsigned int i; + + BPY_BM_GENERIC_MESH_TEST("elements"); + + /* this will loop over all elements which is a shame but + * we need to know this before alloc */ + /* calls bpy_bmelemseq_length() */ + tot = Py_TYPE(value)->tp_as_sequence->sq_length((PyObject *)self); + + BMO_slot_buffer_alloc(&bmop, slot_name, tot); + + i = 0; + BM_ITER_BPY_BM_SEQ (ele, &iter, ((BPy_BMElemSeq *)value)) { + ((void **)slot->data.buf)[i] = (void *)ele; + i++; + } + } + /* keep this last */ + else if (PySequence_Check(value)) { + BMElem **elem_array = NULL; + Py_ssize_t elem_array_len; + + elem_array = BPy_BMElem_PySeq_As_Array(&bm, value, 0, PY_SSIZE_T_MAX, + &elem_array_len, BM_VERT | BM_EDGE | BM_FACE, + TRUE, TRUE, slot_name); + + /* error is set above */ + if (elem_array == NULL) { + return NULL; + } + + BMO_slot_buffer_alloc(&bmop, slot_name, elem_array_len); + memcpy(slot->data.buf, elem_array, sizeof(void *) * elem_array_len); + PyMem_FREE(elem_array); + } + else { + PyErr_Format(PyExc_TypeError, + "%.200s: keyword \"%.200s\" expected " + "a bmesh sequence, list, (htype, flag) pair, not %.200s", + self->opname, slot_name, Py_TYPE(value)->tp_name); + return NULL; + } + +#undef BPY_BM_GENERIC_MESH_TEST + + break; + } + default: + /* TODO --- many others */ + PyErr_Format(PyExc_NotImplementedError, + "%.200s: keyword \"%.200s\" type %d not working yet!", + self->opname, slot_name, slot->slot_type); + return NULL; + break; + } + } + } + + BMO_op_exec(bm, &bmop); + BMO_op_finish(bm, &bmop); + + if (bpy_bm_op_as_py_error(bm) == -1) { + return NULL; + } + + Py_RETURN_NONE; +} + + +PyTypeObject bmesh_op_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "BMeshOpFunc", /* tp_name */ + sizeof(BPy_BMeshOpFunc), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + NULL, /* tp_dealloc */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* tp_compare */ /* DEPRECATED in python 3.0! */ + (reprfunc) bpy_bmesh_op_repr, /* tp_repr */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + (ternaryfunc)pyrna_op_call, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + + /* will only use these if this is a subtype of a py class */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, + /*** Added in release 2.2 ***/ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + NULL, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + + +/* bmesh fake module 'bmesh.ops' + * ***************************** */ + +static PyObject *bpy_bmesh_fmod_getattro(PyObject *UNUSED(self), PyObject *pyname) +{ + const unsigned int tot = bmesh_total_ops; + unsigned int i; + const char *name = _PyUnicode_AsString(pyname); + + for (i = 0; i < tot; i++) { + if (strcmp(opdefines[i]->name, name) == 0) { + return bpy_bmesh_op_CreatePyObject(opdefines[i]->name); + } + } + + PyErr_Format(PyExc_AttributeError, + "BMeshOpsModule: , operator \"%.200s\" doesn't exist", + name); + return NULL; +} + +static PyObject *bpy_bmesh_fmod_dir(PyObject *UNUSED(self)) +{ + const unsigned int tot = bmesh_total_ops; + unsigned int i; + PyObject *ret; + + ret = PyList_New(bmesh_total_ops); + + for (i = 0; i < tot; i++) { + PyList_SET_ITEM(ret, i, PyUnicode_FromString(opdefines[i]->name)); + } + + return ret; +} + +static struct PyMethodDef bpy_bmesh_fmod_methods[] = { + {"__dir__", (PyCFunction)bpy_bmesh_fmod_dir, METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL} +}; + +static PyTypeObject bmesh_ops_fakemod_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "BMeshOpsModule", /* tp_name */ + 0, /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + NULL, /* tp_dealloc */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* tp_compare */ /* DEPRECATED in python 3.0! */ + NULL, /* tp_repr */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + + /* will only use these if this is a subtype of a py class */ + bpy_bmesh_fmod_getattro, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* subclassed */ /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, + /*** Added in release 2.2 ***/ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + bpy_bmesh_fmod_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +PyObject *BPyInit_bmesh_ops(void) +{ + PyObject *submodule; + + if (PyType_Ready(&bmesh_ops_fakemod_Type) < 0) + return NULL; + + if (PyType_Ready(&bmesh_op_Type) < 0) + return NULL; + + submodule = PyObject_New(PyObject, &bmesh_ops_fakemod_Type); + + /* prevent further creation of instances */ + bmesh_ops_fakemod_Type.tp_init = NULL; + bmesh_ops_fakemod_Type.tp_new = NULL; + + return submodule; +} diff --git a/source/blender/python/bmesh/bmesh_py_ops.h b/source/blender/python/bmesh/bmesh_py_ops.h new file mode 100644 index 00000000000..56c980b57ea --- /dev/null +++ b/source/blender/python/bmesh/bmesh_py_ops.h @@ -0,0 +1,35 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/bmesh/bmesh_py_ops.h + * \ingroup pybmesh + */ + +#ifndef __BMESH_PY_OPS_H__ +#define __BMESH_PY_OPS_H__ + +PyObject *BPyInit_bmesh_ops(void); + +#endif /* __BMESH_PY_OPS_H__ */ diff --git a/source/blender/python/bmesh/bmesh_py_types.h b/source/blender/python/bmesh/bmesh_py_types.h index 85bbd5d7b09..947e66bf24e 100644 --- a/source/blender/python/bmesh/bmesh_py_types.h +++ b/source/blender/python/bmesh/bmesh_py_types.h @@ -186,4 +186,13 @@ char *BPy_BMElem_StringFromHType(const char htype); ele; \ ele = BM_iter_step(iter)) + +#ifdef __PY_CAPI_UTILS_H__ +struct PyC_FlagSet; +extern struct PyC_FlagSet bpy_bm_scene_vert_edge_face_flags[]; +extern struct PyC_FlagSet bpy_bm_htype_vert_edge_face_flags[]; +extern struct PyC_FlagSet bpy_bm_htype_all_flags[]; +extern struct PyC_FlagSet bpy_bm_hflag_all_flags[]; +#endif + #endif /* __BMESH_TYPES_H__ */ diff --git a/source/blender/python/bmesh/bmesh_py_utils.c b/source/blender/python/bmesh/bmesh_py_utils.c index bb4e0c61c2b..4417bc2ce8d 100644 --- a/source/blender/python/bmesh/bmesh_py_utils.c +++ b/source/blender/python/bmesh/bmesh_py_utils.c @@ -457,6 +457,7 @@ static PyObject *bpy_bm_utils_face_split(PyObject *UNUSED(self), PyObject *args, py_vert_a->v, py_vert_b->v, (float (*)[3])coords, ncoords, &l_new, py_edge_example ? py_edge_example->e : NULL); + PyMem_Free(coords); } else { f_new = BM_face_split(bm, py_face->f, @@ -667,13 +668,13 @@ static struct PyMethodDef BPy_BM_utils_methods[] = { }; -PyDoc_STRVAR(BPy_BM_doc, +PyDoc_STRVAR(BPy_BM_utils_doc, "This module provides access to blenders bmesh data structures." ); -static struct PyModuleDef BPy_BM_types_module_def = { +static struct PyModuleDef BPy_BM_utils_module_def = { PyModuleDef_HEAD_INIT, "bmesh.utils", /* m_name */ - BPy_BM_doc, /* m_doc */ + BPy_BM_utils_doc, /* m_doc */ 0, /* m_size */ BPy_BM_utils_methods, /* m_methods */ NULL, /* m_reload */ @@ -687,7 +688,7 @@ PyObject *BPyInit_bmesh_utils(void) { PyObject *submodule; - submodule = PyModule_Create(&BPy_BM_types_module_def); + submodule = PyModule_Create(&BPy_BM_utils_module_def); return submodule; } diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 10562271941..7375ad454a0 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -147,6 +147,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) /* note that context is an int, python does the conversion in this case */ int context = WM_OP_EXEC_DEFAULT; + int is_undo = FALSE; /* XXX Todo, work out a better solution for passing on context, * could make a tuple from self and pack the name and Context into it... */ @@ -157,7 +158,8 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) return NULL; } - if (!PyArg_ParseTuple(args, "sO|O!s:_bpy.ops.call", &opname, &context_dict, &PyDict_Type, &kw, &context_str)) + if (!PyArg_ParseTuple(args, "sO|O!si:_bpy.ops.call", + &opname, &context_dict, &PyDict_Type, &kw, &context_str, &is_undo)) return NULL; ot = WM_operatortype_find(opname, TRUE); @@ -236,7 +238,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) PyThreadState *ts = PyEval_SaveThread(); #endif - operator_ret = WM_operator_call_py(C, ot, context, &ptr, reports); + operator_ret = WM_operator_call_py(C, ot, context, &ptr, reports, is_undo); #ifdef BPY_RELEASE_GIL /* regain GIL */ diff --git a/source/blender/python/intern/stubs.c b/source/blender/python/intern/stubs.c index ab43b518085..50d2e88f210 100644 --- a/source/blender/python/intern/stubs.c +++ b/source/blender/python/intern/stubs.c @@ -35,5 +35,5 @@ //void BPY_text_free_code(void) {} void BPY_pyconstraint_exec(void) {} void BPY_pyconstraint_target(void) {} -int BPY_is_pyconstraint(void) {return 0;} +int BPY_is_pyconstraint(void) { return 0;} void BPY_pyconstraint_update(void) {} diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 8b79301f264..c08165f9850 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -133,6 +133,7 @@ int mathutils_array_parse(float *array, int array_min, int array_max, PyObject * } } +/* on error, -1 is returned and no allocation is made */ int mathutils_array_parse_alloc(float **array, int array_min, PyObject *value, const char *error_prefix) { int size; @@ -164,6 +165,7 @@ int mathutils_array_parse_alloc(float **array, int array_min, PyObject *value, c { PyObject *value_fast = NULL; // *array = NULL; + int ret; /* non list/tuple cases */ if (!(value_fast = PySequence_Fast(value, error_prefix))) { @@ -182,7 +184,13 @@ int mathutils_array_parse_alloc(float **array, int array_min, PyObject *value, c *array = PyMem_Malloc(size * sizeof(float)); - return mathutils_array_parse_fast(*array, size, value_fast, error_prefix); + ret = mathutils_array_parse_fast(*array, size, value_fast, error_prefix); + + if (ret == -1) { + PyMem_Free(*array); + } + + return ret; } } diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index 8fb3a3f74d6..79285b7778d 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -54,11 +54,18 @@ static int row_vector_multiplication(float rvec[MAX_DIMENSIONS], VectorObject *v /* Supports 2D, 3D, and 4D vector objects both int and float values * accepted. Mixed float and int values accepted. Ints are parsed to float */ -static PyObject *Vector_new(PyTypeObject *type, PyObject *args, PyObject *UNUSED(kwds)) +static PyObject *Vector_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { float *vec = NULL; int size = 3; /* default to a 3D vector */ + if (kwds && PyDict_Size(kwds)) { + PyErr_SetString(PyExc_TypeError, + "Vector(): " + "takes no keyword args"); + return NULL; + } + switch (PyTuple_GET_SIZE(args)) { case 0: vec = PyMem_Malloc(size * sizeof(float)); @@ -74,9 +81,6 @@ static PyObject *Vector_new(PyTypeObject *type, PyObject *args, PyObject *UNUSED break; case 1: if ((size = mathutils_array_parse_alloc(&vec, 2, PyTuple_GET_ITEM(args, 0), "mathutils.Vector()")) == -1) { - if (vec) { - PyMem_Free(vec); - } return NULL; } break; @@ -86,7 +90,7 @@ static PyObject *Vector_new(PyTypeObject *type, PyObject *args, PyObject *UNUSED "more then a single arg given"); return NULL; } - return Vector_CreatePyObject(vec, size, Py_NEW, type); + return Vector_CreatePyObject_alloc(vec, size, type); } static PyObject *vec__apply_to_copy(PyNoArgsFunction vec_func, VectorObject *self) @@ -294,7 +298,6 @@ static PyObject *C_Vector_Repeat(PyObject *cls, PyObject *args) if ((value_size = mathutils_array_parse_alloc(&iter_vec, 2, value, "Vector.Repeat(vector, size), invalid 'vector' arg")) == -1) { - PyMem_Free(iter_vec); return NULL; } @@ -308,6 +311,7 @@ static PyObject *C_Vector_Repeat(PyObject *cls, PyObject *args) vec = PyMem_Malloc(size * sizeof(float)); if (vec == NULL) { + PyMem_Free(iter_vec); PyErr_SetString(PyExc_MemoryError, "Vector.Repeat(): " "problem allocating pointer space"); @@ -891,19 +895,18 @@ PyDoc_STRVAR(Vector_dot_doc, static PyObject *Vector_dot(VectorObject *self, PyObject *value) { float *tvec; + PyObject *ret; if (BaseMath_ReadCallback(self) == -1) return NULL; if (mathutils_array_parse_alloc(&tvec, self->size, value, "Vector.dot(other), invalid 'other' arg") == -1) { - goto cleanup; + return NULL; } - return PyFloat_FromDouble(dot_vn_vn(self->vec, tvec, self->size)); - -cleanup: + ret = PyFloat_FromDouble(dot_vn_vn(self->vec, tvec, self->size)); PyMem_Free(tvec); - return NULL; + return ret; } PyDoc_STRVAR(Vector_angle_doc, @@ -1133,12 +1136,12 @@ static PyObject *Vector_lerp(VectorObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "Of:lerp", &value, &fac)) return NULL; - if (mathutils_array_parse_alloc(&tvec, size, value, "Vector.lerp(other), invalid 'other' arg") == -1) { - goto cleanup; + if (BaseMath_ReadCallback(self) == -1) { + return NULL; } - if (BaseMath_ReadCallback(self) == -1) { - goto cleanup; + if (mathutils_array_parse_alloc(&tvec, size, value, "Vector.lerp(other), invalid 'other' arg") == -1) { + return NULL; } vec = PyMem_Malloc(size * sizeof(float)); @@ -1158,10 +1161,6 @@ static PyObject *Vector_lerp(VectorObject *self, PyObject *args) PyMem_Free(tvec); return Vector_CreatePyObject_alloc(vec, size, Py_TYPE(self)); - -cleanup: - PyMem_Free(tvec); - return NULL; } PyDoc_STRVAR(Vector_rotate_doc, @@ -1363,7 +1362,7 @@ static int Vector_ass_slice(VectorObject *self, int begin, int end, PyObject *se size = (end - begin); if (mathutils_array_parse_alloc(&vec, size, seq, "vector[begin:end] = [...]") == -1) { - goto cleanup; + return -1; } if (vec == NULL) { @@ -1376,16 +1375,12 @@ static int Vector_ass_slice(VectorObject *self, int begin, int end, PyObject *se /*parsed well - now set in vector*/ memcpy(self->vec + begin, vec, size * sizeof(float)); + PyMem_Free(vec); + if (BaseMath_WriteCallback(self) == -1) return -1; - PyMem_Free(vec); - return 0; - -cleanup: - PyMem_Free(vec); - return -1; } /* Numeric Protocols */ diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index 4d05b837952..524515ac460 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -944,6 +944,109 @@ static PyObject *M_Geometry_barycentric_transform(PyObject *UNUSED(self), PyObje return Vector_CreatePyObject(vec, 3, Py_NEW, NULL); } +PyDoc_STRVAR(M_Geometry_points_in_planes_doc, +".. function:: points_in_planes(planes)\n" +"\n" +" Returns a list of points inside all planes given and a list of index values for the planes used.\n" +"\n" +" :arg planes: List of planes (4D vectors).\n" +" :type planes: list of :class:`mathutils.Vector`\n" +" :return: two lists, once containing the vertices inside the planes, another containing the plane indicies used\n" +" :rtype: pair of lists\n" +); +/* note: this function could be optimized by some spatial structure */ +static PyObject *M_Geometry_points_in_planes(PyObject *UNUSED(self), PyObject *args) +{ + PyObject *py_planes; + float (*planes)[4]; + unsigned int planes_len; + + if (!PyArg_ParseTuple(args, "O:points_in_planes", + &py_planes)) + { + return NULL; + } + + if ((planes_len = mathutils_array_parse_alloc_v((float **)&planes, 4, py_planes, "points_in_planes")) == -1) { + return NULL; + } + else { + /* note, this could be refactored into plain C easy - py bits are noted */ + const float eps = 0.0001f; + const unsigned int len = (unsigned int)planes_len; + unsigned int i, j, k, l; + + float n1n2[3], n2n3[3], n3n1[3]; + float potentialVertex[3]; + char *planes_used = MEM_callocN(sizeof(char) * len, __func__); + + /* python */ + PyObject *py_verts = PyList_New(0); + PyObject *py_plene_index = PyList_New(0); + + for (i = 0; i < len; i++) { + const float *N1 = planes[i]; + for (j = i + 1; j < len; j++) { + const float *N2 = planes[j]; + cross_v3_v3v3(n1n2, N1, N2); + if (len_squared_v3(n1n2) > eps) { + for (k = j + 1; k < len; k++) { + const float *N3 = planes[k]; + cross_v3_v3v3(n2n3, N2, N3); + if (len_squared_v3(n2n3) > eps) { + cross_v3_v3v3(n3n1, N3, N1); + if (len_squared_v3(n3n1) > eps) { + const float quotient = dot_v3v3(N1, n2n3); + if (fabsf(quotient) > eps) { + /* potentialVertex = (n2n3 * N1[3] + n3n1 * N2[3] + n1n2 * N3[3]) * (-1.0 / quotient); */ + const float quotient_ninv = -1.0f / quotient; + potentialVertex[0] = ((n2n3[0] * N1[3]) + (n3n1[0] * N2[3]) + (n1n2[0] * N3[3])) * quotient_ninv; + potentialVertex[1] = ((n2n3[1] * N1[3]) + (n3n1[1] * N2[3]) + (n1n2[1] * N3[3])) * quotient_ninv; + potentialVertex[2] = ((n2n3[2] * N1[3]) + (n3n1[2] * N2[3]) + (n1n2[2] * N3[3])) * quotient_ninv; + for (l = 0; l < len; l++) { + const float *NP = planes[l]; + if ((dot_v3v3(NP, potentialVertex) + NP[3]) > 0.000001f) { + break; + } + } + + if (l == len) { /* ok */ + /* python */ + PyObject *item = Vector_CreatePyObject(potentialVertex, 3, Py_NEW, NULL); + PyList_Append(py_verts, item); + Py_DECREF(item); + + planes_used[i] = planes_used[j] = planes_used[k] = TRUE; + } + } + } + } + } + } + } + } + + PyMem_Free(planes); + + /* now make a list of used planes */ + for (i = 0; i < len; i++) { + if (planes_used[i]) { + PyObject *item = PyLong_FromLong(i); + PyList_Append(py_plene_index, item); + Py_DECREF(item); + } + } + MEM_freeN(planes_used); + + { + PyObject *ret = PyTuple_New(2); + PyTuple_SET_ITEM(ret, 0, py_verts); + PyTuple_SET_ITEM(ret, 1, py_plene_index); + return ret; + } + } +} + #ifndef MATH_STANDALONE PyDoc_STRVAR(M_Geometry_interpolate_bezier_doc, @@ -1279,6 +1382,7 @@ static PyMethodDef M_Geometry_methods[] = { {"area_tri", (PyCFunction) M_Geometry_area_tri, METH_VARARGS, M_Geometry_area_tri_doc}, {"normal", (PyCFunction) M_Geometry_normal, METH_VARARGS, M_Geometry_normal_doc}, {"barycentric_transform", (PyCFunction) M_Geometry_barycentric_transform, METH_VARARGS, M_Geometry_barycentric_transform_doc}, + {"points_in_planes", (PyCFunction) M_Geometry_points_in_planes, METH_VARARGS, M_Geometry_points_in_planes_doc}, #ifndef MATH_STANDALONE {"interpolate_bezier", (PyCFunction) M_Geometry_interpolate_bezier, METH_VARARGS, M_Geometry_interpolate_bezier_doc}, {"tessellate_polygon", (PyCFunction) M_Geometry_tessellate_polygon, METH_O, M_Geometry_tessellate_polygon_doc}, |