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