Blender  V2.93
mathutils_kdtree.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
24 #include <Python.h>
25 
26 #include "MEM_guardedalloc.h"
27 
28 #include "BLI_kdtree.h"
29 #include "BLI_utildefines.h"
30 
31 #include "../generic/py_capi_utils.h"
32 #include "../generic/python_utildefines.h"
33 
34 #include "mathutils.h"
35 #include "mathutils_kdtree.h" /* own include */
36 
37 #include "BLI_strict_flags.h"
38 
39 typedef struct {
40  PyObject_HEAD KDTree_3d *obj;
43  uint count_balance; /* size when we last balanced */
44 } PyKDTree;
45 
46 /* -------------------------------------------------------------------- */
47 /* Utility helper functions */
48 
49 static void kdtree_nearest_to_py_tuple(const KDTreeNearest_3d *nearest, PyObject *py_retval)
50 {
51  BLI_assert(nearest->index >= 0);
52  BLI_assert(PyTuple_GET_SIZE(py_retval) == 3);
53 
54  PyTuple_SET_ITEMS(py_retval,
55  Vector_CreatePyObject((float *)nearest->co, 3, NULL),
56  PyLong_FromLong(nearest->index),
57  PyFloat_FromDouble(nearest->dist));
58 }
59 
60 static PyObject *kdtree_nearest_to_py(const KDTreeNearest_3d *nearest)
61 {
62  PyObject *py_retval;
63 
64  py_retval = PyTuple_New(3);
65 
66  kdtree_nearest_to_py_tuple(nearest, py_retval);
67 
68  return py_retval;
69 }
70 
71 static PyObject *kdtree_nearest_to_py_and_check(const KDTreeNearest_3d *nearest)
72 {
73  PyObject *py_retval;
74 
75  py_retval = PyTuple_New(3);
76 
77  if (nearest->index != -1) {
78  kdtree_nearest_to_py_tuple(nearest, py_retval);
79  }
80  else {
81  PyC_Tuple_Fill(py_retval, Py_None);
82  }
83 
84  return py_retval;
85 }
86 
87 /* -------------------------------------------------------------------- */
88 /* KDTree */
89 
90 /* annoying since arg parsing won't check overflow */
91 #define UINT_IS_NEG(n) ((n) > INT_MAX)
92 
93 static int PyKDTree__tp_init(PyKDTree *self, PyObject *args, PyObject *kwargs)
94 {
95  uint maxsize;
96  const char *keywords[] = {"size", NULL};
97 
98  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "I:KDTree", (char **)keywords, &maxsize)) {
99  return -1;
100  }
101 
102  if (UINT_IS_NEG(maxsize)) {
103  PyErr_SetString(PyExc_ValueError, "negative 'size' given");
104  return -1;
105  }
106 
107  self->obj = BLI_kdtree_3d_new(maxsize);
108  self->maxsize = maxsize;
109  self->count = 0;
110  self->count_balance = 0;
111 
112  return 0;
113 }
114 
115 static void PyKDTree__tp_dealloc(PyKDTree *self)
116 {
117  BLI_kdtree_3d_free(self->obj);
118  Py_TYPE(self)->tp_free((PyObject *)self);
119 }
120 
121 PyDoc_STRVAR(py_kdtree_insert_doc,
122  ".. method:: insert(co, index)\n"
123  "\n"
124  " Insert a point into the KDTree.\n"
125  "\n"
126  " :arg co: Point 3d position.\n"
127  " :type co: float triplet\n"
128  " :arg index: The index of the point.\n"
129  " :type index: int\n");
130 static PyObject *py_kdtree_insert(PyKDTree *self, PyObject *args, PyObject *kwargs)
131 {
132  PyObject *py_co;
133  float co[3];
134  int index;
135  const char *keywords[] = {"co", "index", NULL};
136 
137  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi:insert", (char **)keywords, &py_co, &index)) {
138  return NULL;
139  }
140 
141  if (mathutils_array_parse(co, 3, 3, py_co, "insert: invalid 'co' arg") == -1) {
142  return NULL;
143  }
144 
145  if (index < 0) {
146  PyErr_SetString(PyExc_ValueError, "negative index given");
147  return NULL;
148  }
149 
150  if (self->count >= self->maxsize) {
151  PyErr_SetString(PyExc_RuntimeError, "Trying to insert more items than KDTree has room for");
152  return NULL;
153  }
154 
155  BLI_kdtree_3d_insert(self->obj, index, co);
156  self->count++;
157 
158  Py_RETURN_NONE;
159 }
160 
161 PyDoc_STRVAR(py_kdtree_balance_doc,
162  ".. method:: balance()\n"
163  "\n"
164  " Balance the tree.\n"
165  "\n"
166  ".. note::\n"
167  "\n"
168  " This builds the entire tree, avoid calling after each insertion.\n");
169 static PyObject *py_kdtree_balance(PyKDTree *self)
170 {
171  BLI_kdtree_3d_balance(self->obj);
172  self->count_balance = self->count;
173  Py_RETURN_NONE;
174 }
175 
177  PyObject *py_filter;
178  bool is_error;
179 };
180 
181 static int py_find_nearest_cb(void *user_data, int index, const float co[3], float dist_sq)
182 {
183  UNUSED_VARS(co, dist_sq);
184 
186 
187  PyObject *py_args = PyTuple_New(1);
188  PyTuple_SET_ITEM(py_args, 0, PyLong_FromLong(index));
189  PyObject *result = PyObject_CallObject(data->py_filter, py_args);
190  Py_DECREF(py_args);
191 
192  if (result) {
193  bool use_node;
194  const int ok = PyC_ParseBool(result, &use_node);
195  Py_DECREF(result);
196  if (ok) {
197  return (int)use_node;
198  }
199  }
200 
201  data->is_error = true;
202  return -1;
203 }
204 
205 PyDoc_STRVAR(py_kdtree_find_doc,
206  ".. method:: find(co, filter=None)\n"
207  "\n"
208  " Find nearest point to ``co``.\n"
209  "\n"
210  " :arg co: 3d coordinates.\n"
211  " :type co: float triplet\n"
212  " :arg filter: function which takes an index and returns True for indices to "
213  "include in the search.\n"
214  " :type filter: callable\n"
215  " :return: Returns (:class:`Vector`, index, distance).\n"
216  " :rtype: :class:`tuple`\n");
217 static PyObject *py_kdtree_find(PyKDTree *self, PyObject *args, PyObject *kwargs)
218 {
219  PyObject *py_co, *py_filter = NULL;
220  float co[3];
221  KDTreeNearest_3d nearest;
222  const char *keywords[] = {"co", "filter", NULL};
223 
224  if (!PyArg_ParseTupleAndKeywords(
225  args, kwargs, "O|O:find", (char **)keywords, &py_co, &py_filter)) {
226  return NULL;
227  }
228 
229  if (mathutils_array_parse(co, 3, 3, py_co, "find: invalid 'co' arg") == -1) {
230  return NULL;
231  }
232 
233  if (self->count != self->count_balance) {
234  PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find()");
235  return NULL;
236  }
237 
238  nearest.index = -1;
239 
240  if (py_filter == NULL) {
241  BLI_kdtree_3d_find_nearest(self->obj, co, &nearest);
242  }
243  else {
244  struct PyKDTree_NearestData data = {0};
245 
246  data.py_filter = py_filter;
247  data.is_error = false;
248 
249  BLI_kdtree_3d_find_nearest_cb(self->obj, co, py_find_nearest_cb, &data, &nearest);
250 
251  if (data.is_error) {
252  return NULL;
253  }
254  }
255 
256  return kdtree_nearest_to_py_and_check(&nearest);
257 }
258 
259 PyDoc_STRVAR(py_kdtree_find_n_doc,
260  ".. method:: find_n(co, n)\n"
261  "\n"
262  " Find nearest ``n`` points to ``co``.\n"
263  "\n"
264  " :arg co: 3d coordinates.\n"
265  " :type co: float triplet\n"
266  " :arg n: Number of points to find.\n"
267  " :type n: int\n"
268  " :return: Returns a list of tuples (:class:`Vector`, index, distance).\n"
269  " :rtype: :class:`list`\n");
270 static PyObject *py_kdtree_find_n(PyKDTree *self, PyObject *args, PyObject *kwargs)
271 {
272  PyObject *py_list;
273  PyObject *py_co;
274  float co[3];
275  KDTreeNearest_3d *nearest;
276  uint n;
277  int i, found;
278  const char *keywords[] = {"co", "n", NULL};
279 
280  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OI:find_n", (char **)keywords, &py_co, &n)) {
281  return NULL;
282  }
283 
284  if (mathutils_array_parse(co, 3, 3, py_co, "find_n: invalid 'co' arg") == -1) {
285  return NULL;
286  }
287 
288  if (UINT_IS_NEG(n)) {
289  PyErr_SetString(PyExc_RuntimeError, "negative 'n' given");
290  return NULL;
291  }
292 
293  if (self->count != self->count_balance) {
294  PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find_n()");
295  return NULL;
296  }
297 
298  nearest = MEM_mallocN(sizeof(KDTreeNearest_3d) * n, __func__);
299 
300  found = BLI_kdtree_3d_find_nearest_n(self->obj, co, nearest, n);
301 
302  py_list = PyList_New(found);
303 
304  for (i = 0; i < found; i++) {
305  PyList_SET_ITEM(py_list, i, kdtree_nearest_to_py(&nearest[i]));
306  }
307 
308  MEM_freeN(nearest);
309 
310  return py_list;
311 }
312 
313 PyDoc_STRVAR(py_kdtree_find_range_doc,
314  ".. method:: find_range(co, radius)\n"
315  "\n"
316  " Find all points within ``radius`` of ``co``.\n"
317  "\n"
318  " :arg co: 3d coordinates.\n"
319  " :type co: float triplet\n"
320  " :arg radius: Distance to search for points.\n"
321  " :type radius: float\n"
322  " :return: Returns a list of tuples (:class:`Vector`, index, distance).\n"
323  " :rtype: :class:`list`\n");
324 static PyObject *py_kdtree_find_range(PyKDTree *self, PyObject *args, PyObject *kwargs)
325 {
326  PyObject *py_list;
327  PyObject *py_co;
328  float co[3];
329  KDTreeNearest_3d *nearest = NULL;
330  float radius;
331  int i, found;
332 
333  const char *keywords[] = {"co", "radius", NULL};
334 
335  if (!PyArg_ParseTupleAndKeywords(
336  args, kwargs, "Of:find_range", (char **)keywords, &py_co, &radius)) {
337  return NULL;
338  }
339 
340  if (mathutils_array_parse(co, 3, 3, py_co, "find_range: invalid 'co' arg") == -1) {
341  return NULL;
342  }
343 
344  if (radius < 0.0f) {
345  PyErr_SetString(PyExc_RuntimeError, "negative radius given");
346  return NULL;
347  }
348 
349  if (self->count != self->count_balance) {
350  PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find_range()");
351  return NULL;
352  }
353 
354  found = BLI_kdtree_3d_range_search(self->obj, co, &nearest, radius);
355 
356  py_list = PyList_New(found);
357 
358  for (i = 0; i < found; i++) {
359  PyList_SET_ITEM(py_list, i, kdtree_nearest_to_py(&nearest[i]));
360  }
361 
362  if (nearest) {
363  MEM_freeN(nearest);
364  }
365 
366  return py_list;
367 }
368 
369 static PyMethodDef PyKDTree_methods[] = {
370  {"insert", (PyCFunction)py_kdtree_insert, METH_VARARGS | METH_KEYWORDS, py_kdtree_insert_doc},
371  {"balance", (PyCFunction)py_kdtree_balance, METH_NOARGS, py_kdtree_balance_doc},
372  {"find", (PyCFunction)py_kdtree_find, METH_VARARGS | METH_KEYWORDS, py_kdtree_find_doc},
373  {"find_n", (PyCFunction)py_kdtree_find_n, METH_VARARGS | METH_KEYWORDS, py_kdtree_find_n_doc},
374  {"find_range",
375  (PyCFunction)py_kdtree_find_range,
376  METH_VARARGS | METH_KEYWORDS,
377  py_kdtree_find_range_doc},
378  {NULL, NULL, 0, NULL},
379 };
380 
381 PyDoc_STRVAR(py_KDtree_doc,
382  "KdTree(size) -> new kd-tree initialized to hold ``size`` items.\n"
383  "\n"
384  ".. note::\n"
385  "\n"
386  " :class:`KDTree.balance` must have been called before using any of the ``find`` "
387  "methods.\n");
388 PyTypeObject PyKDTree_Type = {
389  PyVarObject_HEAD_INIT(NULL, 0) "KDTree", /* tp_name */
390  sizeof(PyKDTree), /* tp_basicsize */
391  0, /* tp_itemsize */
392  /* methods */
393  (destructor)PyKDTree__tp_dealloc, /* tp_dealloc */
394  (printfunc)NULL, /* tp_print */
395  NULL, /* tp_getattr */
396  NULL, /* tp_setattr */
397  NULL, /* tp_compare */
398  NULL, /* tp_repr */
399  NULL, /* tp_as_number */
400  NULL, /* tp_as_sequence */
401  NULL, /* tp_as_mapping */
402  NULL, /* tp_hash */
403  NULL, /* tp_call */
404  NULL, /* tp_str */
405  NULL, /* tp_getattro */
406  NULL, /* tp_setattro */
407  NULL, /* tp_as_buffer */
408  Py_TPFLAGS_DEFAULT, /* tp_flags */
409  py_KDtree_doc, /* Documentation string */
410  NULL, /* tp_traverse */
411  NULL, /* tp_clear */
412  NULL, /* tp_richcompare */
413  0, /* tp_weaklistoffset */
414  NULL, /* tp_iter */
415  NULL, /* tp_iternext */
416  (struct PyMethodDef *)PyKDTree_methods, /* tp_methods */
417  NULL, /* tp_members */
418  NULL, /* tp_getset */
419  NULL, /* tp_base */
420  NULL, /* tp_dict */
421  NULL, /* tp_descr_get */
422  NULL, /* tp_descr_set */
423  0, /* tp_dictoffset */
424  (initproc)PyKDTree__tp_init, /* tp_init */
425  (allocfunc)PyType_GenericAlloc, /* tp_alloc */
426  (newfunc)PyType_GenericNew, /* tp_new */
427  (freefunc)0, /* tp_free */
428  NULL, /* tp_is_gc */
429  NULL, /* tp_bases */
430  NULL, /* tp_mro */
431  NULL, /* tp_cache */
432  NULL, /* tp_subclasses */
433  NULL, /* tp_weaklist */
434  (destructor)NULL, /* tp_del */
435 };
436 
437 PyDoc_STRVAR(py_kdtree_doc, "Generic 3-dimensional kd-tree to perform spatial searches.");
438 static struct PyModuleDef kdtree_moduledef = {
439  PyModuleDef_HEAD_INIT,
440  "mathutils.kdtree", /* m_name */
441  py_kdtree_doc, /* m_doc */
442  0, /* m_size */
443  NULL, /* m_methods */
444  NULL, /* m_reload */
445  NULL, /* m_traverse */
446  NULL, /* m_clear */
447  NULL, /* m_free */
448 };
449 
450 PyMODINIT_FUNC PyInit_mathutils_kdtree(void)
451 {
452  PyObject *m = PyModule_Create(&kdtree_moduledef);
453 
454  if (m == NULL) {
455  return NULL;
456  }
457 
458  /* Register the 'KDTree' class */
459  if (PyType_Ready(&PyKDTree_Type)) {
460  return NULL;
461  }
462  PyModule_AddType(m, &PyKDTree_Type);
463 
464  return m;
465 }
#define BLI_assert(a)
Definition: BLI_assert.h:58
A kd-tree for nearest neighbor search.
Strict compiler flags for areas of code we want to ensure don't do conversions without us knowing abo...
unsigned int uint
Definition: BLI_sys_types.h:83
#define UNUSED_VARS(...)
Read Guarded memory(de)allocation.
PyObject * self
Definition: bpy_driver.c:185
void * user_data
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
int mathutils_array_parse(float *array, int array_min, int array_max, PyObject *value, const char *error_prefix)
Definition: mathutils.c:118
PyObject * Vector_CreatePyObject(const float *vec, const int size, PyTypeObject *base_type)
static int PyKDTree__tp_init(PyKDTree *self, PyObject *args, PyObject *kwargs)
static void kdtree_nearest_to_py_tuple(const KDTreeNearest_3d *nearest, PyObject *py_retval)
#define UINT_IS_NEG(n)
static PyObject * py_kdtree_balance(PyKDTree *self)
PyTypeObject PyKDTree_Type
PyMODINIT_FUNC PyInit_mathutils_kdtree(void)
static PyObject * py_kdtree_find_range(PyKDTree *self, PyObject *args, PyObject *kwargs)
static PyObject * kdtree_nearest_to_py_and_check(const KDTreeNearest_3d *nearest)
static struct PyModuleDef kdtree_moduledef
static void PyKDTree__tp_dealloc(PyKDTree *self)
static PyMethodDef PyKDTree_methods[]
static int py_find_nearest_cb(void *user_data, int index, const float co[3], float dist_sq)
static PyObject * py_kdtree_find_n(PyKDTree *self, PyObject *args, PyObject *kwargs)
static PyObject * py_kdtree_find(PyKDTree *self, PyObject *args, PyObject *kwargs)
PyDoc_STRVAR(py_kdtree_insert_doc, ".. method:: insert(co, index)\n" "\n" " Insert a point into the KDTree.\n" "\n" " :arg co: Point 3d position.\n" " :type co: float triplet\n" " :arg index: The index of the point.\n" " :type index: int\n")
static PyObject * kdtree_nearest_to_py(const KDTreeNearest_3d *nearest)
static PyObject * py_kdtree_insert(PyKDTree *self, PyObject *args, PyObject *kwargs)
int PyC_ParseBool(PyObject *o, void *p)
void PyC_Tuple_Fill(PyObject *tuple, PyObject *value)
#define PyTuple_SET_ITEMS(op_arg,...)
uint count_balance
PyObject_HEAD KDTree_3d * obj