EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_octree.h
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ octree geometry headers
5 # Copyright (C) 2015 National Research Council Canada
6 #
7 # This file is part of EGSnrc.
8 #
9 # EGSnrc is free software: you can redistribute it and/or modify it under
10 # the terms of the GNU Affero General Public License as published by the
11 # Free Software Foundation, either version 3 of the License, or (at your
12 # option) any later version.
13 #
14 # EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
17 # more details.
18 #
19 # You should have received a copy of the GNU Affero General Public License
20 # along with EGSnrc. If not, see <http://www.gnu.org/licenses/>.
21 #
22 ###############################################################################
23 #
24 # Author: Frederic Tessier, 2008
25 #
26 # Contributors: Iwan Kawrakow
27 # Hubert Ho
28 # Reid Townson
29 #
30 ###############################################################################
31 */
32 
33 
34 /*
35 ===============================================================================
36 egs_octree: an octree implementation to the egs++ geometry collection.
37 ===============================================================================
38 
39 An octree is a way to partition space in a tree-like fashion, and it is a direct
40 extension of binary trees to 3D. The idea is to start with a cuboid and divide
41 it in half along each of the three cartesian axes to yield 8 children cuboids
42 (hence the name "octree"), and so on recursively until some desired limit. One
43 can restrict this subdivision scheme for any branch of the octree to obtain
44 leaf cells of varying size: the resolution of the octree can vary spatially.
45 
46 The main motivation for coding an egs++ octree geometry is to increase the
47 performance of large voxelized geometries such as a the human phantom. If we
48 imagine individual voxels in the phantom to be the leaves of the octree, then we
49 can merge voxels of same medium into larger cells and significantly reduce the
50 overall number of regions, thus reducing the number of howfar calls.
51 
52 The geometry can handle multiple bounding boxes in order to model different part
53 of the geometry at different resolutions (see example below). This can be used
54 to model micromatrices, for example.
55 
56 Any egs++ geometry can be "octreefied". One defines a child geometry, a bounding
57 box and the desired resolution, and the geometry will grow and prune the octree
58 accordingly; the child geometry is simply used as an oracle that returns the
59 local medium index for a given point. This approach allows one to use any
60 existing input file and wrap an octree around it, so in effect in can also serve
61 as a generic voxelizer (there is an option to disable pruning).
62 
63 More documentation will follow in the code, but you should be able to try it
64 right away with your own geometries, following the examples I will commit to
65 the egs++/geometry/examples. Please take it out for a spin on your own favorite
66 geometry and let me know if it works well.
67 
68 Enjoy.
69 
70 
71 ===============================================================================
72 EXAMPLE:
73 ===============================================================================
74 
75 Here is a simple example that defines a sphere and then models it with an octree
76 in which the positive octant features a higher resolution:
77 
78 :start geometry definition:
79 
80  :start geometry:
81  library = egs_spheres
82  name = my_sphere
83  :start media input:
84  media = my_medium
85  :stop media input:
86  midpoint = 0 0 0
87  radii = 0.9
88  :stop geometry:
89 
90  :start geometry:
91  library = egs_octree
92  name = my_octree
93  :start octree box:
94  box min = -1 -1 -1
95  box max = 1 1 1
96  resolution = 32 32 32
97  :stop octree box:
98  :start octree box:
99  box min = 0 0 0
100  box max = 1 1 1
101  resolution = 128 128 128
102  :stop octree box:
103  child geometry = my_sphere
104  :stop geometry:
105 
106  simulation geometry = my_octree
107 
108 :stop geometry definition:
109 
110 
111 ===============================================================================
112 PERFORMANCE:
113 ===============================================================================
114 
115 I tested the octree geometry on the fax06 human phantom to check what kind of
116 gains can be achieved. Using the howfar time test from the geometry tester, I
117 found the following:
118 
119 fax06 human phantom:
120 -------------------------------------------------------------------------------
121 model # cells avg cell size avg # steps time / sample (on tux)
122 -------------------------------------------------------------------------------
123 voxels 141716520 0.12 284 14 us
124 octree 9045359 0.30 46 20 us
125 -------------------------------------------------------------------------------
126 GAIN 94 % 84 % 32 %
127 -------------------------------------------------------------------------------
128 
129 There is a major gain in the number of cells and the number of howfar calls,
130 but the performance increase is mitigated by the more intricate logic for
131 navigating in the octree (finding neighbors etc.). So the performance increase
132 is only 32%, as opposed to the 84% that could be expected solely from the
133 decrease of the number of steps. Of course, all these numbers are model
134 specific, and the gain will be better for geometries in which there are large
135 volumes of uniform medium.
136 
137 
138 ===============================================================================
139 MEMORY:
140 ===============================================================================
141 
142 Each node in the final octree (the number of reported regions) requires 46
143 bytes of memory (on 64-bit machines), that is, 38 bytes for each instance of
144 the node EGS_OCtree_node class and 8 bytes for a pointer to the node in the
145 region list. So for example the fax06 phantom requires about 400 MB of memory
146 (but it shows up as using about 600 MB, so maybe there is a leak?).
147 
148 
149 ===============================================================================
150 TODO:
151 ===============================================================================
152 
153 1. Add an option in the VHP geometry to represent the model with an octree, and
154 perhaps implement a bottom-up octree growth algorithm if it can be faster than
155 the current recursive method.
156 
157 2. Add the option to merge voxels when a certain proportion of the children
158 nodes have the same medium.
159 
160 3. Add the option to set the resolution depending on the medium or other
161 criteria than bounding boxes?
162 
163 =============================================================================
164 */
165 
166 
172 #ifndef EGS_OCTREE_
173 #define EGS_OCTREE_
174 
175 #include "egs_functions.h"
176 #include "egs_base_geometry.h"
177 #include <vector>
178 #include <cstring>
179 #include <cmath>
180 
181 
182 #ifdef WIN32
183 
184  #ifdef BUILD_OCTREE_DLL
185  #define EGS_OCTREE_EXPORT __declspec(dllexport)
186  #else
187  #define EGS_OCTREE_EXPORT __declspec(dllimport)
188  #endif
189  #define EGS_OCTREE_LOCAL
190 
191 #else
192 
193  #ifdef HAVE_VISIBILITY
194  #define EGS_OCTREE_EXPORT __attribute__ ((visibility ("default")))
195  #define EGS_OCTREE_LOCAL __attribute__ ((visibility ("hidden")))
196  #else
197  #define EGS_OCTREE_EXPORT
198  #define EGS_OCTREE_LOCAL
199  #endif
200 
201 #endif
202 
203 
205 public:
206  EGS_Vector vmin, vmax;
207  int level, maxlevel;
208  int ixmin, iymin, izmin;
209  int ixmax, iymax, izmax;
210  int nx, ny, nz;
211 
212  // constructor
213  EGS_Octree_bbox(const EGS_Vector &boxMin, const EGS_Vector &boxMax, vector<int> &bboxRes) :
214  vmin(boxMin), vmax(boxMax), nx(bboxRes[0]), ny(bboxRes[1]), nz(bboxRes[2]) {
215  maxlevel = 0;
216  ixmin = iymin = izmin = 0;
217  ixmax = iymax = izmax = 0;
218  }
219 
220  // addition
221  EGS_Octree_bbox operator+ (const EGS_Octree_bbox &b2) const {
222 
223  EGS_Octree_bbox b1(*this);
224 
225  // set the b1 box limits to enclose the two boxes
226  if (b2.vmin.x < b1.vmin.x) {
227  b1.vmin.x = b2.vmin.x;
228  }
229  if (b2.vmin.y < b1.vmin.y) {
230  b1.vmin.y = b2.vmin.y;
231  }
232  if (b2.vmin.z < b1.vmin.z) {
233  b1.vmin.z = b2.vmin.z;
234  }
235  if (b2.vmax.x > b1.vmax.x) {
236  b1.vmax.x = b2.vmax.x;
237  }
238  if (b2.vmax.y > b1.vmax.y) {
239  b1.vmax.y = b2.vmax.y;
240  }
241  if (b2.vmax.z > b1.vmax.z) {
242  b1.vmax.z = b2.vmax.z;
243  }
244 
245  // calculate voxel sizes for the two boxes
246  EGS_Float dx1 = (b1.vmax.x-b1.vmin.x)/b1.nx;
247  EGS_Float dy1 = (b1.vmax.y-b1.vmin.y)/b1.ny;
248  EGS_Float dz1 = (b1.vmax.z-b1.vmin.z)/b1.nz;
249  EGS_Float dx2 = (b2.vmax.x-b2.vmin.x)/b2.nx;
250  EGS_Float dy2 = (b2.vmax.y-b2.vmin.y)/b2.ny;
251  EGS_Float dz2 = (b2.vmax.z-b2.vmin.z)/b2.nz;
252 
253  // calculate the number of voxels in the overall box b1 (using the highest resolution of b1 and b2)
254  if (dx2 < dx1) {
255  b1.nx = (int)((b1.vmax.x - b1.vmin.x + 0.5*dx2) / dx2);
256  }
257  else {
258  b1.nx = (int)((b1.vmax.x - b1.vmin.x +0.5*dx1) / dx1);
259  }
260  if (dy2 < dy1) {
261  b1.ny = (int)((b1.vmax.y - b1.vmin.y + 0.5*dy2) / dy2);
262  }
263  else {
264  b1.ny = (int)((b1.vmax.y - b1.vmin.y +0.5*dy1) / dy1);
265  }
266  if (dz2 < dz1) {
267  b1.nz = (int)((b1.vmax.z - b1.vmin.z + 0.5*dz2) / dz2);
268  }
269  else {
270  b1.nz = (int)((b1.vmax.z - b1.vmin.z +0.5*dz1) / dz1);
271  }
272 
273  // return the overall box b1
274  return b1;
275  }
276 
277  EGS_Octree_bbox &operator+= (const EGS_Octree_bbox &b2) {
278 
279  // set the b1 box limits to enclose the two boxes
280  if (b2.vmin.x < vmin.x) {
281  vmin.x = b2.vmin.x;
282  }
283  if (b2.vmin.y < vmin.y) {
284  vmin.y = b2.vmin.y;
285  }
286  if (b2.vmin.z < vmin.z) {
287  vmin.z = b2.vmin.z;
288  }
289  if (b2.vmax.x > vmax.x) {
290  vmax.x = b2.vmax.x;
291  }
292  if (b2.vmax.y > vmax.y) {
293  vmax.y = b2.vmax.y;
294  }
295  if (b2.vmax.z > vmax.z) {
296  vmax.z = b2.vmax.z;
297  }
298 
299  // calculate voxel sizes for the two boxes
300  EGS_Float dx1 = (vmax.x-vmin.x)/nx;
301  EGS_Float dy1 = (vmax.y-vmin.y)/ny;
302  EGS_Float dz1 = (vmax.z-vmin.z)/nz;
303  EGS_Float dx2 = (b2.vmax.x-b2.vmin.x)/b2.nx;
304  EGS_Float dy2 = (b2.vmax.y-b2.vmin.y)/b2.ny;
305  EGS_Float dz2 = (b2.vmax.z-b2.vmin.z)/b2.nz;
306 
307  // calculate the number of voxels in the overall box b1 (using the highest resolution of b1 and b2)
308  if (dx2 < dx1) {
309  nx = (int)((vmax.x - vmin.x + 0.5*dx2) / dx2);
310  }
311  else {
312  nx = (int)((vmax.x - vmin.x +0.5*dx1) / dx1);
313  }
314  if (dy2 < dy1) {
315  ny = (int)((vmax.y - vmin.y + 0.5*dy2) / dy2);
316  }
317  else {
318  ny = (int)((vmax.y - vmin.y +0.5*dy1) / dy1);
319  }
320  if (dz2 < dz1) {
321  nz = (int)((vmax.z - vmin.z + 0.5*dz2) / dz2);
322  }
323  else {
324  nz = (int)((vmax.z - vmin.z +0.5*dz1) / dz1);
325  }
326 
327  // return the overall box
328  return *this;
329  }
330 };
331 
332 
334 public:
335  int ix, iy, iz;
336  int region;
337  short medium;
338  unsigned short level;
341 
342  // constructor
343  EGS_Octree_node() {
344  medium = -1; // set the medium to -1 by default
345  region = -1; // set region to -1 by default
346  level = 0; // set level to 0 (root) by default
347  ix = iy = iz = 0; // set octree indices to 0 by default
348  child = NULL; // the node has no children initially
349  parent = NULL; // the node has no parent initially
350  }
351 
352  // create children nodes
353  void createChildren() { // create children to the this node
354  if (!child) { // ensure there are no children already
355  child = new EGS_Octree_node [8]; // allocate memory for 8 new children nodes
356  if (!child) {
357  egsFatal("EGS_Octree_node::createChildren(): Memory allocation error");
358  }
359  for (int i=0; i<8; i++) { // loop over all 8 newly created children nodes
360  child[i].level = level+1; // increase level by 1 compared to current level
361  child[i].ix = (ix << 1) | (i>>0 & 0x1); // shift up ix by one, and set new bit to that of the child index's bit 0 (x position)
362  child[i].iy = (iy << 1) | (i>>1 & 0x1); // shift up iy by one, and set new bit to that of the child index's bit 1 (y position)
363  child[i].iz = (iz << 1) | (i>>2 & 0x1); // shift up iz by one, and set new bit to that of the child index's bit 2 (z position)
364  child[i].parent = this; // set the parent pointer of every child to the current node
365  }
366  }
367  }
368 
369  // delete children (recursively)
370  void deleteChildren() { // delete children of this node
371  if (child) { // if this node has children, delete them
372  for (int i=0; i<8; i++) {
373  child[i].deleteChildren(); // recursive calls to delete all children branches
374  }
375  delete [] child; // free the memory allocated for the 8 children
376  child = NULL; // set children pointer to NULL to indicate that there are no children now
377  }
378  }
379 
380  // collapse node
381  int collapseChildren() { // collapse children in this node if they are all the same medium
382  if (child) { // check that we indeed have children
383  for (int i=0; i<8; i++) { // loop over each of the 8 children of this node
384  if (child[i].child) {
385  return 0; // bail out if there are nodes below the children (this could be made recursive)
386  }
387  if (child[i].medium!=child[0].medium) {
388  return 0; // bail out as soon as one children has a different medium
389  }
390  }
391  medium = child[0].medium; // set the medium of this node to that of the children
392  deleteChildren(); // delete the children of this node
393  return 1; // return 1 to indicate that the children were collapsed
394  }
395  return 0;
396  }
397 
398  // insideBBox
399  bool insideBBox(EGS_Octree_bbox &bbox) {
400  int shift = bbox.maxlevel - level;
401  int ii;
402  ii = (ix<<shift);
403  if (ii < bbox.ixmin) {
404  return false;
405  }
406  ii = ~(~ix<<shift);
407  if (ii > bbox.ixmax) {
408  return false;
409  }
410  ii = (iy<<shift);
411  if (ii < bbox.iymin) {
412  return false;
413  }
414  ii = ~(~iy<<shift);
415  if (ii > bbox.iymax) {
416  return false;
417  }
418  ii = iz<<shift;
419  if (ii < bbox.izmin) {
420  return false;
421  }
422  ii = ~(~iz<<shift);
423  if (ii > bbox.izmax) {
424  return false;
425  }
426  return true;
427  }
428 
429  // instersectBBox
430  bool intersectBBox(EGS_Octree_bbox &bbox) {
431 
432  int shift = bbox.maxlevel - level;
433  int iimin, iimax;
434 
435  iimin = (ix<<shift);
436  if (bbox.ixmin > iimin) {
437  iimin = bbox.ixmin;
438  }
439  iimax = ~(~ix<<shift);
440  if (bbox.ixmax < iimax) {
441  iimax = bbox.ixmax;
442  }
443  if (iimax < iimin) {
444  return false;
445  }
446 
447  iimin = (iy<<shift);
448  if (bbox.iymin > iimin) {
449  iimin = bbox.iymin;
450  }
451  iimax = ~(~iy<<shift);
452  if (bbox.iymax < iimax) {
453  iimax = bbox.iymax;
454  }
455  if (iimax < iimin) {
456  return false;
457  }
458 
459  iimin = (iz<<shift);
460  if (bbox.izmin > iimin) {
461  iimin = bbox.izmin;
462  }
463  iimax = ~(~iz<<shift);
464  if (bbox.izmax < iimax) {
465  iimax = bbox.izmax;
466  }
467  if (iimax < iimin) {
468  return false;
469  }
470 
471  return true;
472  }
473 };
474 
475 
519 class EGS_OCTREE_EXPORT EGS_Octree : public EGS_BaseGeometry {
520 
521  EGS_Octree_node *root;
522  EGS_Octree_node **nodeReg;
523  EGS_BaseGeometry *geom;
524  EGS_Float bbxmin, bbymin, bbzmin;
525  EGS_Float bbxmax, bbymax, bbzmax;
526  EGS_Float xmin, ymin, zmin;
527  EGS_Float xmax, ymax, zmax;
528  EGS_Float dx, dy, dz, dxi, dyi, dzi;
529  int ixmin, iymin, izmin;
530  int ixmax, iymax, izmax;
531  int maxlevel, n;
532  int nx, ny, nz;
533  static string type;
534  long int nLeaf, nLeafMax;
535  vector<EGS_Octree_node *> tmp;
536 
537 public:
538 
539  EGS_Octree(vector<EGS_Octree_bbox> &vBox, bool pruneTree, EGS_BaseGeometry *g) : EGS_BaseGeometry(""), geom(g) {
540 
541  // combine bounding boxes to get the overall bounding box
542  EGS_Octree_bbox bbox(vBox[0]);
543  for (int i=1; i<vBox.size(); i++) {
544  bbox += vBox[i];
545  }
546  vBox.push_back(bbox);
547 
548  // save overall resolution
549  nx = bbox.nx;
550  ny = bbox.ny;
551  nz = bbox.nz;
552 
553  // set size of cells at maxlevel
554  dx = (bbox.vmax.x - bbox.vmin.x) / bbox.nx;
555  dy = (bbox.vmax.y - bbox.vmin.y) / bbox.ny;
556  dz = (bbox.vmax.z - bbox.vmin.z) / bbox.nz;
557 
558  // pre-compute cell size inverses to save time
559  dxi = 1.0/dx;
560  dyi = 1.0/dy;
561  dzi = 1.0/dz;
562 
563  // set octree depth (maxlevel)
564  int res = nx;
565  if (ny > res) {
566  res = ny;
567  }
568  if (nz > res) {
569  res = nz;
570  }
571  maxlevel = (int) ceil(log((EGS_Float)res)/0.6931471805599452862);
572 
573  // set octree cell count at maxlevel;
574  n = 1<<maxlevel;
575 
576  // set octree bounds in space
577  xmin = bbox.vmin.x;
578  ymin = bbox.vmin.y;
579  zmin = bbox.vmin.z;
580  xmax = bbox.vmin.x + dx*n;
581  ymax = bbox.vmin.y + dy*n;
582  zmax = bbox.vmin.z + dz*n;
583 
584  // set cell indices range for each box;
585  {
586  for (int i=0; i<vBox.size(); i++) {
587  EGS_Octree_bbox *box = &vBox[i];
588  box->ixmin = (int)((box->vmin.x - xmin + 0.5*dx) * dxi);
589  box->iymin = (int)((box->vmin.y - ymin + 0.5*dy) * dyi);
590  box->izmin = (int)((box->vmin.z - zmin + 0.5*dz) * dzi);
591  box->nx = (int)((box->vmax.x - box->vmin.x + 0.5*dx) * dxi);
592  box->ny = (int)((box->vmax.y - box->vmin.y + 0.5*dy) * dyi);
593  box->nz = (int)((box->vmax.z - box->vmin.z + 0.5*dz) * dzi);
594  box->ixmax = box->ixmin + box->nx - 1;
595  box->iymax = box->iymin + box->ny - 1;
596  box->izmax = box->izmin + box->nz - 1;
597  if (box->ixmin<0) {
598  box->ixmin = 0;
599  }
600  if (box->ixmax>=n) {
601  box->ixmax = n-1;
602  }
603  if (box->iymin<0) {
604  box->iymin = 0;
605  }
606  if (box->iymax>=n) {
607  box->iymax = n-1;
608  }
609  if (box->izmin<0) {
610  box->izmin = 0;
611  }
612  if (box->izmax>=n) {
613  box->izmax = n-1;
614  }
615  box->nx = box->ixmax - box->ixmin + 1;
616  box->ny = box->iymax - box->iymin + 1;
617  box->nz = box->izmax - box->izmin + 1;
618  }
619  }
620 
621 
622  // determine maxlevel in each defined bounding box
623 // {for (int i=0; i<vBox.size(); i++) {
624 // EGS_Octree_bbox *box = &vBox[i];
625 // EGS_Float scale = box->nxtree/(EGS_Float)box->nx;
626 // if (box->nytree/box->ny > scale) scale = box->nytree/(EGS_Float)box->ny;
627 // if (box->nztree/box->nz > scale) scale = box->nztree/(EGS_Float)box->nz;
628 // int difflevel = (int) ceil (log(scale)/0.6931471805599452862);
629 // box->maxlevel = maxlevel;
630 // box->level = maxlevel-difflevel;
631 // }}
632 
633  // avoid indirections for overall bounding box parameters
634  EGS_Octree_bbox *box = &(vBox.back());
635  bbxmin = box->vmin.x;
636  bbxmax = box->vmax.x;
637  bbymin = box->vmin.y;
638  bbymax = box->vmax.y;
639  bbzmin = box->vmin.z;
640  bbzmax = box->vmax.z;
641  ixmin = box->ixmin;
642  ixmax = box->ixmax;
643  iymin = box->iymin;
644  iymax = box->iymax;
645  izmin = box->izmin;
646  izmax = box->izmax;
647 
648  // build octree
649  root = new EGS_Octree_node();
650  growOctree(root, vBox, pruneTree);
651 
652  // allocate memory to hold the region-indexed node pointers in a simple array, and free up the tmp vector
653  nreg = tmp.size();
654  nodeReg = new EGS_Octree_node* [nreg];
655  {
656  for (int i=0; i<nreg; i++) {
657  nodeReg[i] = tmp[i];
658  }
659  }
660  tmp.erase(tmp.begin(),tmp.end());
661 
662  // calculate leaf node statistics
663  nLeaf = 0;
664  nLeafMax = 0;
665  statOctree(root, vBox);
666  }
667 
668 
669  // destructor
670  ~EGS_Octree() {
671  if (root) {
672  root->deleteChildren();
673  delete root;
674  delete [] nodeReg;
675  }
676  }
677 
678 
679  // statOctree
680  void statOctree(EGS_Octree_node *node, vector<EGS_Octree_bbox> &vBox) {
681  if (node->child) {
682  for (int i=0; i<8; i++) {
683  statOctree(node->child+i, vBox);
684  }
685  }
686  else if (node->insideBBox(vBox[vBox.size()-1]) && node->medium>=0) {
687  int shift = maxlevel - node->level;
688  nLeaf++;
689  nLeafMax += 1<<(3*shift);
690  }
691  }
692 
693 
694  // growOctree (recursive)
695  void growOctree(EGS_Octree_node *node, vector<EGS_Octree_bbox> &vBox, bool prune) {
696 
697  // assume node needs no refinement
698  bool refineNode = false;
699 
700  // check if we need to refine this node
701  if (node->level < maxlevel) {
702 
703  // check all bounding boxes to determine local refinement level (priority to boxes defined later)
704  int level = 0;
705  {
706  for (int i=0; i<vBox.size()-1; i++) {
707  int boxlevel = vBox[i].level;
708  if (node->insideBBox(vBox[i])) {
709  level = boxlevel;
710  }
711  }
712  }
713 
714  // node may still need refinement if it intersects a higher resolution bounding box
715  {
716  for (int i=0; i<vBox.size()-1; i++) {
717  int boxlevel = vBox[i].level;
718  if (!node->insideBBox(vBox[i]) && node->intersectBBox(vBox[i])) {
719  if (boxlevel > level) {
720  level = boxlevel;
721  }
722  }
723  }
724  }
725 
726  // if the level of this node is not sufficient, set it for refinement
727  if (node->level < level) {
728  refineNode = true;
729  }
730  }
731 
732  // if this node needs refinement, grow children branches (recursively), and then try to collapse children
733  if (refineNode) {
734  node->createChildren();
735  for (int i=0; i<8; i++) {
736  growOctree(node->child+i, vBox, prune);
737  }
738  if (prune) {
739  if (node->collapseChildren()) {
740  tmp.resize(tmp.size()-8);
741  node->region = tmp.size();
742  tmp.push_back(node);
743  }
744  }
745  }
746  // otherwise this node is a leaf: set the node medium and add it to the region list
747  else {
748  bool insideSomeBox = false;
749  for (int i=0; i<vBox.size()-1; i++) {
750  if (node->insideBBox(vBox[i])) {
751  insideSomeBox = true;
752  break;
753  }
754  }
755  node->medium = -1;
756  if (insideSomeBox) {
757  int shift = maxlevel - node->level;
758  int ix = node->ix << shift;
759  int iy = node->iy << shift;
760  int iz = node->iz << shift;
761  int ireg = geom->isWhere(EGS_Vector(xmin+(ix+0.5)*dx, ymin+(iy+0.5)*dy, zmin+(iz+0.5)*dz));
762  if (ireg>=0) {
763  node->medium = geom->medium(ireg);
764  }
765  }
766  node->region = tmp.size();
767  tmp.push_back(node);
768  }
769  }
770 
771 
772  // getNeighborNodeX
773  EGS_Octree_node *getNeighborNodeX(EGS_Octree_node *node, int ixn, int iyn, int izn) {
774 
775  // check if neighbor index is in range
776  if (ixn < ixmin || ixn > ixmax) {
777  return NULL;
778  }
779 
780  // constrain neighbor indices in y and z
781  int shift = maxlevel - node->level;
782  if (iyn < node->iy<<shift) {
783  iyn = node->iy<<shift;
784  }
785  else if (iyn > ~(~node->iy<<shift)) {
786  iyn = ~(~node->iy<<shift);
787  }
788  if (izn < node->iz<<shift) {
789  izn = node->iz<<shift;
790  }
791  else if (izn > ~(~node->iz<<shift)) {
792  izn = ~(~node->iz<<shift);
793  }
794 
795  // walk up and down the octree to new cell
796  int diff = node->ix ^ (ixn>>shift);
797  shift = 0;
798  while (diff & (1<<shift)) {
799  node = node->parent;
800  shift++;
801  }
802  shift = maxlevel - node->level;
803  while (node->child) {
804  shift--;
805  int childIndex;
806  childIndex = (ixn>>shift & 0x1);
807  childIndex |= (iyn>>shift & 0x1) << 1;
808  childIndex |= (izn>>shift & 0x1) << 2;
809  node = node->child + childIndex;
810  }
811  shift = maxlevel - node->level;
812 
813  return node;
814  }
815 
816 
817  // getNeighborNodeY
818  EGS_Octree_node *getNeighborNodeY(EGS_Octree_node *node, int ixn, int iyn, int izn) {
819 
820  // check if neighbor index is in range
821  if (iyn < iymin || iyn > iymax) {
822  return NULL;
823  }
824 
825  // constrain neighbor indices in x and z
826  int shift = maxlevel - node->level;
827  if (ixn < node->ix<<shift) {
828  ixn = node->ix<<shift;
829  }
830  else if (ixn > ~(~node->ix<<shift)) {
831  ixn = ~(~node->ix<<shift);
832  }
833  if (izn < node->iz<<shift) {
834  izn = node->iz<<shift;
835  }
836  else if (izn > ~(~node->iz<<shift)) {
837  izn = ~(~node->iz<<shift);
838  }
839 
840  // walk up and down the octree to new cell
841  int diff = node->iy ^ (iyn>>shift);
842  shift = 0;
843  while (diff & (1<<shift)) {
844  node = node->parent;
845  shift++;
846  }
847  shift = maxlevel - node->level;
848  while (node->child) {
849  shift--;
850  int childIndex;
851  childIndex = (ixn>>shift & 0x1);
852  childIndex |= (iyn>>shift & 0x1) << 1;
853  childIndex |= (izn>>shift & 0x1) << 2;
854  node = node->child + childIndex;
855  }
856  return node;
857  }
858 
859 
860  // getNeighborNodeZ
861  EGS_Octree_node *getNeighborNodeZ(EGS_Octree_node *node, int ixn, int iyn, int izn) {
862 
863  // check if neighbor index is in range
864  if (izn < izmin || izn > izmax) {
865  return NULL;
866  }
867 
868  // constrain neighbor indices in x and y
869  int shift = maxlevel - node->level;
870  if (ixn < node->ix<<shift) {
871  ixn = node->ix<<shift;
872  }
873  else if (ixn > ~(~node->ix<<shift)) {
874  ixn = ~(~node->ix<<shift);
875  }
876  if (iyn < node->iy<<shift) {
877  iyn = node->iy<<shift;
878  }
879  else if (iyn > ~(~node->iy<<shift)) {
880  iyn = ~(~node->iy<<shift);
881  }
882 
883  // walk up and down the octree to new cell
884  int diff = node->iz ^ (izn>>shift);
885  shift = 0;
886  while (diff & (1<<shift)) {
887  node = node->parent;
888  shift++;
889  }
890  shift = maxlevel - node->level;
891  while (node->child) {
892  shift--;
893  int childIndex;
894  childIndex = (ixn>>shift & 0x1);
895  childIndex |= (iyn>>shift & 0x1) << 1;
896  childIndex |= (izn>>shift & 0x1) << 2;
897  node = node->child + childIndex;
898  }
899  return node;
900  }
901 
902 
903  // getNode
904  EGS_Octree_node *getNode(int ix, int iy, int iz) {
905  if ((ix<ixmin) || (ix>ixmax) || (iy<iymin) || (iy>iymax) || (iz<izmin) || (iz>izmax)) {
906  return NULL;
907  }
908  EGS_Octree_node *node = root;
909  int shift = maxlevel;
910  while (node->child) {
911  shift--;
912  int childIndex;
913  childIndex = (ix>>shift & 0x1);
914  childIndex |= (iy>>shift & 0x1) << 1;
915  childIndex |= (iz>>shift & 0x1) << 2;
916  node = node->child + childIndex;
917  }
918  return node;
919  }
920 
921 
922  // setIndices
923  void setIndices(const EGS_Vector &r, int &ix, int &iy, int &iz) {
924  ix = (int)((r.x-xmin)*dxi);
925  iy = (int)((r.y-ymin)*dyi);
926  iz = (int)((r.z-zmin)*dzi);
927  if (ix<ixmin) {
928  ix=ixmin;
929  }
930  if (ix>ixmax) {
931  ix=ixmax;
932  }
933  if (iy<iymin) {
934  iy=iymin;
935  }
936  if (iy>iymax) {
937  iy=iymax;
938  }
939  if (iz<izmin) {
940  iz=izmin;
941  }
942  if (iz>izmax) {
943  iz=izmax;
944  }
945  }
946 
947 
948  // isInside
949  bool isInside(const EGS_Vector &r) {
950  if (r.x >= bbxmin && r.x <= bbxmax &&
951  r.y >= bbymin && r.y <= bbymax &&
952  r.z >= bbzmin && r.z <= bbzmax) {
953  return true;
954  }
955  return false;
956  }
957 
958 
959  // isWhere
960  int isWhere(const EGS_Vector &r) {
961  if (!isInside(r)) {
962  return -1;
963  }
964  int ix, iy, iz;
965  setIndices(r,ix,iy,iz);
966  return getNode(ix,iy,iz)->region;
967  }
968 
969 
970  // inside (deprecated)
971  int inside(const EGS_Vector &r) {
972  return isWhere(r);
973  }
974 
975 
976  // isWhereFast
977  int isWhereFast(const EGS_Vector &r) {
978  if (!isInside(r)) {
979  return -1;
980  }
981  int ix, iy, iz;
982  setIndices(r, ix, iy, iz);
983  return getNode(ix,iy,iz)->region;
984  }
985 
986 
987  // medium
988  int medium(int ireg) const {
989  return nodeReg[ireg]->medium;
990  }
991 
992 
993  // howfarIn
994  int howfarIn(EGS_Octree_node *node, const EGS_Vector &r, const EGS_Vector &u, EGS_Float &t, EGS_Vector *normal=0) {
995 
996  int ix, iy, iz, tmp;
997  int crossed = -1;
998 
999  if (!node) {
1000  return -1;
1001  }
1002 
1003  // set shift and local cell indices
1004  int shift = maxlevel - node->level; // how many levels missing between current level and full depth
1005  ix = node->ix;
1006  iy = node->iy;
1007  iz = node->iz;
1008 
1009  // x direction
1010  if (u.x > 0) {
1011  ix = ~(~ix<<shift); // fill lower level bits with 1's to always consider +x below current node
1012  EGS_Float xBound;
1013  if (ix>=ixmax) {
1014  xBound = bbxmax;
1015  }
1016  else {
1017  xBound = xmin + (ix+1)*dx;
1018  }
1019  EGS_Float d = (xBound-r.x) / u.x;
1020  if (d <= t) {
1021  t = d;
1022  crossed = 0;
1023  ix++;
1024  if (normal) {
1025  *normal = EGS_Vector(-1,0,0);
1026  }
1027  }
1028  }
1029  else if (u.x < 0) {
1030  ix = ix<<shift; // fill lower level bits with 0's to always consider -x below current node
1031  EGS_Float xBound;
1032  if (ix<=ixmin) {
1033  xBound = bbxmin;
1034  }
1035  else {
1036  xBound = xmin + ix*dx;
1037  }
1038  EGS_Float d = (xBound-r.x) / u.x;
1039  if (d <= t) {
1040  t = d;
1041  crossed = 0;
1042  ix--;
1043  if (normal) {
1044  *normal = EGS_Vector(1,0,0);
1045  }
1046  }
1047  }
1048 
1049 
1050  // y direction
1051  if (u.y > 0) {
1052  iy = ~(~iy<<shift); // fill lower level bits with 1's to always consider +y below current node
1053  EGS_Float yBound;
1054  if (iy>=iymax) {
1055  yBound = bbymax;
1056  }
1057  else {
1058  yBound = ymin + (iy+1)*dy;
1059  }
1060  EGS_Float d = (yBound - r.y) / u.y;
1061  if (d <= t) {
1062  t = d;
1063  crossed = 1;
1064  iy++;
1065  if (normal) {
1066  *normal = EGS_Vector(0,-1,0);
1067  }
1068  }
1069  }
1070  else if (u.y < 0) {
1071  iy = iy<<shift; // fill lower level bits with 0's to always consider -y below current node
1072  EGS_Float yBound;
1073  if (iy<=iymin) {
1074  yBound = bbymin;
1075  }
1076  else {
1077  yBound = ymin + iy*dy;
1078  }
1079  EGS_Float d = (yBound-r.y) / u.y;
1080  if (d <= t) {
1081  t = d;
1082  crossed = 1;
1083  iy--;
1084  if (normal) {
1085  *normal = EGS_Vector(0,1,0);
1086  }
1087  }
1088  }
1089 
1090  // z direction
1091  if (u.z > 0) {
1092  iz = ~(~iz<<shift); // fill lower level bits with 1's to always consider +z below current node
1093  EGS_Float zBound;
1094  if (iz>=izmax) {
1095  zBound = bbzmax;
1096  }
1097  else {
1098  zBound = zmin + (iz+1)*dz;
1099  }
1100  EGS_Float d = (zBound-r.z) / u.z;
1101  if (d <= t) {
1102  t = d;
1103  crossed = 2;
1104  iz++;
1105  if (normal) {
1106  *normal = EGS_Vector(0,0,-1);
1107  }
1108  }
1109  }
1110  else if (u.z < 0) {
1111  iz = iz<<shift; // fill lower level bits with 0's to always consider -z below current node
1112  EGS_Float zBound;
1113  if (iz<=izmin) {
1114  zBound = bbzmin;
1115  }
1116  else {
1117  zBound = zmin + iz*dz;
1118  }
1119  EGS_Float d = (zBound-r.z) / u.z;
1120  if (d <= t) {
1121  t = d;
1122  crossed = 2;
1123  iz--;
1124  if (normal) {
1125  *normal = EGS_Vector(0,0,1);
1126  }
1127  }
1128  }
1129 
1130  // get the new region index for the neighbor cell:
1131  // 1) find the new position in the plane perpendicular to the crossing direction
1132  // 2) get the indices for the neighbor cell at maximum depth, corresponding to that position
1133  // 3) call an axis specific function to get the neighbor node
1134  if (crossed==0) {
1135  EGS_Vector ryz(r.x, r.y+t*u.y, r.z+t*u.z);
1136  setIndices(ryz, tmp, iy, iz);
1137  node = getNeighborNodeX(node, ix, iy, iz);
1138  }
1139  else if (crossed==1) {
1140  EGS_Vector rxz(r.x+t*u.x, r.y, r.z+t*u.z);
1141  setIndices(rxz, ix, tmp, iz);
1142  node = getNeighborNodeY(node, ix, iy, iz);
1143  }
1144  else if (crossed==2) {
1145  EGS_Vector rxy(r.x+t*u.x, r.y+t*u.y, r.z);
1146  setIndices(rxy, ix, iy, tmp);
1147  node = getNeighborNodeZ(node, ix, iy, iz);
1148  }
1149 
1150  if (node) {
1151  return node->region;
1152  }
1153  else {
1154  return -1;
1155  }
1156  }
1157 
1158 
1159  // howfarOut
1160  int howfarOut(const EGS_Vector &r, const EGS_Vector &u, EGS_Float &t, EGS_Vector *normal=0) {
1161 
1162  int tmp;
1163  int ix=0, iy=0, iz=0;
1164  EGS_Float d, tlong = 2*t;
1165  EGS_Octree_node *node = NULL;
1166 
1167  // x axis
1168  if (r.x <= bbxmin && u.x > 0) {
1169  ix = ixmin;
1170  d = (bbxmin-r.x) / u.x;
1171  }
1172  else if (r.x >= bbxmax && u.x < 0) {
1173  ix = ixmax;
1174  d = (bbxmax-r.x) / u.x;
1175  }
1176  else {
1177  d = tlong;
1178  }
1179  if (d <= t) {
1180  EGS_Float yy = r.y + u.y*d;
1181  EGS_Float zz = r.z + u.z*d;
1182  if (yy >= bbymin && yy <= bbymax && zz >= bbzmin && zz <= bbzmax) {
1183  EGS_Vector rr(0,yy,zz);
1184  setIndices(rr, tmp, iy, iz);
1185  t = d;
1186  if (normal) {
1187  *normal = (ix == ixmin) ? EGS_Vector(-1,0,0) : EGS_Vector(1,0,0);
1188  }
1189  node = getNode(ix,iy,iz);
1190  return node->region;
1191  }
1192  }
1193 
1194  // y axis
1195  if (r.y <= bbymin && u.y > 0) {
1196  iy = iymin;
1197  d = (bbymin-r.y) / u.y;
1198  }
1199  else if (r.y >= bbymax && u.y < 0) {
1200  iy = iymax;
1201  d = (bbymax-r.y) / u.y;
1202  }
1203  else {
1204  d = tlong;
1205  }
1206  if (d <= t) {
1207  EGS_Float xx = r.x + u.x*d;
1208  EGS_Float zz = r.z + u.z*d;
1209  if (xx >= bbxmin && xx <= bbxmax && zz >= bbzmin && zz <= bbzmax) {
1210  EGS_Vector rr(xx,0,zz);
1211  setIndices(rr, ix, tmp, iz);
1212  t = d;
1213  if (normal) {
1214  *normal = (iy == iymin) ? EGS_Vector(0,-1,0) : EGS_Vector(0,1,0);
1215  }
1216  node = getNode(ix,iy,iz);
1217  return node->region;
1218  }
1219  }
1220 
1221  // z axis
1222  if (r.z <= bbzmin && u.z > 0) {
1223  iz = izmin;
1224  d = (bbzmin-r.z) / u.z;
1225  }
1226  else if (r.z >= bbzmax && u.z < 0) {
1227  iz = izmax;
1228  d = (bbzmax-r.z) / u.z;
1229  }
1230  else {
1231  d = tlong;
1232  }
1233  if (d <= t) {
1234  EGS_Float xx = r.x + u.x*d;
1235  EGS_Float yy = r.y + u.y*d;
1236  if (xx >= bbxmin && xx <= bbxmax && yy >= bbymin && yy <= bbymax) {
1237  EGS_Vector rr(xx,yy,0);
1238  setIndices(rr, ix, iy, tmp);
1239  t = d;
1240  if (normal) {
1241  *normal = (iz == izmin) ? EGS_Vector(0,0,-1) : EGS_Vector(0,0,1);
1242  }
1243  node = getNode(ix,iy,iz);
1244  return node->region;
1245  }
1246  }
1247 
1248  return -1;
1249  }
1250 
1251 
1252  // howfar
1253  int howfar(int ireg, const EGS_Vector &r, const EGS_Vector &u, EGS_Float &t, int *newmed, EGS_Vector *normal=0) {
1254 
1255  int inew = ireg;
1256 
1257  // get new region number
1258  if (ireg==-1) {
1259  inew = howfarOut(r, u, t, normal);
1260  }
1261  else {
1262  inew = howfarIn(nodeReg[ireg], r, u, t, normal);
1263  }
1264 
1265  // set new medium
1266  if (inew>=0 && newmed) {
1267  *newmed = nodeReg[inew]->medium;
1268  }
1269  return inew;
1270  }
1271 
1272 
1273  // hownearIn
1274  EGS_Float hownearIn(int ireg, const EGS_Vector &r) {
1275  EGS_Octree_node *node = nodeReg[ireg];
1276  int shift = maxlevel - node->level;
1277  EGS_Float t1, t2, tx, ty, tz;
1278  int imin, imax;
1279 
1280  // x
1281  imin = node->ix << shift;
1282  imax = ~(~node->ix << shift);
1283  t1 = (r.x-xmin)-dx*imin;
1284  t2 = dx*(imax-imin+1)-t1;
1285  tx = t1 < t2 ? t1 : t2;
1286 
1287  // y
1288  imin = node->iy << shift;
1289  imax = ~(~node->iy << shift);
1290  t1 = (r.y-ymin)-dy*imin;
1291  t2 = dy*(imax-imin+1)-t1;
1292  ty = t1 < t2 ? t1 : t2;
1293 
1294  // z
1295  imin = node->iz << shift;
1296  imax = ~(~node->iz << shift);
1297  t1 = (r.z-zmin)-dz*imin;
1298  t2 = dz*(imax-imin+1)-t1;
1299  tz = t1 < t2 ? t1 : t2;
1300 
1301  return tx<ty && tx<tz ? tx : ty<tz ? ty : tz;
1302  }
1303 
1304 
1305  // hownear
1306  EGS_Float hownear(int ireg, const EGS_Vector &r) {
1307  if (ireg>=0) {
1308  return hownearIn(ireg, r);
1309  }
1310  int nc=0;
1311  EGS_Float s1=0, s2=0;
1312  if (r.x < bbxmin || r.x > bbxmax) {
1313  EGS_Float t = r.x < bbxmin ? bbxmin-r.x : r.x-bbxmax;
1314  nc++;
1315  s1 += t;
1316  s2 += t*t;
1317  }
1318  if (r.y < bbymin || r.y > bbymax) {
1319  EGS_Float t = r.y < bbymin ? bbymin-r.y : r.y-bbymax;
1320  nc++;
1321  s1 += t;
1322  s2 += t*t;
1323  }
1324  if (r.z < bbzmin || r.z > bbzmax) {
1325  EGS_Float t = r.z < bbzmin ? bbzmin-r.z : r.z-bbzmax;
1326  nc++;
1327  s1 += t;
1328  s2 += t*t;
1329  }
1330  return nc == 1 ? s1 : sqrt(s2);
1331  }
1332 
1333 
1334  // getType
1335  const string &getType() const {
1336  return type;
1337  }
1338 
1339  // printInfo
1340  void printInfo() const;
1341 };
1342 
1343 #endif
Base geometry class. Every geometry class must be derived from EGS_BaseGeometry.
virtual EGS_Float hownear(int ireg, const EGS_Vector &x)=0
Calculate the distance to a boundary for position x in any direction.
virtual int inside(const EGS_Vector &x)=0
Returns the region index, if inside, or -1 if outside (obsolete)
virtual const string & getType() const =0
Get the geometry type.
int nreg
Number of local regions in this geometry.
virtual int howfar(int ireg, const EGS_Vector &x, const EGS_Vector &u, EGS_Float &t, int *newmed=0, EGS_Vector *normal=0)=0
Calculate the distance to a boundary from x along the direction u.
virtual bool isInside(const EGS_Vector &x)=0
Is the position x inside the geometry?
virtual int medium(int ireg) const
Returns the medium index in region ireg.
virtual void printInfo() const
Print information about this geometry.
EGS_BaseGeometry(const string &Name)
Construct a geometry named Name.
virtual int isWhere(const EGS_Vector &x)=0
In which region is poisition x?
EGS_Octree_node * child
pointer to children nodes, NULL is there are no children
Definition: egs_octree.h:339
EGS_Octree_node * parent
pointer to the parent node (only root node can have parent set to NULL)
Definition: egs_octree.h:340
short medium
medium index for the node
Definition: egs_octree.h:337
int iz
octree indices, representing the binary location code of the node in x, y, z;
Definition: egs_octree.h:335
int region
region number of the node
Definition: egs_octree.h:336
unsigned short level
depth of the node (root node is level 0)
Definition: egs_octree.h:338
An octree geometry.
Definition: egs_octree.h:519
A class representing 3D vectors.
Definition: egs_vector.h:57
EGS_Float y
y-component
Definition: egs_vector.h:62
EGS_Float z
z-component
Definition: egs_vector.h:63
EGS_Float x
x-component
Definition: egs_vector.h:61
EGS_BaseGeometry class header file.
Global egspp functions header file.
EGS_InfoFunction EGS_EXPORT egsFatal
Always use this function for reporting fatal errors.