ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/oocnn.c
Revision: 2.2
Committed: Mon Aug 14 21:12:10 2017 UTC (6 years, 8 months ago) by rschregle
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R2, rad5R3, rad5R1
Changes since 2.1: +9 -1 lines
Log Message:
Updated photon map code for Windows; no multproc or ooC for now

File Contents

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