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 * Contributor(s): Dan Eicher, Campbell Barton
20 * ***** END GPL LICENSE BLOCK *****
23 /** \file blender/python/mathutils/mathutils_kdtree.c
26 * This file defines the 'mathutils.kdtree' module, a general purpose module to access
27 * blenders kdtree for 3d spatial lookups.
32 #include "MEM_guardedalloc.h"
34 #include "BLI_utildefines.h"
35 #include "BLI_kdtree.h"
37 #include "../generic/py_capi_utils.h"
39 #include "mathutils.h"
40 #include "mathutils_kdtree.h" /* own include */
42 #include "BLI_strict_flags.h"
49 unsigned int count_balance; /* size when we last balanced */
53 /* -------------------------------------------------------------------- */
54 /* Utility helper functions */
56 static void kdtree_nearest_to_py_tuple(const KDTreeNearest *nearest, PyObject *py_retval)
58 BLI_assert(nearest->index >= 0);
59 BLI_assert(PyTuple_GET_SIZE(py_retval) == 3);
61 PyTuple_SET_ITEM(py_retval, 0, Vector_CreatePyObject((float *)nearest->co, 3, Py_NEW, NULL));
62 PyTuple_SET_ITEM(py_retval, 1, PyLong_FromLong(nearest->index));
63 PyTuple_SET_ITEM(py_retval, 2, PyFloat_FromDouble(nearest->dist));
66 static PyObject *kdtree_nearest_to_py(const KDTreeNearest *nearest)
70 py_retval = PyTuple_New(3);
72 kdtree_nearest_to_py_tuple(nearest, py_retval);
77 static PyObject *kdtree_nearest_to_py_and_check(const KDTreeNearest *nearest)
81 py_retval = PyTuple_New(3);
83 if (nearest->index != -1) {
84 kdtree_nearest_to_py_tuple(nearest, py_retval);
87 PyC_Tuple_Fill(py_retval, Py_None);
94 /* -------------------------------------------------------------------- */
97 /* annoying since arg parsing won't check overflow */
98 #define UINT_IS_NEG(n) ((n) > INT_MAX)
100 static int PyKDTree__tp_init(PyKDTree *self, PyObject *args, PyObject *kwargs)
102 unsigned int maxsize;
103 const char *keywords[] = {"size", NULL};
105 if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *)"I:KDTree", (char **)keywords, &maxsize)) {
109 if (UINT_IS_NEG(maxsize)) {
110 PyErr_SetString(PyExc_ValueError, "negative 'size' given");
114 self->obj = BLI_kdtree_new(maxsize);
115 self->maxsize = maxsize;
117 self->count_balance = 0;
122 static void PyKDTree__tp_dealloc(PyKDTree *self)
124 BLI_kdtree_free(self->obj);
125 Py_TYPE(self)->tp_free((PyObject *)self);
128 PyDoc_STRVAR(py_kdtree_insert_doc,
129 ".. method:: insert(index, co)\n"
131 " Insert a point into the KDTree.\n"
133 " :arg co: Point 3d position.\n"
134 " :type co: float triplet\n"
135 " :arg index: The index of the point.\n"
136 " :type index: int\n"
138 static PyObject *py_kdtree_insert(PyKDTree *self, PyObject *args, PyObject *kwargs)
143 const char *keywords[] = {"co", "index", NULL};
145 if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "Oi:insert", (char **)keywords,
151 if (mathutils_array_parse(co, 3, 3, py_co, "insert: invalid 'co' arg") == -1)
155 PyErr_SetString(PyExc_ValueError, "negative index given");
159 if (self->count >= self->maxsize) {
160 PyErr_SetString(PyExc_RuntimeError, "Trying to insert more items than KDTree has room for");
164 BLI_kdtree_insert(self->obj, index, co, NULL);
170 PyDoc_STRVAR(py_kdtree_balance_doc,
171 ".. method:: balance()\n"
173 " Balance the tree.\n"
175 static PyObject *py_kdtree_balance(PyKDTree *self)
177 BLI_kdtree_balance(self->obj);
178 self->count_balance = self->count;
182 PyDoc_STRVAR(py_kdtree_find_doc,
183 ".. method:: find(co)\n"
185 " Find nearest point to ``co``.\n"
187 " :arg co: 3d coordinates.\n"
188 " :type co: float triplet\n"
189 " :return: Returns (:class:`Vector`, index, distance).\n"
190 " :rtype: :class:`tuple`\n"
192 static PyObject *py_kdtree_find(PyKDTree *self, PyObject *args, PyObject *kwargs)
196 KDTreeNearest nearest;
197 const char *keywords[] = {"co", NULL};
199 if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "O:find", (char **)keywords,
205 if (mathutils_array_parse(co, 3, 3, py_co, "find: invalid 'co' arg") == -1)
208 if (self->count != self->count_balance) {
209 PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find()");
216 BLI_kdtree_find_nearest(self->obj, co, NULL, &nearest);
218 return kdtree_nearest_to_py_and_check(&nearest);
221 PyDoc_STRVAR(py_kdtree_find_n_doc,
222 ".. method:: find_n(co, n)\n"
224 " Find nearest ``n`` points to ``co``.\n"
226 " :arg co: 3d coordinates.\n"
227 " :type co: float triplet\n"
228 " :arg n: Number of points to find.\n"
230 " :return: Returns a list of tuples (:class:`Vector`, index, distance).\n"
231 " :rtype: :class:`list`\n"
233 static PyObject *py_kdtree_find_n(PyKDTree *self, PyObject *args, PyObject *kwargs)
238 KDTreeNearest *nearest;
241 const char *keywords[] = {"co", "n", NULL};
243 if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "OI:find_n", (char **)keywords,
249 if (mathutils_array_parse(co, 3, 3, py_co, "find_n: invalid 'co' arg") == -1)
252 if (UINT_IS_NEG(n)) {
253 PyErr_SetString(PyExc_RuntimeError, "negative 'n' given");
257 if (self->count != self->count_balance) {
258 PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find_n()");
262 nearest = MEM_mallocN(sizeof(KDTreeNearest) * n, __func__);
264 found = BLI_kdtree_find_nearest_n(self->obj, co, NULL, nearest, n);
266 py_list = PyList_New(found);
268 for (i = 0; i < found; i++) {
269 PyList_SET_ITEM(py_list, i, kdtree_nearest_to_py(&nearest[i]));
277 PyDoc_STRVAR(py_kdtree_find_range_doc,
278 ".. method:: find_range(co, radius)\n"
280 " Find all points within ``radius`` of ``co``.\n"
282 " :arg co: 3d coordinates.\n"
283 " :type co: float triplet\n"
284 " :arg radius: Distance to search for points.\n"
285 " :type radius: float\n"
286 " :return: Returns a list of tuples (:class:`Vector`, index, distance).\n"
287 " :rtype: :class:`list`\n"
289 static PyObject *py_kdtree_find_range(PyKDTree *self, PyObject *args, PyObject *kwargs)
294 KDTreeNearest *nearest = NULL;
298 const char *keywords[] = {"co", "radius", NULL};
300 if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "Of:find_range", (char **)keywords,
306 if (mathutils_array_parse(co, 3, 3, py_co, "find_range: invalid 'co' arg") == -1)
310 PyErr_SetString(PyExc_RuntimeError, "negative radius given");
314 if (self->count != self->count_balance) {
315 PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find_range()");
319 found = BLI_kdtree_range_search(self->obj, co, NULL, &nearest, radius);
321 py_list = PyList_New(found);
323 for (i = 0; i < found; i++) {
324 PyList_SET_ITEM(py_list, i, kdtree_nearest_to_py(&nearest[i]));
335 static PyMethodDef PyKDTree_methods[] = {
336 {"insert", (PyCFunction)py_kdtree_insert, METH_VARARGS | METH_KEYWORDS, py_kdtree_insert_doc},
337 {"balance", (PyCFunction)py_kdtree_balance, METH_NOARGS, py_kdtree_balance_doc},
338 {"find", (PyCFunction)py_kdtree_find, METH_VARARGS | METH_KEYWORDS, py_kdtree_find_doc},
339 {"find_n", (PyCFunction)py_kdtree_find_n, METH_VARARGS | METH_KEYWORDS, py_kdtree_find_n_doc},
340 {"find_range", (PyCFunction)py_kdtree_find_range, METH_VARARGS | METH_KEYWORDS, py_kdtree_find_range_doc},
341 {NULL, NULL, 0, NULL}
344 PyDoc_STRVAR(py_KDtree_doc,
345 "KdTree(size) -> new kd-tree initialized to hold ``size`` items.\n"
349 " :class:`KDTree.balance` must have been called before using any of the ``find`` methods.\n"
351 PyTypeObject PyKDTree_Type = {
352 PyVarObject_HEAD_INIT(NULL, 0)
353 "KDTree", /* tp_name */
354 sizeof(PyKDTree), /* tp_basicsize */
357 (destructor)PyKDTree__tp_dealloc, /* tp_dealloc */
359 NULL, /* tp_getattr */
360 NULL, /* tp_setattr */
361 NULL, /* tp_compare */
363 NULL, /* tp_as_number */
364 NULL, /* tp_as_sequence */
365 NULL, /* tp_as_mapping */
369 NULL, /* tp_getattro */
370 NULL, /* tp_setattro */
371 NULL, /* tp_as_buffer */
372 Py_TPFLAGS_DEFAULT, /* tp_flags */
373 py_KDtree_doc, /* Documentation string */
374 NULL, /* tp_traverse */
376 NULL, /* tp_richcompare */
377 0, /* tp_weaklistoffset */
379 NULL, /* tp_iternext */
380 (struct PyMethodDef *)PyKDTree_methods, /* tp_methods */
381 NULL, /* tp_members */
382 NULL, /* tp_getset */
385 NULL, /* tp_descr_get */
386 NULL, /* tp_descr_set */
387 0, /* tp_dictoffset */
388 (initproc)PyKDTree__tp_init, /* tp_init */
389 (allocfunc)PyType_GenericAlloc, /* tp_alloc */
390 (newfunc)PyType_GenericNew, /* tp_new */
391 (freefunc)0, /* tp_free */
396 NULL, /* tp_subclasses */
397 NULL, /* tp_weaklist */
398 (destructor) NULL /* tp_del */
401 PyDoc_STRVAR(py_kdtree_doc,
402 "Generic 3-dimentional kd-tree to perform spatial searches."
404 static struct PyModuleDef kdtree_moduledef = {
405 PyModuleDef_HEAD_INIT,
406 "mathutils.kdtree", /* m_name */
407 py_kdtree_doc, /* m_doc */
409 NULL, /* m_methods */
411 NULL, /* m_traverse */
416 PyMODINIT_FUNC PyInit_mathutils_kdtree(void)
418 PyObject *m = PyModule_Create(&kdtree_moduledef);
424 /* Register the 'KDTree' class */
425 if (PyType_Ready(&PyKDTree_Type)) {
428 PyModule_AddObject(m, (char *)"KDTree", (PyObject *) &PyKDTree_Type);