1 #include <vector>
2 #include <algorithm>
3 #include <memory>
4 #include <array>
5 #include <iostream>
6 #include <cassert>
7 #include <math.h>
8 #include <random>
9 #include <string>
10 #include <fstream>
11 
12 
13 typedef double F;
14 const int Ndim = 3;
15 
16 /*  A simple node struct that contains a key and a fixed number of children,
17     typically Nchildren = 2**Ndim
18  */
19 template <typename keyType, int Nchildren>
20 struct GenericNode
21 {
22     // Tree data
23     GenericNode<keyType, Nchildren>** children = nullptr;
24     GenericNode<keyType, Nchildren>* parent = nullptr;
25 
26     // Node data
27     keyType key;
28     int level = 0;
29     bool terminal = false;
30     int index = -1;
31 };
32 
33 template <typename keyType>
34 struct RayInfo {
35     std::vector<keyType> keys;
36     std::vector<F> t;
37 
38     RayInfo() {};
39     RayInfo(int N) {
40         if (N > 0) {
41             keys.reserve(N);
42             t.reserve(2*N);
43         }
44     }
45 };
46 
47 struct Ray {
48     std::array<F, Ndim> o; // Origin
49     std::array<F, Ndim> d; // Direction
50     F tmin = -1e99;
51     F tmax = 1e99;
52 
53     Ray(const F* _o, const F* _d, const F _tmin, const F _tmax) : tmin(_tmin), tmax(_tmax) {
54         for (auto idim = 0; idim < Ndim; ++idim) {
55             o[idim] = _o[idim];
56         }
57         F dd = 0;
58         for (auto idim = 0; idim < Ndim; ++idim) {
59             dd += _d[idim] * _d[idim];
60         }
61         dd = sqrt(dd);
62         for (auto idim = 0; idim < Ndim; ++idim) {
63             d[idim] = _d[idim] / dd;
64         }
65     };
66 
67     Ray() {};
68 };
69 
70 /*  Converts an array of integer position into a flattened index.
71     The fast varying index is the last one.
72  */
73 inline unsigned char ijk2iflat(const std::array<bool, Ndim> ijk) {
74     unsigned char iflat = 0;
75     for (auto i : ijk) {
76         iflat += i;
77         iflat <<= 1;
78     }
79     return iflat >> 1;
80 };
81 
82 /*  Converts a flattened index into an array of integer position.
83     The fast varying index is the last one.
84 */
85 inline std::array<bool, Ndim> iflat2ijk(unsigned char iflat) {
86     std::array<bool, Ndim> ijk;
87     for (auto idim = Ndim-1; idim >= 0; --idim) {
88         ijk[idim] = iflat & 0b1;
89         iflat >>= 1;
90     }
91     return ijk;
92 };
93 
94 /*  A class to build an octree and cast rays through it. */
95 template <class keyType>
96 class Octree {
97     typedef GenericNode<keyType, (1<<Ndim)> Node;
98     typedef std::vector<keyType> keyVector;
99     typedef std::array<F, Ndim> Pos;
100     typedef std::array<int, Ndim> iPos;
101     typedef std::array<unsigned char, Ndim> ucPos;
102 
103 private:
104     const unsigned char twotondim;
105     const int maxDepth;
106     Pos size;
107     Pos DLE; // Domain left edge
108     Pos DRE; // Domain right edge
109     Node* root;
110     int global_index = 0;
111 
112 public:
113     Octree(int _maxDepth, F* _DLE, F* _DRE) :
114             twotondim (1<<Ndim),
115             maxDepth(_maxDepth) {
116         for (auto idim = 0; idim < Ndim; ++idim) {
117             DRE[idim] = _DRE[idim];
118             DLE[idim] = _DLE[idim];
119             size[idim] = _DRE[idim] - _DLE[idim];
120         }
121 
122         root = new Node();
123         // Allocate root's children
124         root->children = (Node**) malloc(sizeof(Node*)*twotondim);
125         for (auto i = 0; i < twotondim; ++i) root->children = nullptr;
126     }
127 
128 
129     ~Octree() {
130         recursive_remove_node(root);
131     };
132 
133     /*
134         Insert a new node in the tree.
135     */
136     Node* insert_node(const iPos ipos, const int lvl, keyType key) {
137         assert(lvl <= maxDepth);
138 
139         // std::cerr << "Inserting at level: " << lvl << "/" << maxDepth << std::endl;
140         // this is 0b100..., where the 1 is at position maxDepth
141         uint64_t mask = 1<<(maxDepth - 1);
142 
143         iPos ijk = ipos;
144         std::array<bool, Ndim> bitMask;
145 
146         Node* node = root;
147         Node* child = nullptr;
148 
149         // Go down the tree
150         for (auto ibit = maxDepth-1; ibit >= maxDepth - lvl; --ibit) {
151             // Find children based on bits
152             for (auto idim = 0; idim < Ndim; ++idim) {
153                 bitMask[idim] = ijk[idim] & mask;
154             }
155             mask >>= 1;
156             auto iflat = ijk2iflat(bitMask);
157 
158             // Create child if it does not exist yet
159             child = create_get_node(node, iflat);
160             node = child;
161         }
162 
163         // Mark last node as terminal
164         node->terminal = true;
165         node->key = key;
166 
167         return node;
168     }
169 
170     Node* insert_node(const int* ipos, const int lvl, keyType key) {
171         std::array<int, Ndim> ipos_as_arr;
172         for (auto idim = 0; idim < Ndim; ++idim) ipos_as_arr[idim] = ipos[idim];
173         return insert_node(ipos_as_arr, lvl, key);
174     }
175 
176     void insert_node_no_ret(const int* ipos, const int lvl, keyType key) {
177         insert_node(ipos, lvl, key);
178     }
179 
180     // Perform multiple ray cast
181     RayInfo<keyType>** cast_rays(const F *origins, const F *directions, const int Nrays) {
182         RayInfo<keyType> **ray_infos = (RayInfo<keyType>**) malloc(sizeof(RayInfo<keyType>*)*Nrays);
183         int Nfound = 0;
184         #pragma omp parallel for shared(ray_infos, Nfound) schedule(static, 100)
185         for (auto i = 0; i < Nrays; ++i) {
186             std::vector<F> tList;
187             ray_infos[i] = new RayInfo<keyType>(Nfound);
188             auto ri = ray_infos[i];
189             Ray r(&origins[3*i], &directions[3*i], -1e99, 1e99);
190             cast_ray(&r, ri->keys, ri->t);
191 
192             // Keep track of the number of cells hit to preallocate the next ray info container
193             Nfound = std::max(Nfound, (int) ri->keys.size());
194         }
195         return ray_infos;
196     }
197 
198     // Perform single ray tracing
199     void cast_ray(Ray *r, keyVector &keyList, std::vector<F> &tList) {
200         // Boolean mask for direction
201         unsigned char a = 0;
202         unsigned char bmask = twotondim >> 1;
203 
204         // Put ray in positive direction and store info in bitmask "a"
205         for (auto idim = 0; idim < Ndim; ++idim) {
206             if (r->d[idim] < 0.0) {
207                 r->o[idim] = size[idim]-r->o[idim];
208                 r->d[idim] = -r->d[idim];
209                 a |= bmask;
210             }
211             bmask >>= 1;
212         }
213 
214         // Compute intersection points
215         Pos t0, t1;
216         for (auto idim = 0; idim < Ndim; ++idim){
217             t0[idim] = (DLE[idim] - r->o[idim]) / r->d[idim];
218             t1[idim] = (DRE[idim] - r->o[idim]) / r->d[idim];
219         }
220 
221         // If entry point is smaller than exit point, find path in octree
222         if (*std::max_element(t0.begin(), t0.end()) < *std::min_element(t1.begin(), t1.end()))
223             proc_subtree(t0[0], t0[1], t0[2],
224                          t1[0], t1[1], t1[2],
225                          root, a, keyList, tList);
226     }
227 
228     void cast_ray(double* o, double* d, keyVector &keyList, std::vector<F> &tList) {
229         Ray r(o, d, -1e99, 1e99);
230         cast_ray(&r, keyList, tList);
231     }
232 
233 private:
234 
235     /*
236         Upsert a node as a child of another.
237 
238         This will create a new node as a child of the current one, or return
239         an existing one if it already exists
240     */
241     Node* create_get_node(Node* parent, const int iflat) {
242         // Create children if not already existing
243         if (parent->children == nullptr) {
244             parent->children = (Node**) malloc(sizeof(Node*)*twotondim);
245             for (auto i = 0; i < twotondim; ++i) parent->children[i] = nullptr;
246         }
247 
248         if (parent->children[iflat] == nullptr) {
249             Node* node = new Node();
250             node->level = parent->level + 1;
251             node->index = global_index;
252             node->parent = parent;
253             ++global_index;
254 
255             parent->children[iflat] = node;
256         }
257         return parent->children[iflat];
258     }
259 
260     /*
261         Recursively free memory.
262     */
263     void recursive_remove_node(Node* node) {
264         if (node->children) {
265             for (auto i = 0; i < twotondim; ++i) {
266                 auto child = node->children[i];
267                 if (child) {
268                     recursive_remove_node(child);
269                 }
270                 delete child;
271             }
272             free(node->children);
273         }
274     }
275 
276     /*
277         Traverse the tree, assuming that the ray intersects
278         From http://wscg.zcu.cz/wscg2000/Papers_2000/X31.pdf
279     */
280     void proc_subtree(const F tx0, const F ty0, const F tz0,
281                       const F tx1, const F ty1, const F tz1,
282                       const Node *n, const unsigned char a,
283                       keyVector &keyList, std::vector<F> &tList, int lvl=0) {
284         // Check if exit face is not in our back
285         if (tx1 < 0 || ty1 < 0 || tz1 < 0) return;
286 
287         // Exit if the node is null (happens if it hasn't been added to the tree)
288         if (!n) return;
289 
290         // Process leaf node
291         if (n->terminal) {
292             keyList.push_back(n->key);
293             // Push entry & exit t
294             tList.push_back(std::max(std::max(tx0, ty0), tz0));
295             tList.push_back(std::min(std::min(tx1, ty1), tz1));
296             assert(n->children == nullptr);
297             return;
298         }
299 
300         // Early break for leafs without children that aren't terminal
301         if (n->children == nullptr) return;
302 
303         // Compute middle intersection
304         F txm, tym, tzm;
305         txm = (tx0 + tx1) * 0.5;
306         tym = (ty0 + ty1) * 0.5;
307         tzm = (tz0 + tz1) * 0.5;
308 
309         unsigned char iNode = first_node(tx0, ty0, tz0, txm, tym, tzm);
310 
311         // Iterate over children
312         do {
313 
314             switch (iNode)
315             {
316             case 0:
317                 proc_subtree(tx0, ty0, tz0, txm, tym, tzm, n->children[a], a, keyList, tList, lvl+1);
318                 iNode = next_node(txm, tym, tzm, 4, 2, 1);
319                 break;
320             case 1:
321                 proc_subtree(tx0, ty0, tzm, txm, tym, tz1, n->children[1^a], a, keyList, tList, lvl+1);
322                 iNode = next_node(txm, tym, tz1, 5, 3, 8);
323                 break;
324             case 2:
325                 proc_subtree(tx0, tym, tz0, txm, ty1, tzm, n->children[2^a], a, keyList, tList, lvl+1);
326                 iNode = next_node(txm, ty1, tzm, 6, 8, 3);
327                 break;
328             case 3:
329                 proc_subtree(tx0, tym, tzm, txm, ty1, tz1, n->children[3^a], a, keyList, tList, lvl+1);
330                 iNode = next_node(txm, ty1, tz1, 7, 8, 8);
331                 break;
332             case 4:
333                 proc_subtree(txm, ty0, tz0, tx1, tym, tzm, n->children[4^a], a, keyList, tList, lvl+1);
334                 iNode = next_node(tx1, tym, tzm, 8, 6, 5);
335                 break;
336             case 5:
337                 proc_subtree(txm, ty0, tzm, tx1, tym, tz1, n->children[5^a], a, keyList, tList, lvl+1);
338                 iNode = next_node(tx1, tym, tz1, 8, 7, 8);
339                 break;
340             case 6:
341                 proc_subtree(txm, tym, tz0, tx1, ty1, tzm, n->children[6^a], a, keyList, tList, lvl+1);
342                 iNode = next_node(tx1, ty1, tzm, 8, 8, 7);
343                 break;
344             case 7:
345                 proc_subtree(txm, tym, tzm, tx1, ty1, tz1, n->children[7^a], a, keyList, tList, lvl+1);
346                 iNode = 8;
347                 break;
348             }
349         } while (iNode < twotondim);
350     }
351 
352     // From "An Efficient Parametric Algorithm for Octree Traversal" by Revelles, Urena, & Lastra
353     inline unsigned char first_node(const F tx0, const F ty0, const F tz0,
354                                     const F txm, const F tym, const F tzm) {
355         unsigned char index = 0;
356         if (tx0 >= std::max(ty0, tz0)) {         // enters YZ plane
357             if (tym < tx0) index |= 0b010;
358             if (tzm < tx0) index |= 0b001;
359         } else if (ty0 >= std::max(tx0, tz0))  { // enters XZ plane
360             if (txm < ty0) index |= 0b100;
361             if (tzm < ty0) index |= 0b001;
362         } else {                                 // enters XY plane
363             if (txm < tz0) index |= 0b100;
364             if (tym < tz0) index |= 0b010;
365         }
366         return index;
367     }
368     // From "An Efficient Parametric Algorithm for Octree Traversal" by Revelles, Urena, & Lastra
369     inline unsigned char next_node(const F tx, const F ty, const F tz,
370                                    const uint8_t ix, const uint8_t iy, const uint8_t iz) {
371         if(tx < std::min(ty, tz)) {         // YZ plane
372             return ix;
373         } else if (ty < std::min(tx, tz)) { // XZ plane
374             return iy;
375         } else {                            // XY plane
376             return iz;
377         }
378     }
379 };
380