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