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