📄 kdtree.c
字号:
/*This file is part of ``kdtree'', a library for working with kd-trees.Copyright (C) 2007-2009 John Tsiombikas <nuclear@member.fsf.org>Redistribution and use in source and binary forms, with or withoutmodification, are permitted provided that the following conditions are met:1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIEDWARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OFMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NOEVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENTOF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESSINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER INCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISINGIN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITYOF SUCH DAMAGE.*/#include <stdio.h>#include <stdlib.h>#include <string.h>#include <math.h>#include "kdtree.h"#if defined(WIN32) || defined(__WIN32__)#include <malloc.h>#endif#ifdef USE_LIST_NODE_ALLOCATOR#ifndef NO_PTHREADS#include <pthread.h>#else#ifndef I_WANT_THREAD_BUGS#error "You are compiling with the fast list node allocator, with pthreads disabled! This WILL break if used from multiple threads."#endif /* I want thread bugs */#endif /* pthread support */#endif /* use list node allocator */struct kdnode { double *pos; int dir; void *data; struct kdnode *left, *right; /* negative/positive side */};struct res_node { struct kdnode *item; double dist_sq; struct res_node *next;};struct kdtree { int dim; struct kdnode *root; void (*destr)(void*);};struct kdres { struct kdtree *tree; struct res_node *rlist, *riter; int size;};#define SQ(x) ((x) * (x))static void clear_rec(struct kdnode *node, void (*destr)(void*));static int insert_rec(struct kdnode **node, const double *pos, void *data, int dir, int dim);static int rlist_insert(struct res_node *list, struct kdnode *item, double dist_sq);static void clear_results(struct kdres *set);#ifdef USE_LIST_NODE_ALLOCATORstatic struct res_node *alloc_resnode(void);static void free_resnode(struct res_node*);#else#define alloc_resnode() malloc(sizeof(struct res_node))#define free_resnode(n) free(n)#endifstruct kdtree *kd_create(int k){ struct kdtree *tree; if(!(tree = malloc(sizeof *tree))) { return 0; } tree->dim = k; tree->root = 0; tree->destr = 0; return tree;}void kd_free(struct kdtree *tree){ if(tree) { kd_clear(tree); free(tree); }}static void clear_rec(struct kdnode *node, void (*destr)(void*)){ if(!node) return; clear_rec(node->left, destr); clear_rec(node->right, destr); if(destr) { destr(node->data); } free(node->pos); free(node);}void kd_clear(struct kdtree *tree){ clear_rec(tree->root, tree->destr); tree->root = 0;}void kd_data_destructor(struct kdtree *tree, void (*destr)(void*)){ tree->destr = destr;}static int insert_rec(struct kdnode **nptr, const double *pos, void *data, int dir, int dim){ int new_dir; struct kdnode *node; if(!*nptr) { if(!(node = malloc(sizeof *node))) { return -1; } if(!(node->pos = malloc(dim * sizeof *node->pos))) { free(node); return -1; } memcpy(node->pos, pos, dim * sizeof *node->pos); node->data = data; node->dir = dir; node->left = node->right = 0; *nptr = node; return 0; } node = *nptr; new_dir = (node->dir + 1) % dim; if(pos[node->dir] < node->pos[node->dir]) { return insert_rec(&(*nptr)->left, pos, data, new_dir, dim); } return insert_rec(&(*nptr)->right, pos, data, new_dir, dim);}int kd_insert(struct kdtree *tree, const double *pos, void *data){ return insert_rec(&tree->root, pos, data, 0, tree->dim);}int kd_insertf(struct kdtree *tree, const float *pos, void *data){ static double sbuf[16]; double *bptr, *buf = 0; int res, dim = tree->dim; if(dim > 16) {#ifndef NO_ALLOCA if(dim <= 256) bptr = buf = alloca(dim * sizeof *bptr); else#endif if(!(bptr = buf = malloc(dim * sizeof *bptr))) { return -1; } } else { bptr = sbuf; } while(dim-- > 0) { *bptr++ = *pos++; } res = kd_insert(tree, buf, data);#ifndef NO_ALLOCA if(tree->dim > 256)#else if(tree->dim > 16)#endif free(buf); return res;}int kd_insert3(struct kdtree *tree, double x, double y, double z, void *data){ double buf[3]; buf[0] = x; buf[1] = y; buf[2] = z; return kd_insert(tree, buf, data);}int kd_insert3f(struct kdtree *tree, float x, float y, float z, void *data){ double buf[3]; buf[0] = x; buf[1] = y; buf[2] = z; return kd_insert(tree, buf, data);}static int find_nearest(struct kdnode *node, const double *pos, double range, struct res_node *list, int ordered, int dim){ double dist_sq, dx; int i, ret, added_res = 0; if(!node) return 0; dist_sq = 0; for(i=0; i<dim; i++) { dist_sq += SQ(node->pos[i] - pos[i]); } if(dist_sq <= SQ(range)) { if(rlist_insert(list, node, ordered ? dist_sq : -1.0) == -1) { return -1; } added_res = 1; } dx = pos[node->dir] - node->pos[node->dir]; ret = find_nearest(dx <= 0.0 ? node->left : node->right, pos, range, list, ordered, dim); if(ret >= 0 && fabs(dx) < range) { added_res += ret; ret = find_nearest(dx <= 0.0 ? node->right : node->left, pos, range, list, ordered, dim); } if(ret == -1) { return -1; } added_res += ret; return added_res;}struct kdres *kd_nearest_range(struct kdtree *kd, const double *pos, double range){ int ret; struct kdres *rset; if(!(rset = malloc(sizeof *rset))) { return 0; } if(!(rset->rlist = alloc_resnode())) { free(rset); return 0; } rset->rlist->next = 0; rset->tree = kd; if((ret = find_nearest(kd->root, pos, range, rset->rlist, 0, kd->dim)) == -1) { kd_res_free(rset); return 0; } rset->size = ret; kd_res_rewind(rset); return rset;}struct kdres *kd_nearest_rangef(struct kdtree *kd, const float *pos, float range){ static double sbuf[16]; double *bptr, *buf = 0; int dim = kd->dim; struct kdres *res; if(dim > 16) {#ifndef NO_ALLOCA if(dim <= 256) bptr = buf = alloca(dim * sizeof *bptr); else#endif if(!(bptr = buf = malloc(dim * sizeof *bptr))) { return 0; } } else { bptr = sbuf; } while(dim-- > 0) { *bptr++ = *pos++; } res = kd_nearest_range(kd, buf, range);#ifndef NO_ALLOCA if(kd->dim > 256)#else if(kd->dim > 16)#endif free(buf); return res;}struct kdres *kd_nearest_range3(struct kdtree *tree, double x, double y, double z, double range){ double buf[3]; buf[0] = x; buf[1] = y; buf[2] = z; return kd_nearest_range(tree, buf, range);}struct kdres *kd_nearest_range3f(struct kdtree *tree, float x, float y, float z, float range){ double buf[3]; buf[0] = x; buf[1] = y; buf[2] = z; return kd_nearest_range(tree, buf, range);}void kd_res_free(struct kdres *rset){ clear_results(rset); free_resnode(rset->rlist); free(rset);}int kd_res_size(struct kdres *set){ return (set->size);}void kd_res_rewind(struct kdres *rset){ rset->riter = rset->rlist->next;}int kd_res_end(struct kdres *rset){ return rset->riter == 0;}int kd_res_next(struct kdres *rset){ rset->riter = rset->riter->next; return rset->riter != 0;}void *kd_res_item(struct kdres *rset, double *pos){ if(rset->riter) { if(pos) { memcpy(pos, rset->riter->item->pos, rset->tree->dim * sizeof *pos); } return rset->riter->item->data; } return 0;}void *kd_res_itemf(struct kdres *rset, float *pos){ if(rset->riter) { if(pos) { int i; for(i=0; i<rset->tree->dim; i++) { pos[i] = rset->riter->item->pos[i]; } } return rset->riter->item->data; } return 0;}void *kd_res_item3(struct kdres *rset, double *x, double *y, double *z){ if(rset->riter) { if(*x) *x = rset->riter->item->pos[0]; if(*y) *y = rset->riter->item->pos[1]; if(*z) *z = rset->riter->item->pos[2]; } return 0;}void *kd_res_item3f(struct kdres *rset, float *x, float *y, float *z){ if(rset->riter) { if(*x) *x = rset->riter->item->pos[0]; if(*y) *y = rset->riter->item->pos[1]; if(*z) *z = rset->riter->item->pos[2]; } return 0;}void *kd_res_item_data(struct kdres *set){ return kd_res_item(set, 0);}/* ---- static helpers ---- */#ifdef USE_LIST_NODE_ALLOCATOR/* special list node allocators. */static struct res_node *free_nodes;#ifndef NO_PTHREADSstatic pthread_mutex_t alloc_mutex = PTHREAD_MUTEX_INITIALIZER;#endifstatic struct res_node *alloc_resnode(void){ struct res_node *node;#ifndef NO_PTHREADS pthread_mutex_lock(&alloc_mutex);#endif if(!free_nodes) { node = malloc(sizeof *node); } else { node = free_nodes; free_nodes = free_nodes->next; node->next = 0; }#ifndef NO_PTHREADS pthread_mutex_unlock(&alloc_mutex);#endif return node;}static void free_resnode(struct res_node *node){#ifndef NO_PTHREADS pthread_mutex_lock(&alloc_mutex);#endif node->next = free_nodes; free_nodes = node;#ifndef NO_PTHREADS pthread_mutex_unlock(&alloc_mutex);#endif}#endif /* list node allocator or not *//* inserts the item. if dist_sq is >= 0, then do an ordered insert */static int rlist_insert(struct res_node *list, struct kdnode *item, double dist_sq){ struct res_node *rnode; if(!(rnode = alloc_resnode())) { return -1; } rnode->item = item; rnode->dist_sq = dist_sq; if(dist_sq >= 0.0) { while(list->next && list->next->dist_sq > dist_sq) { list = list->next; } } rnode->next = list->next; list->next = rnode; return 0;}static void clear_results(struct kdres *rset){ struct res_node *tmp, *node = rset->rlist->next; while(node) { tmp = node; node = node->next; free_resnode(tmp); } rset->rlist->next = 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -