Changeset View
Changeset View
Standalone View
Standalone View
source/blender/python/generic/kdtree_py_api.c
- This file was added.
| /* | |||||
| * ***** 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. | |||||
| * | |||||
| * Contributor(s): Dan Eicher | |||||
| * | |||||
| * ***** END GPL LICENSE BLOCK ***** | |||||
| */ | |||||
| #include <Python.h> | |||||
| #include "kdtree_py_api.h" | |||||
| #include "../mathutils/mathutils.h" | |||||
| #include "BLI_kdtree.h" | |||||
| #include "MEM_guardedalloc.h" | |||||
| typedef struct { | |||||
| PyObject_HEAD | |||||
| KDTree *obj; | |||||
| unsigned int maxsize; | |||||
| unsigned int count; | |||||
| unsigned int balanced; | |||||
| } PyKDTree; | |||||
| static int | |||||
| py_seq_to_vec3(PyObject *seq, void *vec) | |||||
| { | |||||
| if (mathutils_array_parse((float*)vec, 3, 3, seq, "KDTree") == -1) | |||||
| return 0; | |||||
| return 1; | |||||
| } | |||||
| static PyObject * | |||||
| kdtree_nearest_to_py_tuple(KDTreeNearest *nearest) | |||||
| { | |||||
| PyObject *py_retval; | |||||
| if (nearest->index == -1) { | |||||
| return PyTuple_Pack(3, Py_None, Py_None, Py_None); /* new references */ | |||||
| } | |||||
| py_retval = PyTuple_New(3); | |||||
| PyTuple_SET_ITEM(py_retval, 0, PyLong_FromLong(nearest->index)); | |||||
| PyTuple_SET_ITEM(py_retval, 1, PyFloat_FromDouble(nearest->dist)); | |||||
| PyTuple_SET_ITEM(py_retval, 2, Vector_CreatePyObject(&(*nearest->co), 3, Py_NEW, NULL)); | |||||
campbellbarton: prefer PyTuple_New(), SET_ITEM, means no need to decref after. | |||||
| return py_retval; | |||||
| } | |||||
| static int | |||||
| PyKDTree__tp_init(PyKDTree *self, PyObject *args, PyObject *kwargs) | |||||
| { | |||||
| unsigned int maxsize; | |||||
| const char *keywords[] = {"size", NULL}; | |||||
| if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "I", (char **) keywords, &maxsize)) { | |||||
campbellbartonAuthorUnsubmitted Not Done Inline ActionsPassing negative values crashes. campbellbarton: Passing negative values crashes. | |||||
| return -1; | |||||
| } | |||||
| self->obj = BLI_kdtree_new(maxsize); | |||||
Not Done Inline Actionsjust pass nearest->co campbellbarton: just pass `nearest->co` | |||||
Not Done Inline ActionsFor some odd reason just passing nearest->co was failing. Tested with: for v in bm.verts:
i, d, co = tree.find_nearest(v.co)
v.co == codna: For some odd reason just passing nearest->co was failing.
Tested with:
for v in bm.verts… | |||||
| self->maxsize = maxsize; | |||||
| self->count = 0; | |||||
| self->balanced = 0; | |||||
| return 0; | |||||
| } | |||||
| static void | |||||
| PyKDTree__tp_dealloc(PyKDTree *self) | |||||
| { | |||||
| BLI_kdtree_free(self->obj); | |||||
| Py_TYPE(self)->tp_free((PyObject*)self); | |||||
| } | |||||
| PyDoc_STRVAR(py_kdtree_insert_doc, | |||||
| ".. method:: insert(index, co)\n" | |||||
| "\n" | |||||
| " Insert a point into the KDTree.\n" | |||||
| "\n" | |||||
| " :arg index: The index of the point.\n" | |||||
| " :type index: unsigned int\n" | |||||
| " :arg co: Point 3d position.\n" | |||||
| " :type co: Sequence of three floats\n" | |||||
| ); | |||||
| static PyObject * | |||||
| py_kdtree_insert(PyKDTree *self, PyObject *args, PyObject *kwargs) | |||||
| { | |||||
| int index; | |||||
| float co[3]; | |||||
| const char *keywords[] = {"index", "co", NULL}; | |||||
| if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "iO&:insert", (char **) keywords, | |||||
| &index, &py_seq_to_vec3, &co)) { | |||||
| return NULL; | |||||
| } | |||||
Not Done Inline Actions"iO&" -> "iO&:insert" will give better exception messages, goes for other uses of PyArg_ParseTupleAndKeywords campbellbarton: `"iO&" -> "iO&:insert"`
will give better exception messages, goes for other uses of… | |||||
| if (self->count >= self->maxsize) { | |||||
| PyErr_SetString(PyExc_RuntimeError, "Trying to insert more items than KDTree has room for"); | |||||
| return NULL; | |||||
| } | |||||
| BLI_kdtree_insert(self->obj, index, co, NULL); | |||||
| self->count++; | |||||
| self->balanced = 0; | |||||
| Py_RETURN_NONE; | |||||
| } | |||||
| PyDoc_STRVAR(py_kdtree_balance_doc, | |||||
| ".. method:: balance()\n" | |||||
| "\n" | |||||
| " Balance the tree.\n" | |||||
| ); | |||||
| static PyObject * | |||||
| py_kdtree_balance(PyKDTree *self) | |||||
| { | |||||
| BLI_kdtree_balance(self->obj); | |||||
| self->balanced = 1; | |||||
| Py_RETURN_NONE; | |||||
| } | |||||
| PyDoc_STRVAR(py_kdtree_find_nearest_doc, | |||||
| ".. method:: find_nearest(co)\n" | |||||
| "\n" | |||||
| " Find nearest point to `co'.\n" | |||||
| "\n" | |||||
Not Done Inline ActionsNot sure theres a good reason to allow passing a None arg, the caller can just ommit the arg in this case. campbellbarton: Not sure theres a good reason to allow passing a None arg, the caller can just ommit the arg in… | |||||
| " KdTree.balance() must have been called before using this method.\n" | |||||
| "\n" | |||||
| " :arg co: 3d coordinates.\n" | |||||
| " :type co: Sequence of three floats\n" | |||||
| " :return: Returns index, distance, :class:`Vector`.\n" | |||||
| " :rtype: :class:`tuple`\n" | |||||
| ); | |||||
| static PyObject * | |||||
| py_kdtree_find_nearest(PyKDTree *self, PyObject *args, PyObject *kwargs) | |||||
| { | |||||
| float co[3]; | |||||
| KDTreeNearest nearest; | |||||
| const char *keywords[] = {"co", NULL}; | |||||
| if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "O&:find_nearest", (char **) keywords, | |||||
| &py_seq_to_vec3, &co)) { | |||||
| return NULL; | |||||
| } | |||||
| if (self->balanced == 0) { | |||||
| PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find_nearest()"); | |||||
| return NULL; | |||||
| } | |||||
| BLI_kdtree_find_nearest(self->obj, co, NULL, &nearest); | |||||
| return kdtree_nearest_to_py_tuple(&nearest); | |||||
| } | |||||
| PyDoc_STRVAR(py_kdtree_find_nearest_n_doc, | |||||
| ".. method:: find_nearest_n(n, co)\n" | |||||
| "\n" | |||||
| " Find nearest `n' points to `co'.\n" | |||||
| "\n" | |||||
| " KdTree.balance() must have been called before using this method.\n" | |||||
| "\n" | |||||
| " :arg n: Number of points to find.\n" | |||||
| " :type n: unsigned int\n" | |||||
| " :arg co: 3d coordinates.\n" | |||||
| " :type co: Sequence of three floats\n" | |||||
| " :return: Returns a list of index, distance, :class:`Vector`.\n" | |||||
| " :rtype: :class:`tuple`\n" | |||||
| ); | |||||
| static PyObject * | |||||
| py_kdtree_find_nearest_n(PyKDTree *self, PyObject *args, PyObject *kwargs) | |||||
| { | |||||
| PyObject *py_list; | |||||
| float co[3]; | |||||
| KDTreeNearest *nearest; | |||||
| unsigned int n; | |||||
| int i, found; | |||||
Not Done Inline Actionsagain, seems odd to support. campbellbarton: again, seems odd to support. | |||||
| const char *keywords[] = {"n", "co", NULL}; | |||||
| if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "IO&:find_nearest_n", (char **) keywords, | |||||
| &n, &py_seq_to_vec3, &co)) { | |||||
| return NULL; | |||||
| } | |||||
| if (self->balanced == 0) { | |||||
| PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find_nearest_n()"); | |||||
| return NULL; | |||||
| } | |||||
| nearest = MEM_mallocN(sizeof(KDTreeNearest) * n, "PyKDTree nearest"); | |||||
| found = BLI_kdtree_find_nearest_n(self->obj, co, NULL, nearest, n); | |||||
Not Done Inline ActionsThe python API could ensure this fairly easily, (just store bool flag)... OR the C api could ensure and assert if it fails. campbellbarton: The python API could ensure this fairly easily, (just store bool flag)... OR the C api could… | |||||
| py_list = PyList_New(found); | |||||
| for (i = 0; i < found; i++) { | |||||
| PyList_SET_ITEM(py_list, i, kdtree_nearest_to_py_tuple(&nearest[i])); | |||||
| } | |||||
| MEM_freeN(nearest); | |||||
| return py_list; | |||||
| } | |||||
| PyDoc_STRVAR(py_kdtree_range_search_doc, | |||||
| ".. method:: range_search(range, co)\n" | |||||
| "\n" | |||||
| " Find all points within `range' of `co'.\n" | |||||
| "\n" | |||||
| " KdTree.balance() must have been called before using this method.\n" | |||||
| "\n" | |||||
| " :arg range: Distance to search for points.\n" | |||||
| " :type range: float\n" | |||||
| " :arg co: 3d coordinates.\n" | |||||
| " :type co: Sequence of three floats\n" | |||||
| " :return: Returns a list of index, distance, :class:`Vector`.\n" | |||||
| " :rtype: :class:`tuple`\n" | |||||
| ); | |||||
| static PyObject * | |||||
Not Done Inline Actionsagain... wouldnt bother with this. campbellbarton: again... wouldnt bother with this. | |||||
| py_kdtree_range_search(PyKDTree *self, PyObject *args, PyObject *kwargs) | |||||
| { | |||||
| PyObject *py_list; | |||||
| float co[3]; | |||||
| KDTreeNearest *nearest = NULL; | |||||
| float range; | |||||
| int i, found; | |||||
| const char *keywords[] = {"range", "co", NULL}; | |||||
| if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "fO&:range_search", (char **) keywords, | |||||
| &range, &py_seq_to_vec3, &co)) { | |||||
| return NULL; | |||||
| } | |||||
| if (self->balanced == 0) { | |||||
| PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling range_search()"); | |||||
| return NULL; | |||||
| } | |||||
| found = BLI_kdtree_range_search(self->obj, co, NULL, &nearest, range); | |||||
| py_list = PyList_New(found); | |||||
| for (i = 0; i < found; i++) { | |||||
| PyList_SET_ITEM(py_list, i, kdtree_nearest_to_py_tuple(&nearest[i])); | |||||
| } | |||||
| MEM_freeN(nearest); | |||||
| return py_list; | |||||
| } | |||||
| static PyMethodDef PyKDTree_methods[] = { | |||||
| {"insert", (PyCFunction)py_kdtree_insert, METH_KEYWORDS|METH_VARARGS, py_kdtree_insert_doc}, | |||||
| {"balance", (PyCFunction)py_kdtree_balance, METH_NOARGS, py_kdtree_balance_doc}, | |||||
| {"find_nearest", (PyCFunction)py_kdtree_find_nearest, METH_KEYWORDS|METH_VARARGS, py_kdtree_find_nearest_doc}, | |||||
| {"find_nearest_n", (PyCFunction)py_kdtree_find_nearest_n, METH_KEYWORDS|METH_VARARGS, py_kdtree_find_nearest_n_doc}, | |||||
| {"range_search", (PyCFunction)py_kdtree_range_search, METH_KEYWORDS|METH_VARARGS, py_kdtree_range_search_doc}, | |||||
| {NULL, NULL, 0, NULL} | |||||
| }; | |||||
| PyDoc_STRVAR(py_kdtree_doc, | |||||
| "Generic 3-dimentional kd-tree.\n" | |||||
| "\n" | |||||
| "KdTree(size) -> new kd-tree initialized to hold `size' items.\n" | |||||
| ); | |||||
| PyTypeObject PyKDTree_Type = { | |||||
| PyVarObject_HEAD_INIT(NULL, 0) | |||||
| (char *) "KDTree", /* tp_name */ | |||||
| sizeof(PyKDTree), /* tp_basicsize */ | |||||
| 0, /* tp_itemsize */ | |||||
| /* methods */ | |||||
| (destructor)PyKDTree__tp_dealloc, /* tp_dealloc */ | |||||
| (printfunc)0, /* tp_print */ | |||||
| (getattrfunc)NULL, /* tp_getattr */ | |||||
| (setattrfunc)NULL, /* tp_setattr */ | |||||
| NULL, /* tp_compare */ | |||||
| (reprfunc)NULL, /* tp_repr */ | |||||
| (PyNumberMethods*)NULL, /* tp_as_number */ | |||||
| (PySequenceMethods*)NULL, /* tp_as_sequence */ | |||||
| (PyMappingMethods*)NULL, /* tp_as_mapping */ | |||||
| (hashfunc)NULL, /* tp_hash */ | |||||
| (ternaryfunc)NULL, /* tp_call */ | |||||
| (reprfunc)NULL, /* tp_str */ | |||||
| (getattrofunc)NULL, /* tp_getattro */ | |||||
| (setattrofunc)NULL, /* tp_setattro */ | |||||
| (PyBufferProcs*)NULL, /* tp_as_buffer */ | |||||
| Py_TPFLAGS_DEFAULT, /* tp_flags */ | |||||
| py_kdtree_doc, /* Documentation string */ | |||||
| (traverseproc)NULL, /* tp_traverse */ | |||||
| (inquiry)NULL, /* tp_clear */ | |||||
| (richcmpfunc)NULL, /* tp_richcompare */ | |||||
| 0, /* tp_weaklistoffset */ | |||||
| (getiterfunc)NULL, /* tp_iter */ | |||||
| (iternextfunc)NULL, /* tp_iternext */ | |||||
| (struct PyMethodDef*)PyKDTree_methods, /* tp_methods */ | |||||
| (struct PyMemberDef*)0, /* tp_members */ | |||||
| 0, /* tp_getset */ | |||||
| NULL, /* tp_base */ | |||||
| NULL, /* tp_dict */ | |||||
| (descrgetfunc)NULL, /* tp_descr_get */ | |||||
| (descrsetfunc)NULL, /* tp_descr_set */ | |||||
| 0, /* tp_dictoffset */ | |||||
| (initproc)PyKDTree__tp_init, /* tp_init */ | |||||
| (allocfunc)PyType_GenericAlloc, /* tp_alloc */ | |||||
| (newfunc)PyType_GenericNew, /* tp_new */ | |||||
| (freefunc)0, /* tp_free */ | |||||
| (inquiry)NULL, /* tp_is_gc */ | |||||
| NULL, /* tp_bases */ | |||||
| NULL, /* tp_mro */ | |||||
| NULL, /* tp_cache */ | |||||
| NULL, /* tp_subclasses */ | |||||
| NULL, /* tp_weaklist */ | |||||
| (destructor) NULL /* tp_del */ | |||||
| }; | |||||
| static struct PyModuleDef kdtree_moduledef = { | |||||
| PyModuleDef_HEAD_INIT, | |||||
| "kdtree", /* m_name */ | |||||
| NULL, /* m_doc */ | |||||
| 0, /* m_size */ | |||||
| NULL, /* m_methods */ | |||||
| NULL, /* m_reload */ | |||||
| NULL, /* m_traverse */ | |||||
| NULL, /* m_clear */ | |||||
| NULL /* m_free */ | |||||
| }; | |||||
| PyObject * | |||||
| BPyInit_kdtree(void) | |||||
| { | |||||
| PyObject *m = PyModule_Create(&kdtree_moduledef); | |||||
| if (m == NULL) { | |||||
| return NULL; | |||||
| } | |||||
| /* Register the 'KDTree' class */ | |||||
| if (PyType_Ready(&PyKDTree_Type)) { | |||||
| return NULL; | |||||
| } | |||||
| PyModule_AddObject(m, (char *) "KDTree", (PyObject *) &PyKDTree_Type); | |||||
| return m; | |||||
| } | |||||
prefer PyTuple_New(), SET_ITEM, means no need to decref after.