1 /****************************************************************\
2 *                                                                *
3 *  Trees for 2D range searching.                                 *
4 *                                                                *
5 *  Guy St.C. Slater..   mailto:guy@ebi.ac.uk                     *
6 *  Copyright (C) 2000-2009.  All Rights Reserved.                *
7 *                                                                *
8 *  This source code is distributed under the terms of the        *
9 *  GNU General Public License, version 3. See the file COPYING   *
10 *  or http://www.gnu.org/licenses/gpl.txt for details            *
11 *                                                                *
12 *  If you use this code, please keep this notice intact.         *
13 *                                                                *
14 \****************************************************************/
15 
16 #include <stdlib.h> /* For lrand48() */
17 
18 #include "rangetree.h"
19 
RangeTree_recent_data_compare(gconstpointer a,gconstpointer b)20 static gint RangeTree_recent_data_compare(gconstpointer a,
21                                           gconstpointer b){
22     register RangeTree_Node *rtn_a = (RangeTree_Node*)a,
23                             *rtn_b = (RangeTree_Node*)b;
24     if(rtn_a->x == rtn_b->x)
25         return rtn_b->y - rtn_a->y;
26     return rtn_b->x - rtn_a->x;
27     }
28 
RangeTree_create(void)29 RangeTree *RangeTree_create(void){
30     register RangeTree *rt = g_new(RangeTree, 1);
31     rt->root = NULL;
32     rt->recent_data = g_tree_new(RangeTree_recent_data_compare);
33     rt->node_recycle = RecycleBin_create("RangeTree_Node",
34                                          sizeof(RangeTree_Node), 256);
35     return rt;
36     }
37 
RangeTree_destroy(RangeTree * rt,RangeTree_FreeFunc rtff,gpointer user_data)38 void RangeTree_destroy(RangeTree *rt, RangeTree_FreeFunc rtff,
39                               gpointer user_data){
40     g_tree_destroy(rt->recent_data);
41     RecycleBin_destroy(rt->node_recycle);
42     g_free(rt);
43     return;
44     }
45 
RangeTree_add(RangeTree * rt,gint x,gint y,gpointer info)46 void RangeTree_add(RangeTree *rt, gint x, gint y, gpointer info){
47     register RangeTree_Node *rtn = RecycleBin_alloc(rt->node_recycle);
48     g_assert(!RangeTree_check_pos(rt, x, y));
49     rtn->x = x;
50     rtn->y = y;
51     rtn->info = info;
52     rtn->left = NULL;
53     rtn->right = NULL;
54     g_assert(!g_tree_lookup(rt->recent_data, rtn));
55     g_tree_insert(rt->recent_data, rtn, rtn);
56     return;
57     }
58 
59 typedef struct {
60     gint tl_x;
61     gint tl_y;
62     gint br_x;
63     gint br_y;
64 } RangeTree_Rectangle;
65 
RangeTree_inside_rectangle(RangeTree_Rectangle * r,RangeTree_Node * n)66 static gboolean RangeTree_inside_rectangle(RangeTree_Rectangle *r,
67                                            RangeTree_Node *n){
68     if((n->x < r->tl_x)
69     || (n->y < r->tl_y)
70     || (n->x >= r->br_x)
71     || (n->y >= r->br_y))
72         return FALSE;
73     return TRUE;
74     }
75 
RangeTree_find_recur(RangeTree_Node * n,gboolean dir,RangeTree_Rectangle * r,RangeTree_ReportFunc rtrf,gpointer user_data,gboolean * found)76 static void RangeTree_find_recur(RangeTree_Node *n, gboolean dir,
77                                  RangeTree_Rectangle *r,
78                                  RangeTree_ReportFunc rtrf,
79                                  gpointer user_data, gboolean *found){
80     if(!n)
81         return;
82     if(dir ? (r->tl_x < n->x) : (r->tl_y < n->y))
83         RangeTree_find_recur(n->left, dir^1, r, rtrf, user_data, found);
84     if(*found)
85         return;
86     if(RangeTree_inside_rectangle(r, n)){
87         if(rtrf(n->x, n->y, n->info, user_data)){
88             (*found) = TRUE;
89             return;
90             }
91         }
92     if(dir ? (n->x <= r->br_x) : (n->y <= r->br_y))
93         RangeTree_find_recur(n->right, dir^1, r, rtrf,
94                              user_data, found);
95     return;
96     }
97 
RangeTree_insert(RangeTree * rt,RangeTree_Node * rtn)98 static void RangeTree_insert(RangeTree *rt, RangeTree_Node *rtn){
99     register RangeTree_Node *n, *parent;
100     register gboolean dir, dim = FALSE;
101     if(!rt->root){
102         rt->root = rtn;
103         return;
104         }
105     for(n = parent = rt->root; n; dim ^= 1){
106         dir = dim ? (rtn->x < n->x)
107                   : (rtn->y < n->y);
108         parent = n;
109         n = dir ? n->left : n->right;
110         }
111     if(dir)
112         parent->left = rtn;
113     else
114         parent->right = rtn;
115     return;
116     }
117 
RangeTree_insert_list(RangeTree * rt,GPtrArray * list)118 static void RangeTree_insert_list(RangeTree *rt, GPtrArray *list){
119     register gint i, j;
120     register RangeTree_Node *rtn, *swap_rtn;
121     srand(list->len);
122     for(i = 0; i < list->len; i++){ /* Shuffle list */
123         rtn = list->pdata[i];
124         j = ((gint)lrand48()) % list->len;
125         swap_rtn = list->pdata[j];
126         list->pdata[j] = rtn;
127         list->pdata[i] = swap_rtn;
128         }
129     for(i = 0; i < list->len; i++){ /* Insert list */
130         rtn = list->pdata[i];
131         RangeTree_insert(rt, rtn);
132         }
133     return;
134     }
135 /* The list contains RangeTree_Data objects to be inserted.
136  * As the range tree is not balanced, this allows
137  * a set of points to be inserted in random order
138  * to avoid the worst-case performance of the RangeTree.
139  */
140 
RangeTree_insert_recent_collect(gpointer key,gpointer value,gpointer data)141 static gint RangeTree_insert_recent_collect(gpointer key,
142                                             gpointer value,
143                                             gpointer data){
144     register GPtrArray *list = data;
145     g_ptr_array_add(list, value);
146     return FALSE;
147     }
148 
RangeTree_insert_recent(RangeTree * rt)149 static void RangeTree_insert_recent(RangeTree *rt){
150     register GPtrArray *node_list = g_ptr_array_new();
151     g_tree_traverse(rt->recent_data, RangeTree_insert_recent_collect,
152                     G_IN_ORDER, node_list);
153     if(node_list->len){
154         RangeTree_insert_list(rt, node_list);
155         g_tree_destroy(rt->recent_data);
156         rt->recent_data = g_tree_new(RangeTree_recent_data_compare);
157         }
158     g_ptr_array_free(node_list, TRUE);
159     return;
160     }
161 
RangeTree_find_internal(RangeTree * rt,gint x_start,gint x_length,gint y_start,gint y_length,RangeTree_ReportFunc rtrf,gpointer user_data)162 static gboolean RangeTree_find_internal(RangeTree *rt,
163                     gint x_start, gint x_length,
164                     gint y_start, gint y_length,
165                     RangeTree_ReportFunc rtrf, gpointer user_data){
166     gboolean found = FALSE;
167     RangeTree_Rectangle r;
168     g_assert(x_length >= 0);
169     g_assert(y_length >= 0);
170     r.tl_x = x_start;
171     r.tl_y = y_start;
172     r.br_x = x_start + x_length;
173     r.br_y = y_start + y_length;
174     RangeTree_find_recur(rt->root, FALSE, &r, rtrf, user_data, &found);
175     return found;
176     }
177 
RangeTree_find(RangeTree * rt,gint x_start,gint x_length,gint y_start,gint y_length,RangeTree_ReportFunc rtrf,gpointer user_data)178 gboolean RangeTree_find(RangeTree *rt, gint x_start, gint x_length,
179                                    gint y_start, gint y_length,
180                     RangeTree_ReportFunc rtrf, gpointer user_data){
181     RangeTree_insert_recent(rt);
182     return RangeTree_find_internal(rt, x_start, x_length,
183                                        y_start, y_length,
184                                        rtrf, user_data);
185     }
186 
RangeTree_find_point(gint x,gint y,gpointer info,gpointer user_data)187 static gboolean RangeTree_find_point(gint x, gint y, gpointer info,
188                               gpointer user_data){
189     return TRUE;
190     }
191 
RangeTree_check_pos(RangeTree * rt,gint x,gint y)192 gboolean RangeTree_check_pos(RangeTree *rt,
193                              gint x, gint y){
194     RangeTree_Node rtn;
195     rtn.x = x;
196     rtn.y = y;
197     if(g_tree_lookup(rt->recent_data, &rtn))
198         return TRUE;
199     return RangeTree_find_internal(rt, x, 1, y, 1,
200                                    RangeTree_find_point, NULL);
201     }
202 
RangeTree_is_empty(RangeTree * rt)203 gboolean RangeTree_is_empty(RangeTree *rt){
204     RangeTree_insert_recent(rt);
205     return rt->root?FALSE:TRUE;
206     }
207 
RangeTree_traverse_recur(RangeTree_Node * rtn,RangeTree_ReportFunc rtrf,gpointer user_data)208 static gboolean RangeTree_traverse_recur(RangeTree_Node *rtn,
209                                      RangeTree_ReportFunc rtrf,
210                                      gpointer user_data){
211     if(!rtn)
212         return FALSE;
213     if(RangeTree_traverse_recur(rtn->left, rtrf, user_data)
214     || rtrf(rtn->x, rtn->y, rtn->info, user_data)
215     || RangeTree_traverse_recur(rtn->right, rtrf, user_data))
216         return TRUE;
217     return FALSE;
218     }
219 
RangeTree_traverse(RangeTree * rt,RangeTree_ReportFunc rtrf,gpointer user_data)220 gboolean RangeTree_traverse(RangeTree *rt,
221                         RangeTree_ReportFunc rtrf, gpointer user_data){
222     return RangeTree_traverse_recur(rt->root, rtrf, user_data);
223     }
224 
225