1 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*-
2 *
3 * XASTIR, Amateur Station Tracking and Information Reporting
4 * Copyright (C) 1999,2000 Frank Giannandrea
5 * Copyright (C) 2000-2019 The Xastir Group
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * Look at the README for more information on the program.
22 */
23 /****************************************************************************
24 * MODULE: R-Tree library
25 *
26 * AUTHOR(S): Antonin Guttman - original code
27 * Melinda Green (melinda@superliminal.com) - major clean-up
28 * and implementation of bounding spheres
29 *
30 * PURPOSE: Multidimensional index
31 *
32 */
33
34 #include <stdio.h>
35 #include "assert.h"
36 #include "index.h"
37 #include "card.h"
38 #include "split_l.h"
39
40
41 /*-----------------------------------------------------------------------------
42 | Load branch buffer with branches from full node plus the extra branch.
43 -----------------------------------------------------------------------------*/
Xastir_RTreeGetBranches(struct Node * N,struct Branch * B)44 static void Xastir_RTreeGetBranches(struct Node *N, struct Branch *B)
45 {
46 register struct Node *n = N;
47 register struct Branch *b = B;
48 register int i;
49
50 assert(n);
51 assert(b);
52
53 /* load the branch buffer */
54 for (i=0; i<MAXKIDS(n); i++)
55 {
56 assert(n->branch[i].child); /* every entry should be full */
57 Xastir_BranchBuf[i] = n->branch[i];
58 }
59 Xastir_BranchBuf[MAXKIDS(n)] = *b;
60 Xastir_BranchCount = MAXKIDS(n) + 1;
61
62 /* calculate rect containing all in the set */
63 Xastir_CoverSplit = Xastir_BranchBuf[0].rect;
64 for (i=1; i<MAXKIDS(n)+1; i++)
65 {
66 Xastir_CoverSplit = Xastir_RTreeCombineRect(&Xastir_CoverSplit, &Xastir_BranchBuf[i].rect);
67 }
68
69 Xastir_RTreeInitNode(n);
70 }
71
72
73
74 /*-----------------------------------------------------------------------------
75 | Initialize a PartitionVars structure.
76 -----------------------------------------------------------------------------*/
Xastir_RTreeInitPVars(struct PartitionVars * P,int maxrects,int minfill)77 static void Xastir_RTreeInitPVars(struct PartitionVars *P, int maxrects, int minfill)
78 {
79 register struct PartitionVars *p = P;
80 register int i;
81 assert(p);
82
83 p->count[0] = p->count[1] = 0;
84 p->total = maxrects;
85 p->minfill = minfill;
86 for (i=0; i<maxrects; i++)
87 {
88 p->taken[i] = FALSE;
89 p->partition[i] = -1;
90 }
91 }
92
93
94
95 /*-----------------------------------------------------------------------------
96 | Put a branch in one of the groups.
97 -----------------------------------------------------------------------------*/
Xastir_RTreeClassify(int i,int group,struct PartitionVars * p)98 static void Xastir_RTreeClassify(int i, int group, struct PartitionVars *p)
99 {
100 assert(p);
101 assert(!p->taken[i]);
102
103 p->partition[i] = group;
104 p->taken[i] = TRUE;
105
106 if (p->count[group] == 0)
107 {
108 p->cover[group] = Xastir_BranchBuf[i].rect;
109 }
110 else
111 p->cover[group] = Xastir_RTreeCombineRect(&Xastir_BranchBuf[i].rect,
112 &p->cover[group]);
113 p->area[group] = Xastir_RTreeRectSphericalVolume(&p->cover[group]);
114 p->count[group]++;
115 }
116
117
118
119 /*-----------------------------------------------------------------------------
120 | Pick two rects from set to be the first elements of the two groups.
121 | Pick the two that are separated most along any dimension, or overlap least.
122 | Distance for separation or overlap is measured modulo the width of the
123 | space covered by the entire set along that dimension.
124 -----------------------------------------------------------------------------*/
Xastir_RTreePickSeeds(struct PartitionVars * P)125 static void Xastir_RTreePickSeeds(struct PartitionVars *P)
126 {
127 register struct PartitionVars *p = P;
128 register int i, dim, high;
129 register struct Rect *r, *rlow, *rhigh;
130 // Original superliminal.com implementation had no initializers here.
131 // They are not strictly necessary, as the variables are initialized
132 // in the first iteration of the first for loop, but GCC complains
133 // anyway. Initializers added to keep it happy.
134 register float w, separation, bestSep=0.0;
135 RectReal width[NUMDIMS];
136 int leastUpper[NUMDIMS], greatestLower[NUMDIMS];
137 int seed0=0, seed1=0;
138 assert(p);
139
140 for (dim=0; dim<NUMDIMS; dim++)
141 {
142 high = dim + NUMDIMS;
143
144 /* find the rectangles farthest out in each direction
145 * along this dimens */
146 greatestLower[dim] = leastUpper[dim] = 0;
147 for (i=1; i<Xastir_NODECARD+1; i++)
148 {
149 r = &Xastir_BranchBuf[i].rect;
150 if (r->boundary[dim] >
151 Xastir_BranchBuf[greatestLower[dim]].rect.boundary[dim])
152 {
153 greatestLower[dim] = i;
154 }
155 if (r->boundary[high] <
156 Xastir_BranchBuf[leastUpper[dim]].rect.boundary[high])
157 {
158 leastUpper[dim] = i;
159 }
160 }
161
162 /* find width of the whole collection along this dimension */
163 width[dim] = Xastir_CoverSplit.boundary[high] -
164 Xastir_CoverSplit.boundary[dim];
165 }
166
167 /* pick the best separation dimension and the two seed rects */
168 for (dim=0; dim<NUMDIMS; dim++)
169 {
170 high = dim + NUMDIMS;
171
172 /* divisor for normalizing by width */
173 assert(width[dim] >= 0);
174 if (width[dim] == 0)
175 {
176 w = (RectReal)1;
177 }
178 else
179 {
180 w = width[dim];
181 }
182
183 rlow = &Xastir_BranchBuf[leastUpper[dim]].rect;
184 rhigh = &Xastir_BranchBuf[greatestLower[dim]].rect;
185 if (dim == 0)
186 {
187 seed0 = leastUpper[0];
188 seed1 = greatestLower[0];
189 separation = bestSep =
190 (rhigh->boundary[0] -
191 rlow->boundary[NUMDIMS]) / w;
192 }
193 else
194 {
195 separation =
196 (rhigh->boundary[dim] -
197 rlow->boundary[dim+NUMDIMS]) / w;
198 if (separation > bestSep)
199 {
200 seed0 = leastUpper[dim];
201 seed1 = greatestLower[dim];
202 bestSep = separation;
203 }
204 }
205 }
206
207 if (seed0 != seed1)
208 {
209 Xastir_RTreeClassify(seed0, 0, p);
210 Xastir_RTreeClassify(seed1, 1, p);
211 }
212 }
213
214
215
216 /*-----------------------------------------------------------------------------
217 | Put each rect that is not already in a group into a group.
218 | Process one rect at a time, using the following hierarchy of criteria.
219 | In case of a tie, go to the next test.
220 | 1) If one group already has the max number of elements that will allow
221 | the minimum fill for the other group, put r in the other.
222 | 2) Put r in the group whose cover will expand less. This automatically
223 | takes care of the case where one group cover contains r.
224 | 3) Put r in the group whose cover will be smaller. This takes care of the
225 | case where r is contained in both covers.
226 | 4) Put r in the group with fewer elements.
227 | 5) Put in group 1 (arbitrary).
228 |
229 | Also update the covers for both groups.
230 -----------------------------------------------------------------------------*/
Xastir_RTreePigeonhole(struct PartitionVars * P)231 static void Xastir_RTreePigeonhole(struct PartitionVars *P)
232 {
233 register struct PartitionVars *p = P;
234 struct Rect newCover[2];
235 register int i, group;
236 RectReal newArea[2], increase[2];
237
238 for (i=0; i<Xastir_NODECARD+1; i++)
239 {
240 if (!p->taken[i])
241 {
242 /* if one group too full, put rect in the other */
243 if (p->count[0] >= p->total - p->minfill)
244 {
245 Xastir_RTreeClassify(i, 1, p);
246 continue;
247 }
248 else if (p->count[1] >= p->total - p->minfill)
249 {
250 Xastir_RTreeClassify(i, 0, p);
251 continue;
252 }
253
254 /* find areas of the two groups' old and new covers */
255 for (group=0; group<2; group++)
256 {
257 if (p->count[group]>0)
258 newCover[group] = Xastir_RTreeCombineRect(
259 &Xastir_BranchBuf[i].rect,
260 &p->cover[group]);
261 else
262 {
263 newCover[group] = Xastir_BranchBuf[i].rect;
264 }
265 newArea[group] = Xastir_RTreeRectSphericalVolume(
266 &newCover[group]);
267 increase[group] = newArea[group]-p->area[group];
268 }
269
270 /* put rect in group whose cover will expand less */
271 if (increase[0] < increase[1])
272 {
273 Xastir_RTreeClassify(i, 0, p);
274 }
275 else if (increase[1] < increase[0])
276 {
277 Xastir_RTreeClassify(i, 1, p);
278 }
279
280 /* put rect in group that will have a smaller cover */
281 else if (p->area[0] < p->area[1])
282 {
283 Xastir_RTreeClassify(i, 0, p);
284 }
285 else if (p->area[1] < p->area[0])
286 {
287 Xastir_RTreeClassify(i, 1, p);
288 }
289
290 /* put rect in group with fewer elements */
291 else if (p->count[0] < p->count[1])
292 {
293 Xastir_RTreeClassify(i, 0, p);
294 }
295 else
296 {
297 Xastir_RTreeClassify(i, 1, p);
298 }
299 }
300 }
301 assert(p->count[0] + p->count[1] == Xastir_NODECARD + 1);
302 }
303
304
305
306 /*-----------------------------------------------------------------------------
307 | Method 0 for finding a partition:
308 | First find two seeds, one for each group, well separated.
309 | Then put other rects in whichever group will be smallest after addition.
310 -----------------------------------------------------------------------------*/
Xastir_RTreeMethodZero(struct PartitionVars * p,int minfill)311 static void Xastir_RTreeMethodZero(struct PartitionVars *p, int minfill)
312 {
313 Xastir_RTreeInitPVars(p, Xastir_BranchCount, minfill);
314 Xastir_RTreePickSeeds(p);
315 Xastir_RTreePigeonhole(p);
316 }
317
318
319
320
321 /*-----------------------------------------------------------------------------
322 | Copy branches from the buffer into two nodes according to the partition.
323 -----------------------------------------------------------------------------*/
Xastir_RTreeLoadNodes(struct Node * N,struct Node * Q,struct PartitionVars * P)324 static void Xastir_RTreeLoadNodes(struct Node *N, struct Node *Q,
325 struct PartitionVars *P)
326 {
327 register struct Node *n = N, *q = Q;
328 register struct PartitionVars *p = P;
329 register int i;
330 assert(n);
331 assert(q);
332 assert(p);
333
334 for (i=0; i<Xastir_NODECARD+1; i++)
335 {
336 if (p->partition[i] == 0)
337 {
338 Xastir_RTreeAddBranch(&Xastir_BranchBuf[i], n, NULL);
339 }
340 else if (p->partition[i] == 1)
341 {
342 Xastir_RTreeAddBranch(&Xastir_BranchBuf[i], q, NULL);
343 }
344 else
345 {
346 assert(FALSE);
347 }
348 }
349 }
350
351
352
353 /*-----------------------------------------------------------------------------
354 | Split a node.
355 | Divides the nodes branches and the extra one between two nodes.
356 | Old node is one of the new ones, and one really new one is created.
357 -----------------------------------------------------------------------------*/
Xastir_RTreeSplitNode(struct Node * n,struct Branch * b,struct Node ** nn)358 void Xastir_RTreeSplitNode(struct Node *n, struct Branch *b, struct Node **nn)
359 {
360 register struct PartitionVars *p;
361 register int level;
362 // This variable is declared, assigned a value, then never used.
363 // Newer GCCs warn about that. Shut them up.
364 // RectReal area;
365
366 assert(n);
367 assert(b);
368
369 /* load all the branches into a buffer, initialize old node */
370 level = n->level;
371 Xastir_RTreeGetBranches(n, b);
372
373 /* find partition */
374 p = &Xastir_Partitions[0];
375
376 /* Note: can't use MINFILL(n) below since n was cleared by GetBranches() */
377 Xastir_RTreeMethodZero(p, level>0 ? MinNodeFill : MinLeafFill);
378
379 /* record how good the split was for statistics */
380 // This variable is declared, assigned a value, then never used.
381 // Newer GCCs warn about that. Shut them up.
382 // area = p->area[0] + p->area[1];
383
384 /* put branches from buffer in 2 nodes according to chosen partition */
385 *nn = Xastir_RTreeNewNode();
386 (*nn)->level = n->level = level;
387 Xastir_RTreeLoadNodes(n, *nn, p);
388 assert(n->count + (*nn)->count == Xastir_NODECARD+1);
389 }
390
391
392
393 /*-----------------------------------------------------------------------------
394 | Print out data for a partition from PartitionVars struct.
395 -----------------------------------------------------------------------------*/
396
397 // This is not used at the moment, and because it's declared static gcc
398 // warns us it's not used. Commented out to shut gcc up
399 #if 0
400 static void Xastir_RTreePrintPVars(struct PartitionVars *p)
401 {
402 int i;
403 assert(p);
404
405 printf("\npartition:\n");
406 for (i=0; i<Xastir_NODECARD+1; i++)
407 {
408 printf("%3d\t", i);
409 }
410 printf("\n");
411 for (i=0; i<Xastir_NODECARD+1; i++)
412 {
413 if (p->taken[i])
414 {
415 printf(" t\t");
416 }
417 else
418 {
419 printf("\t");
420 }
421 }
422 printf("\n");
423 for (i=0; i<Xastir_NODECARD+1; i++)
424 {
425 printf("%3d\t", p->partition[i]);
426 }
427 printf("\n");
428
429 printf("count[0] = %d area = %f\n", p->count[0], p->area[0]);
430 printf("count[1] = %d area = %f\n", p->count[1], p->area[1]);
431 printf("total area = %f effectiveness = %3.2f\n",
432 p->area[0] + p->area[1],
433 Xastir_RTreeRectSphericalVolume(&Xastir_CoverSplit)/(p->area[0]+p->area[1]));
434
435 printf("cover[0]:\n");
436 Xastir_RTreePrintRect(&p->cover[0], 0);
437
438 printf("cover[1]:\n");
439 Xastir_RTreePrintRect(&p->cover[1], 0);
440 }
441 #endif // shut up GCC
442