Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenlib/intern/BLI_kdtree.c')
-rw-r--r--source/blender/blenlib/intern/BLI_kdtree.c319
1 files changed, 241 insertions, 78 deletions
diff --git a/source/blender/blenlib/intern/BLI_kdtree.c b/source/blender/blenlib/intern/BLI_kdtree.c
index bf470d88578..a81f9b28b83 100644
--- a/source/blender/blenlib/intern/BLI_kdtree.c
+++ b/source/blender/blenlib/intern/BLI_kdtree.c
@@ -32,9 +32,14 @@
#include "BLI_utildefines.h"
#include "BLI_strict_flags.h"
+typedef struct KDTreeNode_head {
+ unsigned int left, right;
+ float co[3];
+ int index;
+} KDTreeNode_head;
typedef struct KDTreeNode {
- struct KDTreeNode *left, *right;
+ unsigned int left, right;
float co[3];
int index;
unsigned int d; /* range is only (0-2) */
@@ -43,7 +48,7 @@ typedef struct KDTreeNode {
struct KDTree {
KDTreeNode *nodes;
unsigned int totnode;
- KDTreeNode *root;
+ unsigned int root;
#ifdef DEBUG
bool is_balanced; /* ensure we call balance first */
unsigned int maxsize; /* max size of the tree */
@@ -54,6 +59,8 @@ struct KDTree {
#define KD_NEAR_ALLOC_INC 100 /* alloc increment for collecting nearest */
#define KD_FOUND_ALLOC_INC 50 /* alloc increment for collecting nearest */
+#define KD_NODE_UNSET ((unsigned int)-1)
+
/**
* Creates or free a kdtree
*/
@@ -64,7 +71,7 @@ KDTree *BLI_kdtree_new(unsigned int maxsize)
tree = MEM_mallocN(sizeof(KDTree), "KDTree");
tree->nodes = MEM_mallocN(sizeof(KDTreeNode) * maxsize, "KDTreeNode");
tree->totnode = 0;
- tree->root = NULL;
+ tree->root = KD_NODE_UNSET;
#ifdef DEBUG
tree->is_balanced = false;
@@ -96,7 +103,7 @@ void BLI_kdtree_insert(KDTree *tree, int index, const float co[3])
/* note, array isn't calloc'd,
* need to initialize all struct members */
- node->left = node->right = NULL;
+ node->left = node->right = KD_NODE_UNSET;
copy_v3_v3(node->co, co);
node->index = index;
node->d = 0;
@@ -106,16 +113,16 @@ void BLI_kdtree_insert(KDTree *tree, int index, const float co[3])
#endif
}
-static KDTreeNode *kdtree_balance(KDTreeNode *nodes, unsigned int totnode, unsigned int axis)
+static unsigned int kdtree_balance(KDTreeNode *nodes, unsigned int totnode, unsigned int axis, const unsigned int ofs)
{
KDTreeNode *node;
float co;
unsigned int left, right, median, i, j;
if (totnode <= 0)
- return NULL;
+ return KD_NODE_UNSET;
else if (totnode == 1)
- return nodes;
+ return 0 + ofs;
/* quicksort style sorting around median */
left = 0;
@@ -134,10 +141,10 @@ static KDTreeNode *kdtree_balance(KDTreeNode *nodes, unsigned int totnode, unsig
if (i >= j)
break;
- SWAP(KDTreeNode, nodes[i], nodes[j]);
+ SWAP(KDTreeNode_head, *(KDTreeNode_head *)&nodes[i], *(KDTreeNode_head *)&nodes[j]);
}
- SWAP(KDTreeNode, nodes[i], nodes[right]);
+ SWAP(KDTreeNode_head, *(KDTreeNode_head *)&nodes[i], *(KDTreeNode_head *)&nodes[right]);
if (i >= median)
right = i - 1;
if (i <= median)
@@ -147,15 +154,16 @@ static KDTreeNode *kdtree_balance(KDTreeNode *nodes, unsigned int totnode, unsig
/* set node and sort subnodes */
node = &nodes[median];
node->d = axis;
- node->left = kdtree_balance(nodes, median, (axis + 1) % 3);
- node->right = kdtree_balance(nodes + median + 1, (totnode - (median + 1)), (axis + 1) % 3);
+ axis = (axis + 1) % 3;
+ node->left = kdtree_balance(nodes, median, axis, ofs);
+ node->right = kdtree_balance(nodes + median + 1, (totnode - (median + 1)), axis, (median + 1) + ofs);
- return node;
+ return median + ofs;
}
void BLI_kdtree_balance(KDTree *tree)
{
- tree->root = kdtree_balance(tree->nodes, tree->totnode, 0);
+ tree->root = kdtree_balance(tree->nodes, tree->totnode, 0, 0);
#ifdef DEBUG
tree->is_balanced = true;
@@ -180,11 +188,11 @@ static float squared_distance(const float v2[3], const float v1[3], const float
return dist;
}
-static KDTreeNode **realloc_nodes(KDTreeNode **stack, unsigned int *totstack, const bool is_alloc)
+static unsigned int *realloc_nodes(unsigned int *stack, unsigned int *totstack, const bool is_alloc)
{
- KDTreeNode **stack_new = MEM_mallocN((*totstack + KD_NEAR_ALLOC_INC) * sizeof(KDTreeNode *), "KDTree.treestack");
- memcpy(stack_new, stack, *totstack * sizeof(KDTreeNode *));
- // memset(stack_new + *totstack, 0, sizeof(KDTreeNode *) * KD_NEAR_ALLOC_INC);
+ unsigned int *stack_new = MEM_mallocN((*totstack + KD_NEAR_ALLOC_INC) * sizeof(unsigned int), "KDTree.treestack");
+ memcpy(stack_new, stack, *totstack * sizeof(unsigned int));
+ // memset(stack_new + *totstack, 0, sizeof(unsigned int) * KD_NEAR_ALLOC_INC);
if (is_alloc)
MEM_freeN(stack);
*totstack += KD_NEAR_ALLOC_INC;
@@ -195,11 +203,12 @@ static KDTreeNode **realloc_nodes(KDTreeNode **stack, unsigned int *totstack, co
* Find nearest returns index, and -1 if no node is found.
*/
int BLI_kdtree_find_nearest(
- KDTree *tree, const float co[3],
+ const KDTree *tree, const float co[3],
KDTreeNearest *r_nearest)
{
- KDTreeNode *root, *node, *min_node;
- KDTreeNode **stack, *defaultstack[KD_STACK_INIT];
+ const KDTreeNode *nodes = tree->nodes;
+ const KDTreeNode *root, *min_node;
+ unsigned int *stack, defaultstack[KD_STACK_INIT];
float min_dist, cur_dist;
unsigned int totstack, cur = 0;
@@ -207,31 +216,31 @@ int BLI_kdtree_find_nearest(
BLI_assert(tree->is_balanced == true);
#endif
- if (UNLIKELY(!tree->root))
+ if (UNLIKELY(tree->root == KD_NODE_UNSET))
return -1;
stack = defaultstack;
totstack = KD_STACK_INIT;
- root = tree->root;
+ root = &nodes[tree->root];
min_node = root;
min_dist = len_squared_v3v3(root->co, co);
if (co[root->d] < root->co[root->d]) {
- if (root->right)
+ if (root->right != KD_NODE_UNSET)
stack[cur++] = root->right;
- if (root->left)
+ if (root->left != KD_NODE_UNSET)
stack[cur++] = root->left;
}
else {
- if (root->left)
+ if (root->left != KD_NODE_UNSET)
stack[cur++] = root->left;
- if (root->right)
+ if (root->right != KD_NODE_UNSET)
stack[cur++] = root->right;
}
while (cur--) {
- node = stack[cur];
+ const KDTreeNode *node = &nodes[stack[cur]];
cur_dist = node->co[node->d] - co[node->d];
@@ -244,10 +253,10 @@ int BLI_kdtree_find_nearest(
min_dist = cur_dist;
min_node = node;
}
- if (node->left)
+ if (node->left != KD_NODE_UNSET)
stack[cur++] = node->left;
}
- if (node->right)
+ if (node->right != KD_NODE_UNSET)
stack[cur++] = node->right;
}
else {
@@ -259,10 +268,10 @@ int BLI_kdtree_find_nearest(
min_dist = cur_dist;
min_node = node;
}
- if (node->right)
+ if (node->right != KD_NODE_UNSET)
stack[cur++] = node->right;
}
- if (node->left)
+ if (node->left != KD_NODE_UNSET)
stack[cur++] = node->left;
}
if (UNLIKELY(cur + 3 > totstack)) {
@@ -282,6 +291,112 @@ int BLI_kdtree_find_nearest(
return min_node->index;
}
+
+/**
+ * A version of #BLI_kdtree_find_nearest which runs a callback
+ * to filter out values.
+ *
+ * \param filter_cb: Filter find results,
+ * Return codes: (1: accept, 0: skip, -1: immediate exit).
+ */
+int BLI_kdtree_find_nearest_cb(
+ const KDTree *tree, const float co[3],
+ int (*filter_cb)(void *user_data, int index, const float co[3], float dist_sq), void *user_data,
+ KDTreeNearest *r_nearest)
+{
+ const KDTreeNode *nodes = tree->nodes;
+ const KDTreeNode *min_node = NULL;
+
+ unsigned int *stack, defaultstack[KD_STACK_INIT];
+ float min_dist = FLT_MAX, cur_dist;
+ unsigned int totstack, cur = 0;
+
+#ifdef DEBUG
+ BLI_assert(tree->is_balanced == true);
+#endif
+
+ if (UNLIKELY(tree->root == KD_NODE_UNSET))
+ return -1;
+
+ stack = defaultstack;
+ totstack = KD_STACK_INIT;
+
+#define NODE_TEST_NEAREST(node) \
+{ \
+ const float dist_sq = len_squared_v3v3((node)->co, co); \
+ if (dist_sq < min_dist) { \
+ const int result = filter_cb(user_data, (node)->index, (node)->co, dist_sq); \
+ if (result == 1) { \
+ min_dist = dist_sq; \
+ min_node = node; \
+ } \
+ else if (result == 0) { \
+ /* pass */ \
+ } \
+ else { \
+ BLI_assert(result == -1); \
+ goto finally; \
+ } \
+ } \
+} ((void)0)
+
+ stack[cur++] = tree->root;
+
+ while (cur--) {
+ const KDTreeNode *node = &nodes[stack[cur]];
+
+ cur_dist = node->co[node->d] - co[node->d];
+
+ if (cur_dist < 0.0f) {
+ cur_dist = -cur_dist * cur_dist;
+
+ if (-cur_dist < min_dist) {
+ NODE_TEST_NEAREST(node);
+
+ if (node->left != KD_NODE_UNSET)
+ stack[cur++] = node->left;
+ }
+ if (node->right != KD_NODE_UNSET)
+ stack[cur++] = node->right;
+ }
+ else {
+ cur_dist = cur_dist * cur_dist;
+
+ if (cur_dist < min_dist) {
+ NODE_TEST_NEAREST(node);
+
+ if (node->right != KD_NODE_UNSET)
+ stack[cur++] = node->right;
+ }
+ if (node->left != KD_NODE_UNSET)
+ stack[cur++] = node->left;
+ }
+ if (UNLIKELY(cur + 3 > totstack)) {
+ stack = realloc_nodes(stack, &totstack, defaultstack != stack);
+ }
+ }
+
+#undef NODE_TEST_NEAREST
+
+
+finally:
+ if (stack != defaultstack)
+ MEM_freeN(stack);
+
+ if (min_node) {
+ if (r_nearest) {
+ r_nearest->index = min_node->index;
+ r_nearest->dist = sqrtf(min_dist);
+ copy_v3_v3(r_nearest->co, min_node->co);
+ }
+
+ return min_node->index;
+ }
+ else {
+ return -1;
+ }
+}
+
static void add_nearest(KDTreeNearest *ptn, unsigned int *found, unsigned int n, int index,
float dist, const float *co)
{
@@ -308,12 +423,13 @@ static void add_nearest(KDTreeNearest *ptn, unsigned int *found, unsigned int n,
* \param r_nearest An array of nearest, sized at least \a n.
*/
int BLI_kdtree_find_nearest_n__normal(
- KDTree *tree, const float co[3], const float nor[3],
+ const KDTree *tree, const float co[3], const float nor[3],
KDTreeNearest r_nearest[],
unsigned int n)
{
- KDTreeNode *root, *node = NULL;
- KDTreeNode **stack, *defaultstack[KD_STACK_INIT];
+ const KDTreeNode *nodes = tree->nodes;
+ const KDTreeNode *root;
+ unsigned int *stack, defaultstack[KD_STACK_INIT];
float cur_dist;
unsigned int totstack, cur = 0;
unsigned int i, found = 0;
@@ -322,32 +438,32 @@ int BLI_kdtree_find_nearest_n__normal(
BLI_assert(tree->is_balanced == true);
#endif
- if (UNLIKELY(!tree->root || n == 0))
+ if (UNLIKELY((tree->root == KD_NODE_UNSET) || n == 0))
return 0;
stack = defaultstack;
totstack = KD_STACK_INIT;
- root = tree->root;
+ root = &nodes[tree->root];
cur_dist = squared_distance(root->co, co, nor);
add_nearest(r_nearest, &found, n, root->index, cur_dist, root->co);
if (co[root->d] < root->co[root->d]) {
- if (root->right)
+ if (root->right != KD_NODE_UNSET)
stack[cur++] = root->right;
- if (root->left)
+ if (root->left != KD_NODE_UNSET)
stack[cur++] = root->left;
}
else {
- if (root->left)
+ if (root->left != KD_NODE_UNSET)
stack[cur++] = root->left;
- if (root->right)
+ if (root->right != KD_NODE_UNSET)
stack[cur++] = root->right;
}
while (cur--) {
- node = stack[cur];
+ const KDTreeNode *node = &nodes[stack[cur]];
cur_dist = node->co[node->d] - co[node->d];
@@ -360,10 +476,10 @@ int BLI_kdtree_find_nearest_n__normal(
if (found < n || cur_dist < r_nearest[found - 1].dist)
add_nearest(r_nearest, &found, n, node->index, cur_dist, node->co);
- if (node->left)
+ if (node->left != KD_NODE_UNSET)
stack[cur++] = node->left;
}
- if (node->right)
+ if (node->right != KD_NODE_UNSET)
stack[cur++] = node->right;
}
else {
@@ -374,10 +490,10 @@ int BLI_kdtree_find_nearest_n__normal(
if (found < n || cur_dist < r_nearest[found - 1].dist)
add_nearest(r_nearest, &found, n, node->index, cur_dist, node->co);
- if (node->right)
+ if (node->right != KD_NODE_UNSET)
stack[cur++] = node->right;
}
- if (node->left)
+ if (node->left != KD_NODE_UNSET)
stack[cur++] = node->left;
}
if (UNLIKELY(cur + 3 > totstack)) {
@@ -434,65 +550,47 @@ static void add_in_range(
* Remember to free nearest after use!
*/
int BLI_kdtree_range_search__normal(
- KDTree *tree, const float co[3], const float nor[3],
+ const KDTree *tree, const float co[3], const float nor[3],
KDTreeNearest **r_nearest, float range)
{
- KDTreeNode *root, *node = NULL;
- KDTreeNode **stack, *defaultstack[KD_STACK_INIT];
+ const KDTreeNode *nodes = tree->nodes;
+ unsigned int *stack, defaultstack[KD_STACK_INIT];
KDTreeNearest *foundstack = NULL;
- float range2 = range * range, dist2;
+ float range_sq = range * range, dist_sq;
unsigned int totstack, cur = 0, found = 0, totfoundstack = 0;
#ifdef DEBUG
BLI_assert(tree->is_balanced == true);
#endif
- if (UNLIKELY(!tree->root))
+ if (UNLIKELY(tree->root == KD_NODE_UNSET))
return 0;
stack = defaultstack;
totstack = KD_STACK_INIT;
- root = tree->root;
-
- if (co[root->d] + range < root->co[root->d]) {
- if (root->left)
- stack[cur++] = root->left;
- }
- else if (co[root->d] - range > root->co[root->d]) {
- if (root->right)
- stack[cur++] = root->right;
- }
- else {
- dist2 = squared_distance(root->co, co, nor);
- if (dist2 <= range2)
- add_in_range(&foundstack, &totfoundstack, found++, root->index, dist2, root->co);
-
- if (root->left)
- stack[cur++] = root->left;
- if (root->right)
- stack[cur++] = root->right;
- }
+ stack[cur++] = tree->root;
while (cur--) {
- node = stack[cur];
+ const KDTreeNode *node = &nodes[stack[cur]];
if (co[node->d] + range < node->co[node->d]) {
- if (node->left)
+ if (node->left != KD_NODE_UNSET)
stack[cur++] = node->left;
}
else if (co[node->d] - range > node->co[node->d]) {
- if (node->right)
+ if (node->right != KD_NODE_UNSET)
stack[cur++] = node->right;
}
else {
- dist2 = squared_distance(node->co, co, nor);
- if (dist2 <= range2)
- add_in_range(&foundstack, &totfoundstack, found++, node->index, dist2, node->co);
+ dist_sq = squared_distance(node->co, co, nor);
+ if (dist_sq <= range_sq) {
+ add_in_range(&foundstack, &totfoundstack, found++, node->index, dist_sq, node->co);
+ }
- if (node->left)
+ if (node->left != KD_NODE_UNSET)
stack[cur++] = node->left;
- if (node->right)
+ if (node->right != KD_NODE_UNSET)
stack[cur++] = node->right;
}
@@ -511,3 +609,68 @@ int BLI_kdtree_range_search__normal(
return (int)found;
}
+
+/**
+ * A version of #BLI_kdtree_range_search which runs a callback
+ * instead of allocating an array.
+ *
+ * \param search_cb: Called for every node found in \a range, false return value performs an early exit.
+ *
+ * \note the order of calls isn't sorted based on distance.
+ */
+void BLI_kdtree_range_search_cb(
+ const KDTree *tree, const float co[3], float range,
+ bool (*search_cb)(void *user_data, int index, const float co[3], float dist_sq), void *user_data)
+{
+ const KDTreeNode *nodes = tree->nodes;
+
+ unsigned int *stack, defaultstack[KD_STACK_INIT];
+ float range_sq = range * range, dist_sq;
+ unsigned int totstack, cur = 0;
+
+#ifdef DEBUG
+ BLI_assert(tree->is_balanced == true);
+#endif
+
+ if (UNLIKELY(tree->root == KD_NODE_UNSET))
+ return;
+
+ stack = defaultstack;
+ totstack = KD_STACK_INIT;
+
+ stack[cur++] = tree->root;
+
+ while (cur--) {
+ const KDTreeNode *node = &nodes[stack[cur]];
+
+ if (co[node->d] + range < node->co[node->d]) {
+ if (node->left != KD_NODE_UNSET)
+ stack[cur++] = node->left;
+ }
+ else if (co[node->d] - range > node->co[node->d]) {
+ if (node->right != KD_NODE_UNSET)
+ stack[cur++] = node->right;
+ }
+ else {
+ dist_sq = len_squared_v3v3(node->co, co);
+ if (dist_sq <= range_sq) {
+ if (search_cb(user_data, node->index, node->co, dist_sq) == false) {
+ goto finally;
+ }
+ }
+
+ if (node->left != KD_NODE_UNSET)
+ stack[cur++] = node->left;
+ if (node->right != KD_NODE_UNSET)
+ stack[cur++] = node->right;
+ }
+
+ if (UNLIKELY(cur + 3 > totstack)) {
+ stack = realloc_nodes(stack, &totstack, defaultstack != stack);
+ }
+ }
+
+finally:
+ if (stack != defaultstack)
+ MEM_freeN(stack);
+}