This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/python/mathutils/mathutils_kdtree.c
Campbell Barton f29a738e23 PyAPI: use keyword only arguments
Use keyword only arguments for the following functions.

- addon_utils.module_bl_info 2nd arg `info_basis`.
- addon_utils.modules 1st `module_cache`, 2nd arg `refresh`.
- addon_utils.modules_refresh 1st arg `module_cache`.
- bl_app_template_utils.activate 1nd arg `template_id`.
- bl_app_template_utils.import_from_id 2nd arg `ignore_not_found`.
- bl_app_template_utils.import_from_path 2nd arg `ignore_not_found`.
- bl_keymap_utils.keymap_from_toolbar.generate 2nd & 3rd args `use_fallback_keys` & `use_reset`.
- bl_keymap_utils.platform_helpers.keyconfig_data_oskey_from_ctrl 2nd arg `filter_fn`.
- bl_ui_utils.bug_report_url.url_prefill_from_blender 1st arg `addon_info`.
- bmesh.types.BMFace.copy 1st & 2nd args `verts`, `edges`.
- bmesh.types.BMesh.calc_volume 1st arg `signed`.
- bmesh.types.BMesh.from_mesh 2nd..4th args `face_normals`, `use_shape_key`, `shape_key_index`.
- bmesh.types.BMesh.from_object 3rd & 4th args `cage`, `face_normals`.
- bmesh.types.BMesh.transform 2nd arg `filter`.
- bmesh.types.BMesh.update_edit_mesh 2nd & 3rd args `loop_triangles`, `destructive`.
- bmesh.types.{BMVertSeq,BMEdgeSeq,BMFaceSeq}.sort 1st & 2nd arg `key`, `reverse`.
- bmesh.utils.face_split 4th..6th args `coords`, `use_exist`, `example`.
- bpy.data.libraries.load 2nd..4th args `link`, `relative`, `assets_only`.
- bpy.data.user_map 1st..3rd args `subset`, `key_types, `value_types`.
- bpy.msgbus.subscribe_rna 5th arg `options`.
- bpy.path.abspath 2nd & 3rd args `start` & `library`.
- bpy.path.clean_name 2nd arg `replace`.
- bpy.path.ensure_ext 3rd arg `case_sensitive`.
- bpy.path.module_names 2nd arg `recursive`.
- bpy.path.relpath 2nd arg `start`.
- bpy.types.EditBone.transform 2nd & 3rd arg `scale`, `roll`.
- bpy.types.Operator.as_keywords 1st arg `ignore`.
- bpy.types.Struct.{keyframe_insert,keyframe_delete} 2nd..5th args `index`, `frame`, `group`, `options`.
- bpy.types.WindowManager.popup_menu 2nd & 3rd arg `title`, `icon`.
- bpy.types.WindowManager.popup_menu_pie 3rd & 4th arg `title`, `icon`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.blend_paths 1st..3rd args `absolute`, `packed`, `local`.
- bpy.utils.execfile 2nd arg `mod`.
- bpy.utils.keyconfig_set 2nd arg `report`.
- bpy.utils.load_scripts 1st & 2nd `reload_scripts` & `refresh_scripts`.
- bpy.utils.preset_find 3rd & 4th args `display_name`, `ext`.
- bpy.utils.resource_path 2nd & 3rd arg `major`, `minor`.
- bpy.utils.script_paths 1st..4th args `subdir`, `user_pref`, `check_all`, `use_user`.
- bpy.utils.smpte_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.smpte_from_seconds 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.system_resource 2nd arg `subdir`.
- bpy.utils.time_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.time_to_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.units.to_string 4th..6th `precision`, `split_unit`, `compatible_unit`.
- bpy.utils.units.to_value 4th arg `str_ref_unit`.
- bpy.utils.user_resource 2nd & 3rd args `subdir`, `create`
- bpy_extras.view3d_utils.location_3d_to_region_2d 4th arg `default`.
- bpy_extras.view3d_utils.region_2d_to_origin_3d 4th arg `clamp`.
- gpu.offscreen.unbind 1st arg `restore`.
- gpu_extras.batch.batch_for_shader 4th arg `indices`.
- gpu_extras.batch.presets.draw_circle_2d 4th arg `segments`.
- gpu_extras.presets.draw_circle_2d 4th arg `segments`.
- imbuf.types.ImBuf.resize 2nd arg `resize`.
- imbuf.write 2nd arg `filepath`.
- mathutils.kdtree.KDTree.find 2nd arg `filter`.
- nodeitems_utils.NodeCategory 3rd & 4th arg `descriptions`, `items`.
- nodeitems_utils.NodeItem 2nd..4th args `label`, `settings`, `poll`.
- nodeitems_utils.NodeItemCustom 1st & 2nd arg `poll`, `draw`.
- rna_prop_ui.draw 5th arg `use_edit`.
- rna_prop_ui.rna_idprop_ui_get 2nd arg `create`.
- rna_prop_ui.rna_idprop_ui_prop_clear 3rd arg `remove`.
- rna_prop_ui.rna_idprop_ui_prop_get 3rd arg `create`.
- rna_xml.xml2rna 2nd arg `root_rna`.
- rna_xml.xml_file_write 4th arg `skip_typemap`.
2021-06-09 03:05:44 +10:00

466 lines
14 KiB
C

/*
* 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.
*/
/** \file
* \ingroup mathutils
*
* This file defines the 'mathutils.kdtree' module, a general purpose module to access
* blenders kdtree for 3d spatial lookups.
*/
#include <Python.h>
#include "MEM_guardedalloc.h"
#include "BLI_kdtree.h"
#include "BLI_utildefines.h"
#include "../generic/py_capi_utils.h"
#include "../generic/python_utildefines.h"
#include "mathutils.h"
#include "mathutils_kdtree.h" /* own include */
#include "BLI_strict_flags.h"
typedef struct {
PyObject_HEAD KDTree_3d *obj;
uint maxsize;
uint count;
uint count_balance; /* size when we last balanced */
} PyKDTree;
/* -------------------------------------------------------------------- */
/* Utility helper functions */
static void kdtree_nearest_to_py_tuple(const KDTreeNearest_3d *nearest, PyObject *py_retval)
{
BLI_assert(nearest->index >= 0);
BLI_assert(PyTuple_GET_SIZE(py_retval) == 3);
PyTuple_SET_ITEMS(py_retval,
Vector_CreatePyObject(nearest->co, 3, NULL),
PyLong_FromLong(nearest->index),
PyFloat_FromDouble(nearest->dist));
}
static PyObject *kdtree_nearest_to_py(const KDTreeNearest_3d *nearest)
{
PyObject *py_retval;
py_retval = PyTuple_New(3);
kdtree_nearest_to_py_tuple(nearest, py_retval);
return py_retval;
}
static PyObject *kdtree_nearest_to_py_and_check(const KDTreeNearest_3d *nearest)
{
PyObject *py_retval;
py_retval = PyTuple_New(3);
if (nearest->index != -1) {
kdtree_nearest_to_py_tuple(nearest, py_retval);
}
else {
PyC_Tuple_Fill(py_retval, Py_None);
}
return py_retval;
}
/* -------------------------------------------------------------------- */
/* KDTree */
/* annoying since arg parsing won't check overflow */
#define UINT_IS_NEG(n) ((n) > INT_MAX)
static int PyKDTree__tp_init(PyKDTree *self, PyObject *args, PyObject *kwargs)
{
uint maxsize;
const char *keywords[] = {"size", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "I:KDTree", (char **)keywords, &maxsize)) {
return -1;
}
if (UINT_IS_NEG(maxsize)) {
PyErr_SetString(PyExc_ValueError, "negative 'size' given");
return -1;
}
self->obj = BLI_kdtree_3d_new(maxsize);
self->maxsize = maxsize;
self->count = 0;
self->count_balance = 0;
return 0;
}
static void PyKDTree__tp_dealloc(PyKDTree *self)
{
BLI_kdtree_3d_free(self->obj);
Py_TYPE(self)->tp_free((PyObject *)self);
}
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 *py_kdtree_insert(PyKDTree *self, PyObject *args, PyObject *kwargs)
{
PyObject *py_co;
float co[3];
int index;
const char *keywords[] = {"co", "index", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi:insert", (char **)keywords, &py_co, &index)) {
return NULL;
}
if (mathutils_array_parse(co, 3, 3, py_co, "insert: invalid 'co' arg") == -1) {
return NULL;
}
if (index < 0) {
PyErr_SetString(PyExc_ValueError, "negative index given");
return NULL;
}
if (self->count >= self->maxsize) {
PyErr_SetString(PyExc_RuntimeError, "Trying to insert more items than KDTree has room for");
return NULL;
}
BLI_kdtree_3d_insert(self->obj, index, co);
self->count++;
Py_RETURN_NONE;
}
PyDoc_STRVAR(py_kdtree_balance_doc,
".. method:: balance()\n"
"\n"
" Balance the tree.\n"
"\n"
".. note::\n"
"\n"
" This builds the entire tree, avoid calling after each insertion.\n");
static PyObject *py_kdtree_balance(PyKDTree *self)
{
BLI_kdtree_3d_balance(self->obj);
self->count_balance = self->count;
Py_RETURN_NONE;
}
struct PyKDTree_NearestData {
PyObject *py_filter;
bool is_error;
};
static int py_find_nearest_cb(void *user_data, int index, const float co[3], float dist_sq)
{
UNUSED_VARS(co, dist_sq);
struct PyKDTree_NearestData *data = user_data;
PyObject *py_args = PyTuple_New(1);
PyTuple_SET_ITEM(py_args, 0, PyLong_FromLong(index));
PyObject *result = PyObject_CallObject(data->py_filter, py_args);
Py_DECREF(py_args);
if (result) {
bool use_node;
const int ok = PyC_ParseBool(result, &use_node);
Py_DECREF(result);
if (ok) {
return (int)use_node;
}
}
data->is_error = true;
return -1;
}
PyDoc_STRVAR(py_kdtree_find_doc,
".. method:: find(co, filter=None)\n"
"\n"
" Find nearest point to ``co``.\n"
"\n"
" :arg co: 3d coordinates.\n"
" :type co: float triplet\n"
" :arg filter: function which takes an index and returns True for indices to "
"include in the search.\n"
" :type filter: callable\n"
" :return: Returns (:class:`Vector`, index, distance).\n"
" :rtype: :class:`tuple`\n");
static PyObject *py_kdtree_find(PyKDTree *self, PyObject *args, PyObject *kwargs)
{
PyObject *py_co, *py_filter = NULL;
float co[3];
KDTreeNearest_3d nearest;
const char *keywords[] = {"co", "filter", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "O|$O:find", (char **)keywords, &py_co, &py_filter)) {
return NULL;
}
if (mathutils_array_parse(co, 3, 3, py_co, "find: invalid 'co' arg") == -1) {
return NULL;
}
if (self->count != self->count_balance) {
PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find()");
return NULL;
}
nearest.index = -1;
if (py_filter == NULL) {
BLI_kdtree_3d_find_nearest(self->obj, co, &nearest);
}
else {
struct PyKDTree_NearestData data = {0};
data.py_filter = py_filter;
data.is_error = false;
BLI_kdtree_3d_find_nearest_cb(self->obj, co, py_find_nearest_cb, &data, &nearest);
if (data.is_error) {
return NULL;
}
}
return kdtree_nearest_to_py_and_check(&nearest);
}
PyDoc_STRVAR(py_kdtree_find_n_doc,
".. method:: find_n(co, n)\n"
"\n"
" Find nearest ``n`` points to ``co``.\n"
"\n"
" :arg co: 3d coordinates.\n"
" :type co: float triplet\n"
" :arg n: Number of points to find.\n"
" :type n: int\n"
" :return: Returns a list of tuples (:class:`Vector`, index, distance).\n"
" :rtype: :class:`list`\n");
static PyObject *py_kdtree_find_n(PyKDTree *self, PyObject *args, PyObject *kwargs)
{
PyObject *py_list;
PyObject *py_co;
float co[3];
KDTreeNearest_3d *nearest;
uint n;
int i, found;
const char *keywords[] = {"co", "n", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OI:find_n", (char **)keywords, &py_co, &n)) {
return NULL;
}
if (mathutils_array_parse(co, 3, 3, py_co, "find_n: invalid 'co' arg") == -1) {
return NULL;
}
if (UINT_IS_NEG(n)) {
PyErr_SetString(PyExc_RuntimeError, "negative 'n' given");
return NULL;
}
if (self->count != self->count_balance) {
PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find_n()");
return NULL;
}
nearest = MEM_mallocN(sizeof(KDTreeNearest_3d) * n, __func__);
found = BLI_kdtree_3d_find_nearest_n(self->obj, co, nearest, n);
py_list = PyList_New(found);
for (i = 0; i < found; i++) {
PyList_SET_ITEM(py_list, i, kdtree_nearest_to_py(&nearest[i]));
}
MEM_freeN(nearest);
return py_list;
}
PyDoc_STRVAR(py_kdtree_find_range_doc,
".. method:: find_range(co, radius)\n"
"\n"
" Find all points within ``radius`` of ``co``.\n"
"\n"
" :arg co: 3d coordinates.\n"
" :type co: float triplet\n"
" :arg radius: Distance to search for points.\n"
" :type radius: float\n"
" :return: Returns a list of tuples (:class:`Vector`, index, distance).\n"
" :rtype: :class:`list`\n");
static PyObject *py_kdtree_find_range(PyKDTree *self, PyObject *args, PyObject *kwargs)
{
PyObject *py_list;
PyObject *py_co;
float co[3];
KDTreeNearest_3d *nearest = NULL;
float radius;
int i, found;
const char *keywords[] = {"co", "radius", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "Of:find_range", (char **)keywords, &py_co, &radius)) {
return NULL;
}
if (mathutils_array_parse(co, 3, 3, py_co, "find_range: invalid 'co' arg") == -1) {
return NULL;
}
if (radius < 0.0f) {
PyErr_SetString(PyExc_RuntimeError, "negative radius given");
return NULL;
}
if (self->count != self->count_balance) {
PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find_range()");
return NULL;
}
found = BLI_kdtree_3d_range_search(self->obj, co, &nearest, radius);
py_list = PyList_New(found);
for (i = 0; i < found; i++) {
PyList_SET_ITEM(py_list, i, kdtree_nearest_to_py(&nearest[i]));
}
if (nearest) {
MEM_freeN(nearest);
}
return py_list;
}
static PyMethodDef PyKDTree_methods[] = {
{"insert", (PyCFunction)py_kdtree_insert, METH_VARARGS | METH_KEYWORDS, py_kdtree_insert_doc},
{"balance", (PyCFunction)py_kdtree_balance, METH_NOARGS, py_kdtree_balance_doc},
{"find", (PyCFunction)py_kdtree_find, METH_VARARGS | METH_KEYWORDS, py_kdtree_find_doc},
{"find_n", (PyCFunction)py_kdtree_find_n, METH_VARARGS | METH_KEYWORDS, py_kdtree_find_n_doc},
{"find_range",
(PyCFunction)py_kdtree_find_range,
METH_VARARGS | METH_KEYWORDS,
py_kdtree_find_range_doc},
{NULL, NULL, 0, NULL},
};
PyDoc_STRVAR(py_KDtree_doc,
"KdTree(size) -> new kd-tree initialized to hold ``size`` items.\n"
"\n"
".. note::\n"
"\n"
" :class:`KDTree.balance` must have been called before using any of the ``find`` "
"methods.\n");
PyTypeObject PyKDTree_Type = {
PyVarObject_HEAD_INIT(NULL, 0) "KDTree", /* tp_name */
sizeof(PyKDTree), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)PyKDTree__tp_dealloc, /* tp_dealloc */
(printfunc)NULL, /* tp_print */
NULL, /* tp_getattr */
NULL, /* tp_setattr */
NULL, /* tp_compare */
NULL, /* tp_repr */
NULL, /* tp_as_number */
NULL, /* tp_as_sequence */
NULL, /* tp_as_mapping */
NULL, /* tp_hash */
NULL, /* tp_call */
NULL, /* tp_str */
NULL, /* tp_getattro */
NULL, /* tp_setattro */
NULL, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
py_KDtree_doc, /* Documentation string */
NULL, /* tp_traverse */
NULL, /* tp_clear */
NULL, /* tp_richcompare */
0, /* tp_weaklistoffset */
NULL, /* tp_iter */
NULL, /* tp_iternext */
(struct PyMethodDef *)PyKDTree_methods, /* tp_methods */
NULL, /* tp_members */
NULL, /* tp_getset */
NULL, /* tp_base */
NULL, /* tp_dict */
NULL, /* tp_descr_get */
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 */
NULL, /* tp_is_gc */
NULL, /* tp_bases */
NULL, /* tp_mro */
NULL, /* tp_cache */
NULL, /* tp_subclasses */
NULL, /* tp_weaklist */
(destructor)NULL, /* tp_del */
};
PyDoc_STRVAR(py_kdtree_doc, "Generic 3-dimensional kd-tree to perform spatial searches.");
static struct PyModuleDef kdtree_moduledef = {
PyModuleDef_HEAD_INIT,
"mathutils.kdtree", /* m_name */
py_kdtree_doc, /* m_doc */
0, /* m_size */
NULL, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
PyMODINIT_FUNC PyInit_mathutils_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_AddType(m, &PyKDTree_Type);
return m;
}