]> git.itanic.dy.fi Git - sdl-planets/blob - quadtree.c
quadtree: tree validator: Add spatial validation
[sdl-planets] / quadtree.c
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include "quadtree.h"
5
6 #ifdef DEBUG
7 #define debug 1
8 #else
9 #define debug 0
10 #endif
11
12 static void _trap(int line)
13 {
14         if (debug) {
15                 printf("Trapped from line %d, use debugger to get backtrace\n",
16                         line);
17                 fflush(stdout);
18                 exit(1);
19         }
20 }
21
22 #define trap() _trap(__LINE__)
23
24 static int quadtree_compare_coord(const struct vector *a,
25                                 const struct vector *b)
26 {
27         int up, left;
28
29         up = b->y < a->y;
30         left = b->x < a->x;
31
32         if (up && left)
33                 return 0;
34         if (up && !left)
35                 return 1;
36         if (left)
37                 return 2;
38         return 3;
39 }
40
41 static int quadtree_compare(const struct quadtree *a, const struct quadtree *b)
42 {
43         return quadtree_compare_coord(&a->pos, &b->pos);
44 }
45
46 static void validate_subtree(const struct quadtree *node)
47 {
48         int i, dir;
49         long int children;
50         children = 0;
51
52         if (!node) {
53                 printf("Attempted to validate a null pointer\n");
54                 fflush(stdout);
55                 trap();
56         }
57
58         for (i = 0; i < 4; i++) {
59                 if (!node->child[i])
60                         continue;
61
62                 if (node->child[i]->parent != node) {
63                         printf("%s:%d Fatal! Tree inconsistency detected at "
64                                "child %d %p in node %p, incorrent parent %p\n",
65                                __func__, __LINE__, i, node->child[i], node,
66                                node->child[i]->parent);
67                         fflush(stdout);
68                         trap();
69                 }
70
71                 if (node->parent) {
72                         if (node->child[i] == node->parent) {
73                                 printf("%s:%d Fatal! Tree loop detected "
74                                        "at child %d in node %p\n",
75                                        __func__, __LINE__, i, node);
76                                 fflush(stdout);
77                                 trap();
78                         }
79                 }
80
81                 dir = quadtree_compare(node, node->child[i]);
82
83                 if (dir != i) {
84                         printf("%s:%d Fatal! Spatial inconsistency detected "
85                                 "at child %d in node %p\n"
86                                 "parent: (%f %f), child (%f %f), "
87                                 "dir %d != %d\n",
88                                 __func__, __LINE__, i, node,
89                                 node->pos.x, node->pos.y,
90                                 node->child[i]->pos.x, node->child[i]->pos.y,
91                                 dir, i);
92                         fflush(stdout);
93                         trap();
94                 }
95
96                 children += node->child[i]->children + 1;
97
98                 validate_subtree(node->child[i]);
99         }
100
101         if (node->depth < 0) {
102                 printf("%s:%d Tree statistics inconsistency detected! "
103                        "Negative depth: %ld\n",
104                        __func__, __LINE__, node->depth);
105         }
106
107         if (node->children != children) {
108                 printf("%s:%d Tree statistics inconsistency detected! "
109                        "child count mismatch. Expected %ld, got %ld\n",
110                        __func__, __LINE__, children, node->children);
111                 fflush(stdout);
112                 trap();
113         }
114
115         for (i = 0; i < 4 && node->children; i++) {
116                 if (!node->child[i])
117                         continue;
118
119                 if (node->depth == node->child[i]->depth + 1)
120                         break;
121
122                 if (node->child[i]->depth > node->depth) {
123                         printf("%s:%d Tree statistics inconsistency detected! "
124                                "child depth mismatch %ld > %ld\n",
125                                __func__, __LINE__, node->child[i]->depth,
126                                node->depth);
127                 }
128         }
129
130         if (i == 4) {
131                 printf("%s:%d Tree statistics inconsistency detected! "
132                        "child depth mismatch.",
133                        __func__, __LINE__);
134                 fflush(stdout);
135                 trap();
136         }
137 }
138
139 static void validate_tree(const struct quadtree *node)
140 {
141         if (debug)
142                 validate_subtree(quadtree_find_parent(node));
143 }
144
145 /**
146  * quadtree_add - add a node to a quadtree
147  * @parent: parent node
148  * @new: the new node to be added
149  *
150  * Add a node to a quadtree. The tree is kept in order, the new node
151  * is placed in the end of appropriate branch.
152  *
153  * The case of nodes sharing identical coordinates is not taken into
154  * account at all.
155  */
156
157 struct quadtree *quadtree_add(struct quadtree *parent, struct quadtree *new,
158                               struct quadtree_ops *ops)
159 {
160         int ret, i;
161         if (parent == new)
162                 trap();
163
164         validate_tree(parent);
165
166         ret = quadtree_compare(parent, new);
167
168         if (ret < 0 || ret >= 4) {
169                 printf("Invalid comparison result of %d\n", ret);
170                 return 0;
171         }
172
173         if (parent->child[ret])
174                 return quadtree_add(parent->child[ret], new, ops);
175
176         parent->child[ret] = new;
177         new->parent = parent;
178         for (i = 0; i < 4; i++)
179                 new->child[i] = 0;
180
181         if (debug) {
182                 printf("adding node %p to parent %p\n", new, parent);
183                 fflush(stdout);
184         }
185
186         quadtree_recalculate_parent_stats(new, ops);
187
188         validate_tree(new);
189
190         return new;
191 }
192
193 /*
194  * _qt_optimal_del - Delete nodes from a detached subtree in optimal order
195  *
196  * All nodes are moved under the new parent node
197  */
198 static void _qt_optimal_del(struct quadtree *parent, struct quadtree *node,
199                        struct quadtree_ops *ops, int parents)
200 {
201         struct quadtree *child[4] = {0};
202         int i, children = 0, max_c, max_i;
203
204         for (i = 0; i < 4; i++) {
205                 if (!node->child[i]) {
206                         child[i] = 0;
207                         continue;
208                 }
209
210                 child[children] = node->child[i];
211                 node->child[i] = NULL;
212                 children++;
213         }
214
215         /*
216          * Try to remove the nodes in such order that the node which
217          * has child number one quarter of the parent's child numer
218          * gets deleted first.
219          */
220
221         if (node->children <= parents / 4) {
222                 if (debug) {
223                         printf("%d: Moving node %p away from parent %p\n",
224                                __LINE__, node, parent);
225                         fflush(stdout);
226                 }
227                 validate_tree(parent);
228                 quadtree_add(parent, node, ops);
229                 node = NULL;
230                 parents /= 4;
231         }
232
233         while(children) {
234                 max_c = -1;
235                 max_i = -1;
236                 for (i = 0; i < 4; i++) {
237                         if (!child[i])
238                                 continue;
239
240                         if (max_c < child[i]->children) {
241                                 max_c = child[i]->children;
242                                 max_i = i;
243                         }
244                 }
245
246                 _qt_optimal_del(parent, child[max_i], ops, parents / 4);
247                 child[max_i] = 0;
248                 children--;
249         }
250
251         if (node) {
252                 validate_tree(parent);
253                 if (debug) {
254                         printf("%d: Moving node %p away from parent %p\n",
255                                __LINE__, node, parent);
256                         fflush(stdout);
257                 }
258                 quadtree_add(parent, node, ops);
259         }
260 }
261
262 /*
263  * quadtree_del - Detach a node from the tree
264  *
265  * Return value: The new root node of the tree. If we are detaching
266  * anything but the root node of the entire tree, the returned root
267  * value will be the original root of the tree.
268  */
269 struct quadtree *quadtree_del(struct quadtree *node,
270                               struct quadtree_ops *ops)
271 {
272         struct quadtree *parent = 0;
273         int i;
274
275         if (debug) {
276                 printf("Deleting node %p under parent %p\n",
277                                node, node->parent);
278                 fflush(stdout);
279         }
280         /*
281          * We are deleting the root node. This means we have to select
282          * a new root node and reconstruct the entire tree under it
283          * again.
284          */
285         if (!node->parent) {
286                 for (i = 0; i < 4; i++) {
287                         if (!node->child[i])
288                                 continue;
289
290                         if (!parent) {
291                                 parent = node->child[i];
292                                 parent->parent = 0;
293                                 continue;
294                         }
295                         _qt_optimal_del(parent, node->child[i], ops,
296                                         node->children);
297                         node->child[i] = 0;
298                 }
299
300                 return parent;
301         }
302
303         /*
304          * We are not deleting the parent. Detach the node from the
305          * parent abd relocate the children. The node will be then
306          * detached from the tree.
307          */
308
309         for (i = 0; i < 4; i++) {
310                 if (node->parent->child[i] == node) {
311                         node->parent->child[i] = 0;
312                         break;
313                 }
314         }
315         if (i == 4)
316                 trap();
317
318         parent = node->parent;
319
320         /*
321          * The sub branch is now detached from the main tree. Fix the
322          * stats.
323          */
324         quadtree_recalculate_parent_stats(node->parent, ops);
325
326         parent = quadtree_find_parent(parent);
327
328         if (node->children) {
329                 for (i = 0; i < 4; i++) {
330                         if (!node->child[i])
331                                 continue;
332
333                         _qt_optimal_del(parent, node->child[i], ops,
334                                         node->children);
335                 }
336         }
337
338         validate_tree(parent);
339         return parent;
340 }
341
342
343 static int _walk_tree(struct quadtree *head, const struct quadtree_iterator *it)
344 {
345         int direction, count = 0;
346
347         direction = it->direction(head, (struct quadtree_iterator *)it);
348
349         if ((direction & QUADTREE_UPLEFT) && head->child[0])
350                 count += _walk_tree(head->child[0], it);
351
352         if ((direction & QUADTREE_UPRIGHT) && head->child[1])
353                 count += _walk_tree(head->child[1], it);
354
355         if ((direction & QUADTREE_DOWNLEFT) && head->child[2])
356                 count += _walk_tree(head->child[2], it);
357
358         if ((direction & QUADTREE_DOWNRIGHT) && head->child[3])
359                 count += _walk_tree(head->child[3], it);
360
361         if ((direction & QUADTREE_SELF) && it->callback) {
362                 it->callback(head, (struct quadtree_iterator *)it);
363                 count++;
364         }
365
366         return count;
367 }
368
369 int walk_quadtree(const struct quadtree_iterator *it)
370 {
371         return _walk_tree(it->head, it);
372 }