ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/oocnn.c
Revision: 2.1
Committed: Tue May 17 17:39:47 2016 UTC (8 years, 11 months ago) by rschregle
Content type: text/plain
Branch: MAIN
Log Message:
Initial import of ooC photon map

File Contents

# Content
1 /*
2 =========================================================================
3 k-nearest neighbour lookup routines for out-of-core octree data structure
4
5 Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
6 (c) Lucerne University of Applied Sciences and Arts,
7 supported by the Swiss National Science Foundation (SNSF, #147053)
8 =========================================================================
9
10 $Id: oocnn.c,v 1.30 2015/11/13 18:07:26 taschreg Exp taschreg $
11 */
12
13
14
15 #include "oocnn.h"
16 #include "oocsort.h"
17 #include <stdlib.h>
18 #include <string.h>
19 #include <math.h>
20
21
22
23 #ifdef DEBUG_OOC_NN
24 static int OOC_SearchQueueCheck (OOC_SearchQueue *queue, const FVECT key,
25 RREAL *(*keyFunc)(const void*),
26 unsigned root)
27 /* Priority queue sanity check */
28 {
29 unsigned kid;
30 const OOC_SearchQueueNode *qn = queue -> node;
31 void *rec;
32 float d2;
33
34 if (root < queue -> tail) {
35 rec = OOC_GetNearest(queue, qn [root].idx);
36 d2 = dist2(keyFunc(rec), key);
37
38 if (qn [root].dist2 != d2)
39 return -1;
40
41 if ((kid = (root << 1) + 1) < queue -> tail) {
42 if (qn [kid].dist2 > qn [root].dist2)
43 return -1;
44 else return OOC_SearchQueueCheck(queue, key, keyFunc, kid);
45 }
46
47 if (++kid < queue -> tail) {
48 if (qn [kid].dist2 > qn [root].dist2)
49 return -1;
50 else return OOC_SearchQueueCheck(queue, key, keyFunc, kid);
51 }
52 }
53
54 return 0;
55 }
56 #endif
57
58
59
60 static float OOC_PutNearest (OOC_SearchQueue *queue, float d2, void *rec)
61 /* Insert data record with SQUARED distance to query point into search
62 * priority queue, maintaining the most distant record at the queue head.
63 * If the queue is full, the new record is only inserted if it is closer
64 * than the queue head.
65 * Returns the new maximum SQUARED distance at the head if the queue is
66 * full. Otherwise returns -1, indicating a maximum for the entire queue is
67 * as yet undefined
68 * The data record is copied into the queue's local record buffa for
69 * post-search retrieval to minimise redundant disk access. Note that it
70 * suffices to only rearrange the corresponding indices in the queue nodes
71 * when restoring the priority queue after every insertion, rather than
72 * moving the actual records. */
73 {
74 OOC_SearchQueueNode *qn = queue -> node;
75 unsigned root, kid, kid2, rootIdx;
76 float d2max = -1; /* Undefined max distance ^2 */
77
78 /* The queue is represented as a linearised binary tree with the root
79 * corresponding to the queue head, and the tail corresponding to the
80 * last leaf */
81 if (queue -> tail < queue -> len) {
82 /* Enlarge queue if not full, insert at tail and resort towards head */
83 kid = queue -> tail++;
84
85 while (kid) {
86 root = (kid - 1) >> 1;
87
88 /* Compare with parent and swap if necessary, else terminate */
89 if (d2 > qn [root].dist2) {
90 qn [kid].dist2 = qn [root].dist2;
91 qn [kid].idx = qn [root].idx;
92 kid = root;
93 }
94 else break;
95 }
96
97 /* Assign tail position as linear index into record buffa
98 * queue -> nnRec and append record */
99 qn [kid].dist2 = d2;
100 qn [kid].idx = queue -> tail - 1;
101 memcpy(OOC_GetNearest(queue, qn [kid].idx), rec, queue -> recSize);
102 }
103 else if (d2 < qn [0].dist2) {
104 /* Queue full and new record closer than maximum at head; replace head
105 * and resort towards tail */
106 root = 0;
107 rootIdx = qn [root].idx;
108
109 while ((kid = (root << 1) + 1) < queue -> tail) {
110 /* Compare with larger kid & swap if necessary, else terminate */
111 if ((kid2 = (kid + 1)) < queue -> tail &&
112 qn [kid2].dist2 > qn [kid].dist2)
113 kid = kid2;
114
115 if (d2 < qn [kid].dist2) {
116 qn [root].dist2 = qn [kid].dist2;
117 qn [root].idx = qn [kid].idx;
118 }
119 else break;
120
121 root = kid;
122 }
123
124 /* Reassign head's previous buffa index and overwrite corresponding
125 * record */
126 qn [root].dist2 = d2;
127 qn [root].idx = rootIdx;
128 memcpy(OOC_GetNearest(queue, qn [root].idx), rec, queue -> recSize);
129
130 /* Update SQUARED maximum distance from head node */
131 d2max = qn [0].dist2;
132 }
133
134 return d2max;
135 }
136
137
138
139 int OOC_InitNearest (OOC_SearchQueue *squeue,
140 unsigned len, unsigned recSize)
141 {
142 squeue -> len = len;
143 squeue -> recSize = recSize;
144 squeue -> node = calloc(len, sizeof(OOC_SearchQueueNode));
145 squeue -> nnRec = calloc(len, recSize);
146 if (!squeue -> node || !squeue -> nnRec) {
147 perror("OOC_InitNearest: failed search queue allocation");
148 return -1;
149 }
150
151 return 0;
152 }
153
154
155
156 void *OOC_GetNearest (const OOC_SearchQueue *squeue, unsigned idx)
157 {
158 return squeue -> nnRec + idx * squeue -> recSize;
159 }
160
161
162
163 static float OOC_BBoxDist2 (const FVECT bbOrg, RREAL bbSiz, const FVECT key)
164 /* Return minimum *SQUARED* distance between key and bounding box defined by
165 * bbOrg and bbSiz; a distance of 0 implies the key lies INSIDE the bbox */
166 {
167 /* Explicit comparison with bbox corners */
168 int i;
169 FVECT d;
170
171 for (i = 0; i < 3; i++) {
172 d [i] = key [i] - bbOrg [i];
173 d [i] = d [i] < 0 ? -d [i] : d [i] - bbSiz;
174
175 /* Set to 0 if inside bbox */
176 if (d [i] < 0)
177 d [i] = 0;
178 }
179
180 return DOT(d, d);
181 }
182
183
184
185 float OOC_FindNearest (OOC_Octree *oct, OOC_Node *node, OOC_DataIdx dataIdx,
186 const FVECT org, float size, const FVECT key,
187 const OOC_SearchFilter *filter,
188 OOC_SearchQueue *queue, float maxDist2)
189 {
190 const float kidSize = size * 0.5;
191 unsigned i, kid, kid0;
192 float d2;
193 char rec [oct -> recSize];
194 FVECT kidOrg;
195 OOC_DataIdx kidDataIdx, recIdx;
196 OOC_Node *kidNode;
197
198 /* Start with suboctant closest to key */
199 for (kid0 = 0, i = 0; i < 3; i++)
200 kid0 |= (key [i] > org [i] + kidSize) << i;
201
202 for (i = 0; i < 7; i++) {
203 kid = kid0 ^ i;
204 kidNode = node;
205 kidDataIdx = dataIdx + OOC_GetKid(oct, &kidNode, kid);
206
207 /* Prune empty suboctant */
208 if ((!kidNode && !OOC_ISLEAF(node)) ||
209 (OOC_ISLEAF(node) && !node -> leaf.num [kid]))
210 continue;
211
212 /* Set up suboctant */
213 VCOPY(kidOrg, org);
214 OOC_OCTORIGIN(kidOrg, kid, kidSize);
215
216 /* Prune suboctant if not overlapped by maxDist2 */
217 if (OOC_BBoxDist2(kidOrg, kidSize, key) > maxDist2)
218 continue;
219
220 if (kidNode) {
221 /* Internal node; recurse into non-empty suboctant */
222 maxDist2 = OOC_FindNearest(oct, kidNode, kidDataIdx, kidOrg,
223 kidSize, key, filter, queue, maxDist2);
224 if (maxDist2 < 0)
225 /* Bail out on error */
226 break;
227 }
228 else if (OOC_ISLEAF(node))
229 /* Leaf node; do linear check of all records in suboctant */
230 for (recIdx = kidDataIdx;
231 recIdx < kidDataIdx + node -> leaf.num [kid]; recIdx++) {
232 if (OOC_GetData(oct, recIdx, rec))
233 return -1;
234
235 if (!filter || filter -> func(rec, filter -> data))
236 /* Insert record in search queue SQUARED dist to key below
237 * maxDist2 and passes filter */
238 if ((d2 = dist2(key, oct -> key(rec))) < maxDist2) {
239 if ((d2 = OOC_PutNearest(queue, d2, rec)) >= 0)
240 /* Update maxDist2 if queue is full */
241 maxDist2 = d2;
242 #ifdef DEBUG_OOC_NN
243 if (OOC_SearchQueueCheck(queue, key, oct -> key, 0)) {
244 fprintf(stderr, "OOC_SearchPush: priority queue "
245 "inconsistency\n");
246 return -1;
247 }
248 #endif
249 }
250 }
251 }
252
253 return maxDist2;
254 }
255
256
257
258 float OOC_Find1Nearest (OOC_Octree *oct, OOC_Node *node, OOC_DataIdx dataIdx,
259 const FVECT org, float size, const FVECT key,
260 const OOC_SearchFilter *filter, void *nnRec,
261 float maxDist2)
262 {
263 const float kidSize = size * 0.5;
264 unsigned i, kid, kid0;
265 float d2;
266 char rec [oct -> recSize];
267 FVECT kidOrg;
268 OOC_DataIdx kidDataIdx, recIdx;
269 OOC_Node *kidNode;
270
271 /* Start with suboctant closest to key */
272 for (kid0 = 0, i = 0; i < 3; i++)
273 kid0 |= (key [i] > org [i] + kidSize) << i;
274
275 for (i = 0; i < 7; i++) {
276 kid = kid0 ^ i;
277 kidNode = node;
278 kidDataIdx = dataIdx + OOC_GetKid(oct, &kidNode, kid);
279
280 /* Prune empty suboctant */
281 if ((!kidNode && !OOC_ISLEAF(node)) ||
282 (OOC_ISLEAF(node) && !node -> leaf.num [kid]))
283 continue;
284
285 /* Set up suboctant */
286 VCOPY(kidOrg, org);
287 OOC_OCTORIGIN(kidOrg, kid, kidSize);
288
289 /* Prune suboctant if not overlapped by maxDist2 */
290 if (OOC_BBoxDist2(kidOrg, kidSize, key) > maxDist2)
291 continue;
292
293 if (kidNode) {
294 /* Internal node; recurse into non-empty suboctant */
295 maxDist2 = OOC_Find1Nearest(oct, kidNode, kidDataIdx, kidOrg,
296 kidSize, key, filter, nnRec, maxDist2);
297 if (maxDist2 < 0)
298 /* Bail out on error */
299 break;
300 }
301 else if (OOC_ISLEAF(node))
302 /* Leaf node; do linear check of all records in suboctant */
303 for (recIdx = kidDataIdx;
304 recIdx < kidDataIdx + node -> leaf.num [kid]; recIdx++) {
305 if (OOC_GetData(oct, recIdx, rec))
306 return -1;
307
308 if (!filter || filter -> func(rec, filter -> data))
309 /* Update closest record and max SQUARED dist to key if it
310 * passes filter */
311 if ((d2 = dist2(key, oct -> key(rec))) < maxDist2) {
312 memcpy(nnRec, rec, oct -> recSize);
313 maxDist2 = d2;
314 }
315 }
316 }
317
318 return maxDist2;
319 }
320