libatomprobe
Library for Atom Probe Tomography (APT) computation
voxels.h
Go to the documentation of this file.
1  /*
2  * common/voxels.h - Voxelised data manipulation class
3  * Copyright (C) 2018, D Haley
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifndef ATOMPROBE_VOXELS_H
20 #define ATOMPROBE_VOXELS_H
21 
23 
27 
28 #include <stack>
29 #include <numeric>
30 #include <atomic>
31 #include <algorithm>
32 #include <fstream>
33 
34 #ifdef _OPENMP
35  #include <omp.h>
36 #endif
37 
38 #ifndef XOR
39  #define XOR(a,b) ((!(a)) ^ (!(b)))
40 #endif
41 
42 namespace AtomProbe{
44 
47 enum{
54 };
55 
56 //Interpolation mode for slice
57 enum
58 {
62 };
63 
64 enum{
67 };
68 
69 //Error codes
70 enum{
75 };
76 
77 
79 
81 enum {
85 };
86 
87 
88 enum {
92 };
93 
94 #ifdef DEBUG
95  bool runVoxelTests();
96 #endif
97 
98 //Voxel abortion flag. Set to true to initiate abort from supporting algorithms
99 
100 
101 
103 
106 template<class T> class Voxels
107 {
108  private:
110  size_t binCount[3];
111 
113  std::vector<T> voxels;
114 
116  //Dataset is assumed to sit in a rectilinear volume from minBound
117  //to maxBound
118  AtomProbe::Point3D minBound, maxBound;
119 
120  size_t shape3(size_t x,size_t y, size_t z) const;
121  public:
122 
124  Voxels();
126  ~Voxels();
127 
129  void swap(Voxels<T> &v);
130 
132  void copy(Voxels<T> &newCopy) const;
133 
135  void setPoint(const AtomProbe::Point3D &pt, const T &val);
136 
138  T getPointData(const AtomProbe::Point3D &pt) const;
139 
140 
142  void getIndex(size_t &x, size_t &y,
143  size_t &z, const AtomProbe::Point3D &p) const;
144 
146  // including upper borders
147  void getIndexWithUpper(size_t &x, size_t &y,
148  size_t &z, const AtomProbe::Point3D &p) const;
149 
151  AtomProbe::Point3D getPoint(size_t x,
152  size_t y, size_t z) const;
154  inline T getData(size_t x, size_t y, size_t z) const;
156  inline T getData(size_t i) const { return voxels[i];}
157 
158  void setEntry(size_t n, const T &val) { voxels[n] = val;};
160  //const T &getDataRef(size_t x, size_t y, size_t z) const;
162  void setData(size_t x, size_t y, size_t z, const T &val);
164  void setData(size_t n, const T &val);
165 
166  //Obtain an interpolated entry. The interpolated values are obtained by padding
167  void getInterpolatedData(const AtomProbe::Point3D &pt, T &v) const;
168 
169 
170  //get an interpolated slice from a section of the data
171  void getInterpSlice(size_t normal, float offset, T *p,
172  size_t interpMode=VOX_INTERP_NONE) const;
173 
174  //Get a specific slice, from an integral offset in the data, no interp
175  void getSlice(size_t normal, size_t offset, T *p) const;
177  void getSize(size_t &x, size_t &y, size_t &z) const;
178 
180 
184  size_t resize(size_t newX, size_t newY, size_t newZ, const AtomProbe::Point3D &newMinBound=AtomProbe::Point3D(0.0f,0.0f,0.0f), const AtomProbe::Point3D &newMaxBound=AtomProbe::Point3D(1.0f,1.0f,1.0f));
185 
186  size_t resize(const Voxels<T> &v);
187 
189 
193  size_t resizeKeepData(size_t newX, size_t newY, size_t newZ,
194  unsigned int direction=CLIP_LOWER_SOUTH_WEST, const AtomProbe::Point3D &newMinBound=AtomProbe::Point3D(0.0f,0.0f,0.0f), const AtomProbe::Point3D &newMaxBound=AtomProbe::Point3D(1.0f,1.0f,1.0f), const T &fill=T(0),bool doFill=false);
195 
196 
197 
199 
204  size_t deprecatedGetEdgeUniqueIndex(size_t x,
205  size_t y, size_t z, unsigned int edge) const;
206 
208 
213  size_t getCellUniqueEdgeIndex(size_t x,
214  size_t y, size_t z, unsigned int edge) const;
215 
217  // returns the axis value so you know edge vector too.
218  // NOte that the value to pass as the edge index is (getEdgeIndex>>2)<<2 to
219  // make the ownership of the voxel correct
220  void getEdgeEnds(size_t edgeIndex,AtomProbe::Point3D &a, AtomProbe::Point3D &b) const;
221 
222 
224  void getEdgeCell(size_t edgeUniqId, size_t &x,size_t &y, size_t &z, size_t &axis ) const;
225 
226  //TODO: there is duplicate code between this and getEdgeEnds. Refactor.
228  void getEdgeEndApproxVals(size_t edgeUniqId, T &a, T &b ) const;
229 
230 
232 
234  T getSum(const T &initialVal=T(0.0)) const;
235 
237  size_t count(const T &minIntensity) const;
238 
240  void fill(const T &val);
244  //Obtain the bounds for a specified axis
245  void getAxisBounds(size_t axis, float &minV, float &maxV) const;
249  void setBounds(const AtomProbe::Point3D &pMin, const AtomProbe::Point3D &pMax);
250  void setBounds(const BoundCube &b);
252  void getBounds(AtomProbe::Point3D &pMin, AtomProbe::Point3D &pMax) const { pMin=minBound;pMax=maxBound;}
253  void getBounds(BoundCube &bc) const { bc.setBounds(minBound,maxBound);}
254 
256  size_t init(size_t nX,size_t nY,size_t nZ, const BoundCube &bound);
258  size_t init(size_t nX,size_t nY, size_t nZ);
260 
265  size_t loadFile(const char *cpFilename, size_t nX,
266  size_t nY, size_t nZ, bool silent=false);
268 
271  size_t writeFile(const char *cpFilename) const;
272 
274  /* Data outside (either above if keepUpper is true, or below threshold if keepUpper is false)
275  * will be set to the new value
276  */
277  void threshold(const T &thresh, bool keepUpper,const T & newVal);
278 
280  /* Data below the threshold will be true if keepUpper is false, and vice-versa
281  */
282  void thresholdToBoolMask(const T &thresh, bool keepUpper,Voxels<bool> &result) const;
283 
285  void applyMask(const Voxels<bool> &mask, const T &newVal, bool invert=false);
286 
288 
292  void thresholdForPosition(std::vector<AtomProbe::Point3D> &p, const T &thresh, bool lowerEq=false) const;
293 
294 
295 
296 
298 
300  static size_t sizeofType() { return sizeof(T);};
301 
302 
304  /* On thresholding (val > thresh), set the value to "onThresh".
305  * Otherwise set to "offthresh"
306  */
307  void binarise(Voxels<T> &result,const T &thresh, const T &onThresh,
308  const T &offThresh) const;
309 
310 
312  /*Remove the data from the voxel set
313  */
314  void clear() { voxels.clear();};
315 
317  T min() const;
318 
320  T max() const;
321 
323  void minMax(T &min, T &max) const;
324 
325 
327 
334  int countPoints( const std::vector<AtomProbe::Point3D> &points, bool noWrap=true, bool doErase=true);
335 
337 
344  int countIons( const std::vector<AtomProbe::IonHit> &ions, bool noWrap=true, bool doErase=true);
345 
347  T trapezIntegral() const;
349  // this is done by dividing each voxel by its volume
350  void calculateDensity();
351 
352  float getBinVolume() const;
353 
355  void operator/=(const Voxels<T> &v);
356 
357  void operator/=(const T &v);
358 
359  bool operator==(const Voxels<T> &v) const;
360 
361  size_t size() const { return voxels.size();}
362 };
363 
364 
365 
367 template<class T, class U>
368 void castVoxels(const Voxels<T> &src, Voxels<U> &dest)
369 {
370  //TODO: Remove me!
371  src=dest;
372 }
373 
375 template<class T, class U>
376 void sumVoxels(const Voxels<T> &src, U &counter)
377 {
378  size_t nx,ny,nz;
379  counter=0;
380  src.getSize(nx,ny,nz);
381 
382  size_t nMax=src.size();
383  for(size_t ui=0; ui<nMax; ui++)
384  {
385  counter+=src.getData(ui);
386  }
387 
388 }
389 
390 //====
391 template<class T>
392 Voxels<T>::Voxels() : voxels(), minBound(AtomProbe::Point3D(0,0,0)), maxBound(AtomProbe::Point3D(1,1,1))
393 {
394 }
395 
396 template<class T>
398 {
399 }
400 
401 
402 template<class T>
403 size_t Voxels<T>::shape3(size_t x, size_t y, size_t z) const
404 {
405  return x + y*binCount[0] + z*binCount[0]*binCount[1];
406 }
407 
408 template<class T>
409 void Voxels<T>::copy(Voxels<T> &newVox) const
410 {
411  newVox.binCount[0]=binCount[0];
412  newVox.binCount[1]=binCount[1];
413  newVox.binCount[2]=binCount[2];
414 
415  newVox.voxels=voxels;
416  newVox.minBound=minBound;
417  newVox.maxBound=maxBound;
418 
419 
420 }
421 
422 template<class T>
423 void Voxels<T>::setPoint(const AtomProbe::Point3D &point,const T &val)
424 {
425  ASSERT(voxels.size());
426  size_t pos[3];
427  for(size_t ui=0;ui<3;ui++)
428  pos[ui] = (size_t)round(point[ui]*(float)binCount[ui]);
429 
430  voxels[shape3(pos[0],pos[1],pos[2])] = val;
431 }
432 
433 
434 template<class T>
435 void Voxels<T>::setData(size_t x, size_t y,
436  size_t z, const T &val)
437 {
438  ASSERT(voxels.size());
439 
440  ASSERT( x < binCount[0] && y < binCount[1] && z < binCount[2]);
441  voxels[shape3(x,y,z)]=val;
442 }
443 
444 template<class T>
445 inline void Voxels<T>::setData(size_t n, const T &val)
446 {
447  ASSERT(voxels.size());
448  ASSERT(n<voxels.size());
449 
450  voxels[n] =val;
451 }
452 
453 template<class T>
455 {
456  ASSERT(voxels.size());
457  size_t pos[3];
458  AtomProbe::Point3D offsetFrac;
459  offsetFrac=point-minBound;
460  for(size_t ui=0;ui<3;ui++)
461  {
462  offsetFrac[ui]/=(maxBound[ui]-minBound[ui]);
463  pos[ui] = (size_t)round(offsetFrac[ui]*(float)binCount[ui]);
464  }
465 
466  return voxels[shape3(pos[0],pos[1],pos[2])];
467 }
468 
469 template<class T>
470 AtomProbe::Point3D Voxels<T>::getPoint(size_t x, size_t y, size_t z) const
471 {
472  //ASSERT(x < binCount[0] && y<binCount[1] && z<binCount[2]);
473 
474  return AtomProbe::Point3D((float)x/(float)binCount[0]*(maxBound[0]-minBound[0]) + minBound[0],
475  (float)y/(float)binCount[1]*(maxBound[1]-minBound[1]) + minBound[1],
476  (float)z/(float)binCount[2]*(maxBound[2]-minBound[2]) + minBound[2]);
477 }
478 
479 template<class T>
481 {
482  return AtomProbe::Point3D((float)1.0/(float)binCount[0]*(maxBound[0]-minBound[0]),
483  (float)1.0/(float)binCount[1]*(maxBound[1]-minBound[1]),
484  (float)1.0/(float)binCount[2]*(maxBound[2]-minBound[2]));
485 }
486 
487 template<class T>
488 void Voxels<T>::getSize(size_t &x, size_t &y, size_t &z) const
489 {
490  ASSERT(voxels.size());
491  x=binCount[0];
492  y=binCount[1];
493  z=binCount[2];
494 }
495 
496 template<class T>
497 size_t Voxels<T>::deprecatedGetEdgeUniqueIndex(size_t x,size_t y, size_t z, unsigned int index) const
498 {
499  //This provides a reversible mapping of x,y,z
500  //X aligned edges are first
501  //Y second
502  //Z third
503 
504 
505  //Consider each parallel set of edges (eg all the X aligned edges)
506  //to be the dual grid of the actual grid. From this you can visualise the
507  //cell centres moving -1/2 -/12 units in the direction normal to the edge direction
508  //to produce the centres of the edge. An additional vertex needs to be created at
509  //the end of each dimension not equal to the alignement dim.
510 
511 
512  // ->ASCII ART TIME<-
513  // In each individual cube, the offsets look like this:
514  // ------------7-----------
515  // \ |\ .
516  // |\ | \ .
517  // | 10 | 11
518  // | \ | \ .
519  // | \ | \ .
520  // | \ --------6-------------|
521  // | | | |
522  // 2 | 3 | |
523  // | | | |
524  // | | | |
525  // | | | |
526  // | 0 | 1
527  // \-----|----5----------- |
528  // \ | \ |
529  // 8 | 9 |
530  // \ | \ |
531  // \ |---------4------------
532  //
533  // ^x
534  // z|\ |
535  // \ |
536  // \-->y
537  //
538 
539  switch(index)
540  {
541  //X axis aligned
542  //--
543  case 0:
544  break;
545  case 1:
546  y++; // one across in Y
547  break;
548  case 2:
549  z++;//One across in Z
550  break;
551  case 3:
552  y++;
553  z++;
554  break;
555  //--
556 
557  //Y Axis aligned
558  //--
559  case 4:
560  break;
561  case 5:
562  z++;
563  break;
564  case 6:
565  x++;
566  break;
567  case 7:
568  z++;
569  x++;
570  break;
571  //--
572 
573  //Z Axis aligned
574  //--
575  case 8:
576  break;
577  case 9:
578  y++;
579  break;
580  case 10:
581  x++;
582  break;
583  case 11:
584  x++;
585  y++;
586  break;
587  //--
588  }
589 
590 
591  size_t result = 12*(z + y*(binCount[2]+1) + x*(binCount[2]+1)*(binCount[1]+1)) +
592  index;
593 
594  return result;
595 
596 }
597 
598 /*
599 template<class T>
600 size_t Voxels<T>::getEdgeUniqueIndex(size_t x,size_t y, size_t z, unsigned int index) const
601 {
602  //This provides a reversible mapping of x,y,z
603  //X aligned edges are first
604  //Y second
605  //Z third
606 
607 
608  //Consider each parallel set of edges (eg all the X aligned edges)
609  //to be the dual grid of the actual grid. From this you can visualise the
610  //cell centres moving -1/2 -/12 units in the direction normal to the edge direction
611  //to produce the centres of the edge. An additional vertex needs to be created at
612  //the end of each dimension not equal to the alignement dim.
613 
614 
615  // ->ASCII ART TIME<-
616  // In each individual cube, the offsets look like this:
617  // ------------7-----------
618  // \ |\ .
619  // |\ | \ .
620  // | 10 | 11
621  // | \ | \ .
622  // | \ | \ .
623  // | \ --------6-------------|
624  // | | | |
625  // 2 | 3 | |
626  // | | | |
627  // | | | |
628  // | | | |
629  // | 0 | 1
630  // \-----|----5----------- |
631  // \ | \ |
632  // 8 | 9 |
633  // \ | \ |
634  // \ |---------4------------
635  //
636  // ^x
637  // z|\ |
638  // \ |
639  // \-->y
640  //
641 
642  switch(index)
643  {
644  //X axis aligned
645  //--
646  case 0:
647  break;
648  case 1:
649  y++; // one across in Y
650  break;
651  case 2:
652  z++;//One across in Z
653  break;
654  case 3:
655  y++;
656  z++;
657  break;
658  //--
659 
660  //Y Axis aligned
661  //--
662  case 4:
663  break;
664  case 5:
665  z++;
666  break;
667  case 6:
668  x++;
669  break;
670  case 7:
671  z++;
672  x++;
673  break;
674  //--
675 
676  //Z Axis aligned
677  //--
678  case 8:
679  break;
680  case 9:
681  y++;
682  break;
683  case 10:
684  x++;
685  break;
686  case 11:
687  x++;
688  y++;
689  break;
690  //--
691  }
692 
693  unsigned int axis = index/4;
694  size_t result = 3*(z + y*(binCount[2]+1) + x*(binCount[2]+1)*(binCount[1]+1)) + axis;
695 
696  return result;
697 
698 }
699 */
700 template<class T>
701 size_t Voxels<T>::getCellUniqueEdgeIndex(size_t x,size_t y, size_t z, unsigned int index) const
702 {
703  //This provides a reversible mapping of x,y,z
704  //X aligned edges are first
705  //Y second
706  //Z third
707 
708 
709  //Consider each parallel set of edges (eg all the X aligned edges)
710  //to be the dual grid of the actual grid. From this you can visualise the
711  //cell centres moving -1/2 -/12 units in the direction normal to the edge direction
712  //to produce the centres of the edge. An additional vertex needs to be created at
713  //the end of each dimension not equal to the alignement dim.
714 
715 
716  // ->ASCII ART TIME<-
717  // In each individual cube, the offsets look like this:
718  // ------------7-----------
719  // \ |\ .
720  // |\ | \ .
721  // | 10 | 11
722  // | \ | \ .
723  // | \ | \ .
724  // | \ --------6-------------|
725  // | | | |
726  // 2 | 3 | |
727  // | | | |
728  // | | | |
729  // | | | |
730  // | 0 | 1
731  // \-----|----5----------- |
732  // \ | \ |
733  // 8 | 9 |
734  // \ | \ |
735  // \ |---------4------------
736  //
737  // ^x
738  // z|\ |
739  // \ |
740  // \-->y
741  //
742 
743  size_t cellIdx = 12*(z + y*(binCount[2]+1) + x*(binCount[2]+1)*(binCount[1]+1)) ;
744 
745  cellIdx+=index;
746  return cellIdx;
747 
748 }
749 
750 template<class T>
751 void Voxels<T>::getEdgeCell(size_t edgeUniqId, size_t &x,size_t &y, size_t &z, size_t &axis ) const
752 {
753  //Invert the mapping generated by the edgeUniqId function
754  //to retrieve the XYZ and axis values
755  //--
756  axis=(edgeUniqId%12)/4;
757 
758  size_t tmp = edgeUniqId/12;
759  x = tmp/((binCount[2]+1)*(binCount[1]+1));
760  tmp-=x*((binCount[2]+1)*(binCount[1]+1));
761 
762  y=tmp/(binCount[2]+1);
763  tmp-=y*(binCount[2]+1);
764 
765  z=tmp;
766  //--
767 
768  ASSERT(x< binCount[0]+1 && y<binCount[1]+1 && z<binCount[2]+1);
769 }
770 template<class T>
771 void Voxels<T>::getEdgeEnds(size_t edgeUniqId, AtomProbe::Point3D &a, AtomProbe::Point3D &b ) const
772 {
773  size_t x,y,z;
774  size_t axis;
775  getEdgeCell(edgeUniqId,x,y,z,axis);
776 
778  AtomProbe::Point3D cellCentre=getPoint(x,y,z);
779 
780  //Generate ends of edge, as seen in ascii diagram in uniqueID
781  switch(axis)
782  {
783  case 0:
784  //|| x
785  a=cellCentre;
786  b=cellCentre + AtomProbe::Point3D(delta[0],0,0);
787  break;
788 
789  case 1:
790  //|| y
791  a=cellCentre;
792  b=cellCentre + AtomProbe::Point3D(0,delta[1],0);
793  break;
794  case 2:
795  //|| z
796  a=cellCentre;
797  b=cellCentre + AtomProbe::Point3D(0,0,delta[2]);
798  break;
799  default:
800  ASSERT(false);
801  }
802 
803 
804 #ifdef DEBUG
805  BoundCube bc;
807  bc.expand(sqrtf(std::numeric_limits<float>::epsilon()));
808  ASSERT(bc.containsPt(a) && bc.containsPt(b));
809 #endif
810 }
811 
812 template<class T>
813 void Voxels<T>::getEdgeEndApproxVals(size_t edgeUniqId, T &a, T &b ) const
814 {
815 
816  //TODO: Speed me up? Could not use
817  // other routines to do this access.
818  // I think some redundant calculations are done
819  AtomProbe::Point3D ptA,ptB;
820  getEdgeEnds(edgeUniqId,ptA,ptB);
821  size_t x,y,z;
822  getIndex(x,y,z,ptA);
823  a = getPointData(ptA);
824  b=getPointData(ptB);
825 }
826 
827 template<class T>
828 void Voxels<T>::getAxisBounds(size_t axis, float &minV, float &maxV ) const
829 {
830  maxV=maxBound[axis];
831  minV=minBound[axis];
832 }
833 
834 template<class T>
835 size_t Voxels<T>::resize(size_t x, size_t y, size_t z, const AtomProbe::Point3D &newMinBound, const AtomProbe::Point3D &newMaxBound)
836 {
837  binCount[0] = x;
838  binCount[1] = y;
839  binCount[2] = z;
840 
841 
842  minBound=newMinBound;
843  maxBound=newMaxBound;
844 
845  try
846  {
847  voxels.resize(binCount[0]*binCount[1]*binCount[2]);
848  }
849  catch(std::exception &e)
850  {
851  return 1;
852  }
853 
854  return 0;
855 }
856 
857 template<class T>
858 size_t Voxels<T>::resize(const Voxels<T> &oth)
859 {
860  return resize(oth.binCount[0],oth.binCount[1],oth.binCount[2],oth.minBound,oth.maxBound);
861 }
862 
863 template<class T>
864 size_t Voxels<T>::resizeKeepData(size_t newX, size_t newY, size_t newZ,
865  unsigned int direction, const AtomProbe::Point3D &newMinBound, const AtomProbe::Point3D &newMaxBound, const T &fill,bool doFill)
866 {
867 
868  ASSERT(direction==CLIP_LOWER_SOUTH_WEST);
869 
870  Voxels<T> v;
871 
872  if(v.resize(newX,newY,newZ))
873  return 1;
874 
875  switch(direction)
876  {
878  {
879  minBound=newMinBound;
880  maxBound=newMaxBound;
881 
882  if(doFill)
883  {
884  size_t itStop[3];
885  itStop[0]=std::min(newX,binCount[0]);
886  itStop[1]=std::min(newY,binCount[1]);
887  itStop[2]=std::min(newZ,binCount[2]);
888 
889  size_t itMax[3];
890  itMax[0]=std::max(newX,binCount[0]);
891  itMax[1]=std::max(newY,binCount[1]);
892  itMax[2]=std::max(newZ,binCount[2]);
893  //Duplicate into new value, if currently inside bounding box
894  //This logic will be a bit slow, owing to repeated "if"ing, but
895  //it is easy to understand. Other logics would have many more
896  //corner cases
897 #pragma omp parallel for
898  for(size_t ui=0;ui<itMax[0];ui++)
899  {
900 
901  for(size_t uj=0;uj<itMax[1];uj++)
902  {
903  for(size_t uk=0;uk<itMax[2];uk++)
904  {
905  if(itStop[0]< binCount[0] &&
906  itStop[1]<binCount[1] && itStop[2] < binCount[2])
907  v.setData(ui,uj,uk,getData(ui,uj,uk));
908  else
909  v.setData(ui,uj,uk,fill);
910  }
911  }
912 
913  }
914  }
915  else
916  {
917  //Duplicate into new value, if currently inside bounding box
918 #pragma omp parallel for
919  for(size_t ui=0;ui<newX;ui++)
920  {
921 
922  for(size_t uj=0;uj<newY;uj++)
923  {
924  for(size_t uk=0;uk<newZ;uk++)
925  v.setData(ui,uj,uk,getData(ui,uj,uk));
926  }
927 
928  }
929 
930  }
931 
932 
933 
934  break;
935  }
936 
937  default:
938  //Not implemented
939  ASSERT(false);
940  }
941 
942  swap(v);
943  return 0;
944 }
945 
946 template<class T>
948 {
949  ASSERT(voxels.size());
950  return minBound;
951 }
952 
953 template<class T>
955 {
956  ASSERT(voxels.size());
957  return maxBound;
958 }
959 
960 template<class T>
962 {
963  ASSERT(voxels.size());
964 #ifdef DEBUG
965  for(auto ui=0;ui<3;ui++)
966  {
967  ASSERT(pMin[ui] <=pMax[ui]);
968  }
969 #endif
970 
971  minBound=pMin;
972  maxBound=pMax;
973 }
974 
975  template<class T>
977 {
978  setBounds(bc.min(),bc.max());
979 }
980 
981 template<class T>
982 size_t Voxels<T>::init(size_t nX, size_t nY,
983  size_t nZ, const BoundCube &bound)
984 {
985  binCount[0]=nX;
986  binCount[1]=nY;
987  binCount[2]=nZ;
988 
989  bound.getBounds(minBound, maxBound);
990 
991  voxels.resize(nX*nY*nZ);
992 
993  return 0;
994 }
995 
996 template<class T>
997 size_t Voxels<T>::init(size_t nX, size_t nY, size_t nZ)
998 
999 {
1000  AtomProbe::Point3D pMin(0,0,0), pMax(nX,nY,nZ);
1001 
1002  return init(nX,nY,nZ,AtomProbe::BoundCube(pMin,pMax));
1003 }
1004 
1005 template<class T>
1006 size_t Voxels<T>::loadFile(const char *cpFilename, size_t nX, size_t nY, size_t nZ , bool silent)
1007 {
1008  //Must be power of two (buffer size when loading files, in sizeof(T)s)
1009  const unsigned int ITEM_BUFFER_SIZE=65536;
1010 
1011  std::ifstream CFile(cpFilename,std::ios::binary);
1012 
1013  if(!CFile)
1014  return VOXELS_BAD_FILE_OPEN;
1015 
1016  CFile.seekg(0,std::ios::end);
1017 
1018 
1019  size_t fileSize = CFile.tellg();
1020  if(fileSize !=nX*nY*nZ*sizeof(T))
1021  return VOXELS_BAD_FILE_SIZE;
1022 
1023  resize(nX,nY,nZ,AtomProbe::Point3D(nX,nY,nZ));
1024 
1025  CFile.seekg(0,std::ios::beg);
1026 
1027  unsigned int curBufferSize=ITEM_BUFFER_SIZE*sizeof(T);
1028  unsigned char *buffer = new unsigned char[curBufferSize];
1029 
1030  //Shrink the buffer size by factors of 2
1031  //in the case of small datasets
1032  while(fileSize < curBufferSize)
1033  curBufferSize = curBufferSize >> 1;
1034 
1035 
1036  //Draw a progress bar
1037  if(!silent)
1038  {
1039  std::cerr << std::endl << "|";
1040  for(unsigned int ui=0; ui<100; ui++)
1041  std::cerr << ".";
1042  std::cerr << "| 100%" << std::endl << "|";
1043  }
1044 
1045  unsigned int lastFrac=0;
1046  unsigned int ui=0;
1047  unsigned int pts=0;
1048  do
1049  {
1050 
1051  //Still have data? Keep going
1052  while((size_t)CFile.tellg() <= fileSize-curBufferSize)
1053  {
1054  //Update progress bar
1055  if(!silent && ((unsigned int)(((float)CFile.tellg()*100.0f)/(float)fileSize) > lastFrac))
1056  {
1057  std::cerr << ".";
1058  pts++;
1059  lastFrac=(unsigned int)(((float)CFile.tellg()*100.0f)/(float)fileSize) ;
1060  }
1061 
1062  //Read a chunk from the file
1063  CFile.read((char *)buffer,curBufferSize);
1064 
1065  if(!CFile.good())
1066  return VOXELS_BAD_FILE_READ;
1067 
1068  //Place the chunk contents into ram
1069  for(size_t position=0; position<curBufferSize; position+=(sizeof(T)))
1070  voxels[ui++] = (*((T *)(buffer+position)));
1071  }
1072 
1073  //Halve the buffer size
1074  curBufferSize = curBufferSize >> 1 ;
1075 
1076  }while(curBufferSize> sizeof(T)); //This does a few extra loops. Not many
1077 
1078  delete[] buffer;
1079 
1080  //Fill out the progress bar
1081  if(!silent)
1082  {
1083  while(pts++ <100)
1084  std::cerr << ".";
1085 
1086  std::cerr << "| done" << std::endl;
1087  }
1088 
1089  return 0;
1090 }
1091 
1092 template<class T>
1093 size_t Voxels<T>::writeFile(const char *filename) const
1094 {
1095 
1096  ASSERT(voxels.size());
1097 
1098  std::ofstream file(filename, std::ios::binary);
1099 
1100  if(!file)
1101  return 1;
1102 
1103 
1104  for(size_t ui=0; ui<voxels.size(); ui++)
1105  {
1106  T v;
1107  v=voxels[ui];
1108  file.write((char *)&v,sizeof(T));
1109  if(!file.good())
1110  return 2;
1111  }
1112  return 0;
1113 }
1114 
1115 
1116 template<class T>
1117 T Voxels<T>::getSum(const T &initialValue) const
1118 {
1119  ASSERT(voxels.size());
1120  T tmp(initialValue);
1121  size_t n=voxels.size();
1122 #pragma omp parallel for reduction(+:tmp)
1123  for(size_t ui=0;ui<n;ui++)
1124  tmp+=voxels[ui];
1125 
1126  return tmp;
1127 }
1128 
1129 template<class T>
1131 {
1132  //Compute volume prefactor - volume of cube of each voxel
1133  //--
1134  float prefactor=1.0;
1135  for(size_t ui=0;ui<3;ui++)
1136  {
1137  prefactor*=(maxBound[ui]-
1138  minBound[ui])/
1139  (float)binCount[ui];
1140  }
1141 
1142  //--
1143 
1144 
1145  double accumulation(0.0);
1146  //Loop across dataset integrating along z direction
1147 #pragma omp parallel for reduction(+:accumulation)
1148  for(size_t ui=0;ui<voxels.size(); ui++)
1149  accumulation+=voxels[ui];
1150 
1151  return prefactor*accumulation;
1152 }
1153 
1154 
1155 template<class T>
1156 size_t Voxels<T>::count(const T &minIntensity) const
1157 {
1158  size_t bins;
1159  bins=binCount[0]*binCount[1]*binCount[2];
1160 
1161  size_t sum=0;
1162 #pragma omp parallel for reduction(+:sum)
1163  for(size_t ui=0;ui<bins; ui++)
1164  {
1165  if(voxels[ui]>=minIntensity)
1166  sum++;
1167  }
1168 
1169  return sum;
1170 }
1171 
1172 template<class T>
1174 {
1175  std::swap(binCount[0],other.binCount[0]);
1176  std::swap(binCount[1],other.binCount[1]);
1177  std::swap(binCount[2],other.binCount[2]);
1178 
1179  voxels.swap(other.voxels);
1180 
1181  std::swap(maxBound,other.maxBound);
1182  std::swap(minBound,other.minBound);
1183 }
1184 
1185 template<class T>
1186 T Voxels<T>::getData(size_t x, size_t y, size_t z) const
1187 {
1188  ASSERT(x < binCount[0] && y < binCount[1] && z < binCount[2]);
1189  return voxels[shape3(x,y,z)];
1190 }
1191 
1192 template<class T>
1193 void Voxels<T>::thresholdForPosition(std::vector<AtomProbe::Point3D> &p, const T &thresh, bool lowerEq) const
1194 {
1195  p.clear();
1196 
1197  if(lowerEq)
1198  {
1199 #pragma omp parallel for
1200  for(size_t ui=0;ui<binCount[0]; ui++)
1201  {
1202  for(size_t uj=0;uj<binCount[1]; uj++)
1203  {
1204  for(size_t uk=0;uk<binCount[2]; uk++)
1205  {
1206  if( getData(ui,uj,uk) < thresh)
1207  {
1208 #pragma omp critical
1209  p.push_back(getPoint(ui,uj,uk));
1210  }
1211 
1212  }
1213  }
1214  }
1215  }
1216  else
1217  {
1218 #pragma omp parallel for
1219  for(size_t ui=0;ui<binCount[0]; ui++)
1220  {
1221  for(size_t uj=0;uj<binCount[1]; uj++)
1222  {
1223  for(size_t uk=0;uk<binCount[2]; uk++)
1224  {
1225  if( getData(ui,uj,uk) > thresh)
1226  {
1227 #pragma omp critical
1228  p.push_back(getPoint(ui,uj,uk));
1229  }
1230 
1231  }
1232  }
1233  }
1234  }
1235 }
1236 
1237 template<class T>
1238 void Voxels<T>::threshold(const T &thresh, bool keepUpper, const T &newVal)
1239 {
1240 
1241  if(keepUpper)
1242  {
1243 #pragma omp parallel for
1244  for(size_t ui=0;ui<(size_t)binCount[0]; ui++)
1245  {
1246  for(size_t uj=0;uj<binCount[1]; uj++)
1247  {
1248  for(size_t uk=0;uk<binCount[2]; uk++)
1249  {
1250  if( getData(ui,uj,uk) < thresh)
1251  setData(ui,uj,uk,newVal);
1252  }
1253  }
1254  }
1255  }
1256  else
1257  {
1258 #pragma omp parallel for
1259  for(size_t ui=0;ui<(size_t)binCount[0]; ui++)
1260  {
1261  for(size_t uj=0;uj<binCount[1]; uj++)
1262  {
1263  for(size_t uk=0;uk<binCount[2]; uk++)
1264  {
1265  if( getData(ui,uj,uk) > thresh)
1266  setData(ui,uj,uk,newVal);
1267  }
1268  }
1269  }
1270  }
1271 }
1272 
1273 template<class T>
1274 void Voxels<T>::thresholdToBoolMask(const T &thresh, bool keepUpper, Voxels<bool> &result) const
1275 {
1276  result.resize(binCount[0],binCount[1],binCount[2],
1277  minBound,maxBound);
1278 
1279 #pragma omp parallel for
1280  for(size_t ui=0;ui<(size_t)binCount[0]; ui++)
1281  {
1282  for(size_t uj=0;uj<binCount[1]; uj++)
1283  {
1284  for(size_t uk=0;uk<binCount[2]; uk++)
1285  {
1286  bool res;
1287  res=(getData(ui,uj,uk) > thresh);
1288 
1289  result.setData(ui,uj,uk,XOR(keepUpper,res));
1290  }
1291  }
1292  }
1293 
1294 }
1295 
1296 template<class T>
1297 void Voxels<T>::applyMask(const Voxels<bool> &mask, const T&newVal,bool invert)
1298 {
1299  ASSERT(mask.size() == size());
1300  ASSERT(mask.getMinBounds()== minBound);
1301  ASSERT(mask.getMaxBounds() == maxBound);
1302 #ifdef DEBUG
1303  size_t v[3];
1304  mask.getSize(v[0],v[1],v[2]);
1305  for(auto i=0;i<3;i++)
1306  {
1307  ASSERT(v[i] == binCount[i]);
1308  }
1309 #endif
1310 
1311  if(invert)
1312  {
1313  for(auto i=0; i<voxels.size(); i++)
1314  {
1315  if(!mask.getData(i))
1316  voxels[i]=newVal;
1317  }
1318  }
1319  else
1320  {
1321  for(auto i=0; i<voxels.size(); i++)
1322  {
1323  if(mask.getData(i))
1324  voxels[i]=newVal;
1325  }
1326  }
1327 }
1328 
1329 template<class T>
1330 void Voxels<T>::binarise(Voxels<T> &result, const T &thresh,
1331  const T &onThresh, const T &offThresh) const
1332 {
1333 
1334  result.resize(binCount[0],binCount[1],
1335  binCount[2],minBound,maxBound);
1336 #pragma omp parallel for
1337  for(size_t ui=0;ui<(size_t)binCount[0]; ui++)
1338  {
1339  for(size_t uj=0;uj<binCount[1]; uj++)
1340  {
1341  for(size_t uk=0;uk<binCount[2]; uk++)
1342  {
1343  if( getData(ui,uj,uk) < thresh)
1344  result.setData(ui,uj,uk,offThresh);
1345  else
1346  {
1347  result.setData(ui,uj,uk,onThresh);
1348  }
1349  }
1350  }
1351  }
1352 }
1353 
1354 
1355 
1356 template<class T>
1358 {
1359  ASSERT(voxels.size());
1360  return *std::min_element(voxels.begin(),voxels.end());
1361 }
1362 
1363 template<class T>
1365 {
1366  ASSERT(voxels.size());
1367  return *std::max_element(voxels.begin(),voxels.end());
1368 }
1369 
1370 
1371 template<class T>
1372 void Voxels<T>::minMax(T &min, T&max) const
1373 {
1374  ASSERT(voxels.size());
1375  //FIXME: we can probably do this in one traverse, maybe quicker?
1376  // needs to be benchmarked.
1377  min=*std::min_element(voxels.begin(),voxels.end());
1378  max=*std::max_element(voxels.begin(),voxels.end());
1379 }
1380 
1381 template<class T>
1382 int Voxels<T>::countPoints( const std::vector<AtomProbe::Point3D> &points, bool noWrap, bool doErase)
1383 {
1384  if(doErase)
1385  {
1386  fill(0);
1387  }
1388 
1389  size_t x,y,z;
1390 
1391  for(size_t ui=0; ui<points.size(); ui++)
1392  {
1393 
1394  getIndex(x,y,z,points[ui]);
1395 
1396  //Ensure it lies within the dataset
1397  if(x < binCount[0] && y < binCount[1] && z< binCount[2])
1398  {
1399  {
1400  T value;
1401  value=getData(x,y,z)+T(1);
1402 
1403  //Prevent wrap-around errors
1404  if (noWrap) {
1405  if(value > getData(x,y,z))
1406  setData(x,y,z,value);
1407  } else {
1408  setData(x,y,z,value);
1409  }
1410  }
1411  }
1412  }
1413 
1414  return 0;
1415 }
1416 
1417 template<class T>
1418 int Voxels<T>::countIons( const std::vector<AtomProbe::IonHit> &points, bool noWrap, bool doErase)
1419 {
1420  if(doErase)
1421  {
1422  fill(0);
1423  }
1424 
1425  size_t x,y,z;
1426 
1427  if (noWrap)
1428  {
1429  for(size_t ui=0; ui<points.size(); ui++)
1430  {
1431 
1432  getIndex(x,y,z,points[ui].getPosRef());
1433 
1434  //Ensure it lies within the dataset
1435  if(x < binCount[0] && y < binCount[1] && z< binCount[2])
1436  {
1437  {
1438  T value;
1439  value=getData(x,y,z)+T(1);
1440 
1441  //Prevent wrap-around errors
1442  if(value > getData(x,y,z))
1443  setData(x,y,z,value);
1444  }
1445  }
1446  }
1447  }
1448  else
1449  {
1450  for(size_t ui=0; ui<points.size(); ui++)
1451  {
1452 
1453  getIndex(x,y,z,points[ui].getPosRef());
1454 
1455  //Ensure it lies within the dataset
1456  if(x < binCount[0] && y < binCount[1] && z< binCount[2])
1457  {
1458  T value;
1459  value=getData(x,y,z)+T(1);
1460  setData(x,y,z,value);
1461  }
1462  }
1463  }
1464 
1465  return 0;
1466 }
1467 
1468 template<class T>
1470 {
1471  AtomProbe::Point3D size = maxBound - minBound;
1472  // calculate the volume of a voxel
1473  double volume = 1.0;
1474  for (int i = 0; i < 3; i++)
1475  volume *= size[i] / binCount[i];
1476 
1477  // normalise the voxel value based on volume
1478 #pragma omp parallel for
1479  for(size_t ui=0; ui<voxels.size(); ui++)
1480  voxels[ui] /= volume;
1481 
1482 }
1483 
1484 template<class T>
1486 {
1487  AtomProbe::Point3D size = maxBound - minBound;
1488  double volume = 1.0;
1489  for (int i = 0; i < 3; i++)
1490  volume *= size[i] / binCount[i];
1491 
1492  return volume;
1493 }
1494 
1495 template<class T>
1496 void Voxels<T>::getIndex(size_t &x, size_t &y,
1497  size_t &z, const AtomProbe::Point3D &p) const
1498 {
1499 
1500  ASSERT(p[0] >=minBound[0] && p[1] >=minBound[1] && p[2] >=minBound[2] &&
1501  p[0] <=maxBound[0] && p[1] <=maxBound[1] && p[2] <=maxBound[2]);
1502  x=(size_t)((p[0]-minBound[0])/(maxBound[0]-minBound[0])*(float)binCount[0]);
1503  y=(size_t)((p[1]-minBound[1])/(maxBound[1]-minBound[1])*(float)binCount[1]);
1504  z=(size_t)((p[2]-minBound[2])/(maxBound[2]-minBound[2])*(float)binCount[2]);
1505 
1506  if(x == binCount[0])
1507  x--;
1508  if(y == binCount[1])
1509  y--;
1510  if(z == binCount[2])
1511  z--;
1512 }
1513 
1514 template<class T>
1515 void Voxels<T>::getIndexWithUpper(size_t &x, size_t &y,
1516  size_t &z, const AtomProbe::Point3D &p) const
1517 {
1518  //Get the data index as per normal
1519  getIndex(x,y,z,p);
1520 
1521  //but, as a special case, if the index is the same as our bincount, check
1522  //to see if it is positioned on an edge
1523  if(x==binCount[0] &&
1524  fabs(p[0] -maxBound[0]) < sqrtf(std::numeric_limits<float>::epsilon()))
1525  x--;
1526  if(y==binCount[1] &&
1527  fabs(p[1] -maxBound[1]) < sqrtf(std::numeric_limits<float>::epsilon()))
1528  y--;
1529  if(z==binCount[2] &&
1530  fabs(p[2] -maxBound[2]) < sqrtf(std::numeric_limits<float>::epsilon()))
1531  z--;
1532 
1533 }
1534 
1535 template<class T>
1536 void Voxels<T>::fill(const T &v)
1537 {
1538  std::fill(voxels.begin(),voxels.end(),v);
1539 }
1540 
1541 //Obtain a slice of the voxel data. Data output will be in column order
1542 // p[posB*nA + posA]. Input slice must be sufficiently sized and allocated
1543 // to hold the output data
1544 template<class T>
1545 void Voxels<T>::getSlice(size_t normalAxis, size_t offset, T *p) const
1546 {
1547  ASSERT(normalAxis < 3);
1548 
1549  size_t dimA,dimB,nA;
1550  switch(normalAxis)
1551  {
1552  case 0:
1553  {
1554  dimA=1;
1555  dimB=2;
1556  nA=binCount[dimA];
1557  break;
1558  }
1559  case 1:
1560  {
1561  dimA=0;
1562  dimB=2;
1563  nA=binCount[dimA];
1564  break;
1565  }
1566  case 2:
1567  {
1568  dimA=0;
1569  dimB=1;
1570  nA=binCount[dimA];
1571  break;
1572  }
1573  default:
1574  ASSERT(false); //WTF - how did you get here??
1575  }
1576 
1577 
1578  //We are within bounds, use normal access functions
1579  switch(normalAxis)
1580  {
1581  case 0:
1582  {
1583  for(size_t ui=0;ui<binCount[dimA];ui++)
1584  {
1585  for(size_t uj=0;uj<binCount[dimB];uj++)
1586  p[uj*nA + ui] = getData(offset,ui,uj);
1587  }
1588  break;
1589  }
1590  case 1:
1591  {
1592  for(size_t ui=0;ui<binCount[dimA];ui++)
1593  {
1594  for(size_t uj=0;uj<binCount[dimB];uj++)
1595  p[uj*nA + ui] = getData(ui,offset,uj);
1596  }
1597  break;
1598  }
1599  case 2:
1600  {
1601  for(size_t ui=0;ui<binCount[dimA];ui++)
1602  {
1603  for(size_t uj=0;uj<binCount[dimB];uj++)
1604  p[uj*nA + ui] = getData(ui,uj,offset);
1605  }
1606  break;
1607  }
1608  default:
1609  ASSERT(false);
1610  }
1611 }
1612 
1613 template<class T>
1614 void Voxels<T>::getInterpSlice(size_t normal, float offset,
1615  T *p, size_t interpMode) const
1616 {
1617  ASSERT(offset <=1.0f && offset >=0.0f);
1618 
1619  //Obtain the appropriately interpolated slice
1620  switch(interpMode)
1621  {
1622  case VOX_INTERP_NONE:
1623  {
1624  size_t slicePos;
1625  slicePos=roundf(offset*binCount[normal]);
1626  slicePos=std::min(slicePos,binCount[normal]-1);
1627  getSlice(normal,slicePos,p);
1628  break;
1629  }
1630  case VOX_INTERP_LINEAR:
1631  {
1632  //Find the upper and lower bounds, then
1633  // limit them so we don't fall off the end of the dataset
1634  size_t sliceUpper,sliceLower;
1635  if(binCount[0] == 1)
1636  sliceUpper=sliceLower=0;
1637  else
1638  {
1639  sliceUpper=ceilf(offset*binCount[normal]);
1640 
1641  if(sliceUpper >=binCount[normal])
1642  sliceUpper=binCount[normal]-1;
1643  else if(sliceUpper==0)
1644  sliceUpper=1;
1645 
1646  sliceLower=sliceUpper-1;
1647  }
1648 
1649  {
1650  T *pLower;
1651  size_t numEntries=binCount[(normal+1)%3]*binCount[(normal+2)%3];
1652 
1653  pLower = new T[numEntries];
1654 
1655  getSlice(normal,sliceLower,pLower);
1656  getSlice(normal,sliceUpper,p);
1657 
1658  //Get the decimal part of the float
1659  float integ;
1660  float delta=modff(offset*binCount[normal],&integ);
1661  for(size_t ui=0;ui<numEntries;ui++)
1662  p[ui] = delta*(p[ui]-pLower[ui]) + pLower[ui];
1663 
1664  delete[] pLower;
1665  }
1666  break;
1667  }
1668  default:
1669  ASSERT(false);
1670 
1671  }
1672 
1673 }
1674 
1675 //FIXME: I think this has a slight shift as we are moving the data voxels
1676 // definition of the voxel centre by 1/2 a pitch, I think
1677 template<class T>
1679 
1680 {
1681 #ifdef DEBUG
1682  BoundCube bc(minBound,maxBound);
1683  ASSERT(bc.containsPt(p));
1684 #endif
1685 
1686  size_t index[3];
1687  getIndex(index[0],index[1],index[2],p);
1688 
1689  AtomProbe::Point3D pitch =getPitch();
1690 
1691  //Find the offset to the voxel that we are in.
1692  //fraction should be in range [0,1)
1693  AtomProbe::Point3D fraction = p - (minBound + AtomProbe::Point3D(index[0],index[1],index[2])*pitch);
1694  fraction =fraction/pitch;
1695 
1696 
1697  size_t iPlus[3];
1698  //0.5 corresponds to voxel centre.
1699  for(unsigned int ui=0;ui<3;ui++)
1700  {
1701  if(index[ui] == (binCount[ui]-1))
1702  iPlus[ui]=0;
1703  else
1704  iPlus[ui]=1;
1705  }
1706 
1707 
1708  float c[2][2];
1709  //Tri-linear interpolation
1710 
1711  //interpolate data values at cube vertices that surround point. We are coming from below the point
1712  // so we are simply extending the field on the upper edge by duplicating values as needed
1713 
1714  float xf = 1-fraction[0];
1715 
1716  size_t xLow,xHigh;
1717  xLow = index[0];
1718  xHigh = index[0] + iPlus[0];
1719  c[0][0] = getData(xLow,index[1],index[2])*xf + getData(xHigh,index[1],index[2])*fraction[0] ;
1720  c[0][1] = getData(xLow,index[1],index[2]+iPlus[2])*xf + getData(xHigh,index[1],index[2]+iPlus[2])*fraction[0] ;
1721  c[1][0] = getData(xLow,index[1]+iPlus[1],index[2])*xf + getData(xHigh,index[1]+iPlus[1],index[2])*fraction[0] ;
1722  c[1][1] = getData(xLow,index[1]+iPlus[1],index[2]+iPlus[2])*xf + getData(xHigh,index[1]+iPlus[1],index[2]+iPlus[2])*fraction[0] ;
1723 
1724 
1725  float c0,c1;
1726  c0 = c[0][0]*(1-fraction[1]) + c[1][0]*fraction[1];
1727  c1 = c[0][1]*(1-fraction[1]) + c[1][1]*fraction[1];
1728 
1729  v= c0*(1-fraction[2])+c1*fraction[2];
1730 
1731 }
1732 
1733 
1734 
1735 template<class T>
1737 {
1738  ASSERT(v.voxels.size() == voxels.size());
1739 
1740  //don't use the built-in /, this
1741  // can generate inf values (which is correct)
1742  // that we want to avoid.
1743  for(size_t ui=0;ui<voxels.size();ui++)
1744  {
1745  if(v.voxels[ui] )
1746  voxels[ui]/=v.voxels[ui];
1747  else
1748  {
1749  ASSERT(!voxels[ui]);
1750  }
1751  }
1752 }
1753 
1754 
1755 template<class T>
1756 void Voxels<T>::operator/=(const T &v)
1757 {
1758  //don't use the built-in /, this
1759  // can generate inf values (which is correct)
1760  // that we want to avoid.
1761  for(size_t ui=0;ui<voxels.size();ui++)
1762  {
1763  if( v > T(0) )
1764  voxels[ui]/=v;
1765  else
1766  {
1767  voxels[ui]=0;
1768  }
1769  }
1770 }
1771 
1772 template<class T>
1773 bool Voxels<T>::operator==(const Voxels<T> &v) const
1774 {
1775  for(size_t ui=0;ui<3;ui++)
1776  {
1777 
1778  if(v.binCount[ui] != binCount[ui])
1779  return false;
1780  }
1781 
1782  return v.voxels == voxels;
1783 }
1784 
1785 #ifdef DEBUG
1786 bool runVoxelTests();
1787 #endif
1788 
1789 //===
1790 }
1791 
1792 #endif
T min() const
Find minimum in dataset.
Definition: voxels.h:1357
static size_t sizeofType()
Return the sizeof value for the T type.
Definition: voxels.h:300
void operator/=(const Voxels< T > &v)
Element wise division.
Definition: voxels.h:1736
void binarise(Voxels< T > &result, const T &thresh, const T &onThresh, const T &offThresh) const
Binarise the data into a result vector.
Definition: voxels.h:1330
bool containsPt(const Point3D &pt) const
Check to see if the point is contained in, or part of the walls of the cube.
Definition: boundcube.cpp:367
size_t count(const T &minIntensity) const
count the number of cells with at least this intensity
Definition: voxels.h:1156
void applyMask(const Voxels< bool > &mask, const T &newVal, bool invert=false)
Apply a mask to this dataset, where the mask is true (if not inverting), replace with specified val (...
Definition: voxels.h:1297
T getSum(const T &initialVal=T(0.0)) const
Get the total value of the data field.
Definition: voxels.h:1117
void castVoxels(const Voxels< T > &src, Voxels< U > &dest)
Convert one type of voxel into another by assignment operator.
Definition: voxels.h:368
AtomProbe::Point3D getMaxBounds() const
Definition: voxels.h:954
size_t resize(size_t newX, size_t newY, size_t newZ, const AtomProbe::Point3D &newMinBound=AtomProbe::Point3D(0.0f, 0.0f, 0.0f), const AtomProbe::Point3D &newMaxBound=AtomProbe::Point3D(1.0f, 1.0f, 1.0f))
Resize the data field.
Definition: voxels.h:835
size_t deprecatedGetEdgeUniqueIndex(size_t x, size_t y, size_t z, unsigned int edge) const
DEPRECATED FUNCTION : Get a unique integer that corresponds to the edge index for the voxel; where ed...
Definition: voxels.h:497
void getEdgeEndApproxVals(size_t edgeUniqId, T &a, T &b) const
Return the values that are associated with the edge ends, as returned by getEdgeEnds.
Definition: voxels.h:813
size_t resizeKeepData(size_t newX, size_t newY, size_t newZ, unsigned int direction=CLIP_LOWER_SOUTH_WEST, const AtomProbe::Point3D &newMinBound=AtomProbe::Point3D(0.0f, 0.0f, 0.0f), const AtomProbe::Point3D &newMaxBound=AtomProbe::Point3D(1.0f, 1.0f, 1.0f), const T &fill=T(0), bool doFill=false)
Resize the data field, maintaining data as best as possible.
Definition: voxels.h:864
int countIons(const std::vector< AtomProbe::IonHit > &ions, bool noWrap=true, bool doErase=true)
Generate a dataset that consists of the counts of points in a given voxel.
Definition: voxels.h:1418
Voxels()
Constructor.
Definition: voxels.h:392
void getBounds(AtomProbe::Point3D &pMin, AtomProbe::Point3D &pMax) const
Get the bounding size.
Definition: voxels.h:252
float getBinVolume() const
Definition: voxels.h:1485
void thresholdToBoolMask(const T &thresh, bool keepUpper, Voxels< bool > &result) const
Generate a boolean voxel field stating whether the data is above or below the threshold specified...
Definition: voxels.h:1274
void swap(Voxels< T > &v)
Swap object contents with other voxel object.
Definition: voxels.h:1173
AtomProbe::Point3D getMinBounds() const
Get the bounding box vertex (min/max)
Definition: voxels.h:947
T trapezIntegral() const
Integrate the datataset via the trapezoidal method.
Definition: voxels.h:1130
~Voxels()
Destructor.
Definition: voxels.h:397
void sumVoxels(const Voxels< T > &src, U &counter)
Use one counting type to sum counts in a voxel of given type.
Definition: voxels.h:376
void getBounds(BoundCube &bc) const
Definition: voxels.h:253
void getBounds(Point3D &low, Point3D &high) const
Get the bounds of the cube as two points, the lower left (minimum bound) and upper right corner (max ...
Definition: boundcube.cpp:302
Template class that stores 3D voxel data.
Definition: voxels.h:106
void calculateDensity()
Convert voxel intensity into voxel density.
Definition: voxels.h:1469
T getData(size_t i) const
Retrieve value of the nth voxel.
Definition: voxels.h:156
size_t init(size_t nX, size_t nY, size_t nZ, const BoundCube &bound)
Initialise the voxel storage.
Definition: voxels.h:982
void fill(const T &val)
Fill all voxels with a given value.
Definition: voxels.h:1536
T getPointData(const AtomProbe::Point3D &pt) const
Retrieve the value of a datapoint, this is rounded to the nearest voxel.
Definition: voxels.h:454
void threshold(const T &thresh, bool keepUpper, const T &newVal)
Threshold the voxels, keeping either the lower or upper values above this threshold.
Definition: voxels.h:1238
T getData(size_t x, size_t y, size_t z) const
Retrieve the value of a specific voxel.
Definition: voxels.h:1186
Point3D max() const
Definition: boundcube.cpp:324
A 3D point data class storage.
Definition: point3D.h:39
void minMax(T &min, T &max) const
Find both min and max in dataset in the same loop.
Definition: voxels.h:1372
AtomProbe::Point3D getPoint(size_t x, size_t y, size_t z) const
Get the position associated with an XYZ voxel.
Definition: voxels.h:470
void getEdgeEnds(size_t edgeIndex, AtomProbe::Point3D &a, AtomProbe::Point3D &b) const
Convert the edge index (as generated by getEdgeUniqueIndex) into a cenre position.
Definition: voxels.h:771
void setEntry(size_t n, const T &val)
Definition: voxels.h:158
size_t loadFile(const char *cpFilename, size_t nX, size_t nY, size_t nZ, bool silent=false)
Load the voxels from file.
Definition: voxels.h:1006
void setBounds(const AtomProbe::Point3D &pMin, const AtomProbe::Point3D &pMax)
Set the bounding size.
Definition: voxels.h:961
void setPoint(const AtomProbe::Point3D &pt, const T &val)
Set the value of a point in the dataset.
Definition: voxels.h:423
void getInterpSlice(size_t normal, float offset, T *p, size_t interpMode=VOX_INTERP_NONE) const
Definition: voxels.h:1614
void getIndex(size_t &x, size_t &y, size_t &z, const AtomProbe::Point3D &p) const
Retrieve the XYZ voxel location associated with a given position.
Definition: voxels.h:1496
Helper class to define a bounding cube.
Definition: boundcube.h:29
void copy(Voxels< T > &newCopy) const
Clone data into another object.
Definition: voxels.h:409
size_t getCellUniqueEdgeIndex(size_t x, size_t y, size_t z, unsigned int edge) const
Get a unique integer that corresponds to an edge index for the voxel; where edges are shared between ...
Definition: voxels.h:701
AtomProbe::Point3D getPitch() const
! Get the spacing for a unit cell
Definition: voxels.h:480
void setData(size_t x, size_t y, size_t z, const T &val)
Retrieve a reference to the data ata given position.
Definition: voxels.h:435
#define ASSERT(f)
T max() const
Find maximum in dataset.
Definition: voxels.h:1364
Point3D min() const
Definition: boundcube.cpp:312
bool operator==(const Voxels< T > &v) const
Definition: voxels.h:1773
void getSlice(size_t normal, size_t offset, T *p) const
Definition: voxels.h:1545
int countPoints(const std::vector< AtomProbe::Point3D > &points, bool noWrap=true, bool doErase=true)
Generate a dataset that consists of the counts of points in a given voxel.
Definition: voxels.h:1382
void getSize(size_t &x, size_t &y, size_t &z) const
Get the size of the data field.
Definition: voxels.h:488
#define XOR(a, b)
Definition: voxels.h:39
void getEdgeCell(size_t edgeUniqId, size_t &x, size_t &y, size_t &z, size_t &axis) const
Convert edge index (only as generted by getCellUniqueEdgeIndex) into a cell & axis value...
Definition: voxels.h:751
size_t size() const
Definition: voxels.h:361
void clear()
Empty the data.
Definition: voxels.h:314
void getInterpolatedData(const AtomProbe::Point3D &pt, T &v) const
Definition: voxels.h:1678
size_t writeFile(const char *cpFilename) const
Write the voxel objects in column major written out to file.
Definition: voxels.h:1093
void getIndexWithUpper(size_t &x, size_t &y, size_t &z, const AtomProbe::Point3D &p) const
Retrieve the XYZ voxel location associated with a given position,.
Definition: voxels.h:1515
void thresholdForPosition(std::vector< AtomProbe::Point3D > &p, const T &thresh, bool lowerEq=false) const
Find the positions of the voxels that are above or below a given value.
Definition: voxels.h:1193
void setBounds(float xMin, float yMin, float zMin, float xMax, float yMax, float zMax)
Set the bounds by passing in minima and maxima of each dimension.
Definition: boundcube.cpp:49
void expand(const BoundCube &b)
Expand (as needed) volume such that the argument bounding cube is enclosed by this one...
Definition: boundcube.cpp:148
void getAxisBounds(size_t axis, float &minV, float &maxV) const
Definition: voxels.h:828