libatomprobe
Library for Atom Probe Tomography (APT) computation
K3DTree-exact.cpp
Go to the documentation of this file.
1 /*
2 * K3DTree-exact.cpp - Precise KD tree implementation
3 * Copyright (C) 2014 Daniel 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 
20 
21 #include <stack>
22 #include <queue>
23 #include <algorithm>
24 #include <cmath>
25 #include <utility>
26 #include <limits>
27 
28 using namespace AtomProbe;
29 
31 
32 
33 using std::stack;
34 using std::vector;
35 using std::pair;
36 using std::queue;
37 
38 const int PROGRESS_REDUCE=5000;
39 
41 
46 {
47  private:
48  unsigned int axis;
49  public:
50  void setAxis(unsigned int Axis){axis=Axis;};
51  inline bool operator()(const std::pair<Point3D,size_t> &p1,const std::pair<Point3D,size_t> &p2) const
52  {return p1.first[axis]<p2.first[axis];};
53 };
54 
55 class NodeWalk
56 {
57  public:
58  size_t index;
60  unsigned int depth;
61  NodeWalk(unsigned int idx,const BoundCube &bc, unsigned int dpth) :
62  index(idx), cube(bc), depth(dpth) {};
63 };
64 
66 {
67 }
68 
70 {
71  ASSERT(treeBounds.isValid());
72  b.setBounds(treeBounds);
73 }
74 
75 const Point3D *K3DTreeExact::getPt(size_t index) const
76 {
77  ASSERT(index < indexedPoints.size());
78  return &(indexedPoints[index].first);
79 }
80 
81 const Point3D &K3DTreeExact::getPtRef(size_t index) const
82 {
83  ASSERT(index < indexedPoints.size());
84  return (indexedPoints[index].first);
85 }
86 
87 size_t K3DTreeExact::getOrigIndex(size_t treeIndex) const
88 {
89  ASSERT(treeIndex <indexedPoints.size());
90  return indexedPoints[treeIndex].second;
91 }
92 
93 bool K3DTreeExact::getTag(size_t treeIndex) const
94 {
95  ASSERT(treeIndex < nodes.size());
96  return nodes[treeIndex].tagged;
97 }
98 
99 void K3DTreeExact::tag(size_t treeIndex,bool tagVal)
100 {
101  ASSERT(treeIndex < nodes.size());
102  nodes[treeIndex].tagged=tagVal;
103 }
104 
105 void K3DTreeExact::tag(const std::vector<size_t> &treeIndicies,bool tagVal)
106 {
107 for(unsigned int ui=0;ui<treeIndicies.size();ui++)
108 {
109  ASSERT(treeIndicies[ui] < nodes.size());
110  nodes[treeIndicies[ui]].tagged=tagVal;
111 }
112 }
113 size_t K3DTreeExact::size() const
114 {
115  ASSERT(nodes.size() == indexedPoints.size());
116  return indexedPoints.size();
117 }
118 
119 void K3DTreeExact::resetPts(std::vector<Point3D> &p, bool clear)
120 {
121  //Compute bounding box for indexedPoints
122  treeBounds.setBounds(p);
123  indexedPoints.resize(p.size());
124 
125  #pragma omp parallel for
126  for(size_t ui=0;ui<indexedPoints.size();ui++)
127  {
128  indexedPoints[ui].first=p[ui];
129  indexedPoints[ui].second=ui;
130  }
131 
132  if(clear)
133  p.clear();
134 
135  nodes.resize(indexedPoints.size());
136 }
137 
138 void K3DTreeExact::resetPts(std::vector<IonHit> &p, bool clear)
139 {
140  indexedPoints.resize(p.size());
141  nodes.resize(p.size());
142 
143  if(p.empty())
144  return;
145 
146 
147  //Compute bounding box for indexedPoints
148  treeBounds=IonHit::getIonDataLimits(p);
149 
150  #pragma omp parallel for
151  for(size_t ui=0;ui<indexedPoints.size();ui++)
152  {
153  indexedPoints[ui].first=p[ui].getPosRef();
154  indexedPoints[ui].second=ui;
155  }
156 
157  if(clear)
158  p.clear();
159 
160 }
161 
163 {
164 
165  using std::make_pair;
166 
167  enum
168  {
169  BUILT_NONE,
170  BUILT_LEFT,
171  BUILT_BOTH
172  };
173 
174  ASSERT(callback());
175  ASSERT(progress);
176 
177  //Clear any existing tags
178  clearAllTags();
179  maxDepth=0;
180 
181  //No indexedPoints? That was easy.
182  if(indexedPoints.empty())
183  return true;
184 
185 
186  ASSERT(treeBounds.isValid());
187 
188  //Maintain a stack of nodeoffsets, and whether we have built the left hand side
189  stack<pair<size_t,size_t> > limits;
190  stack<char> buildStatus;
191  stack<size_t> splitStack;
192 
193  //Data runs from 0 to size-1 INCLUSIVE
194  limits.push(make_pair(0,indexedPoints.size()-1));
195  buildStatus.push(BUILT_NONE);
196  splitStack.push((size_t)-1);
197 
198 
199  AxisCompareExact axisCmp;
200 
201  size_t numSeen=0; // for progress reporting
202  size_t splitIndex=0;
203 
204 
205 
206 
207  #ifdef DEBUG
208  for(size_t ui=0;ui<nodes.size();ui++)
209  {
210  nodes[ui].childLeft=nodes[ui].childRight=(size_t)-2;
211  }
212  #endif
213 
214  size_t *childPtr=0;
215  do
216  {
217 
218  switch(buildStatus.top())
219  {
220  case BUILT_NONE:
221  {
222  //OK, so we have not seen this data at this level before
223  int curAxis=(limits.size()-1)%3;
224  //First time we have seen this group? OK, we need to sort
225  //along its hyper plane.
226  axisCmp.setAxis(curAxis);
227 
228  //Sort data; note that the limits.top().second is the INCLUSIVE
229  // upper end.
230  std::sort(indexedPoints.begin()+limits.top().first,
231  indexedPoints.begin() + limits.top().second+1,axisCmp);
232 
233  //Initially assume that the mid node is the median; then we slide it up
234  splitIndex=(limits.top().second+limits.top().first)/2;
235 
236  //Keep sliding the split towards the upper boundary until we hit a different
237  //data value. This ensure that all data on the left of the sub-tree is <=
238  // to this data value for the specified sort axis
239  while(splitIndex != limits.top().second
240  && indexedPoints[splitIndex].first.getValue(curAxis) ==
241  indexedPoints[splitIndex+1].first.getValue(curAxis))
242  splitIndex++;
243 
244  buildStatus.top()++; //Increment the build status to "left" case.
245 
246 
247  if(limits.size() ==1)
248  {
249  //root node
250  treeRoot=splitIndex;
251  }
252  else
253  *childPtr=splitIndex;
254 
255  //look to see if there is any left data
256  if(splitIndex >limits.top().first)
257  {
258  //There is; we have to branch again
259  limits.push(make_pair(
260  limits.top().first,splitIndex-1));
261 
262  buildStatus.push(BUILT_NONE);
263  //Set the child pointer, as we don't know
264  //the correct value until the next sort.
265  childPtr=&nodes[splitIndex].childLeft;
266  }
267  else
268  {
269  //There is not. Set the left branch to null
270  nodes[splitIndex].childLeft=(size_t)-1;
271  }
272  splitStack.push(splitIndex);
273 
274  break;
275  }
276 
277  case BUILT_LEFT:
278  {
279  //Either of these cases results in use
280  //handling the right branch.
281  buildStatus.top()++;
282  splitIndex=splitStack.top();
283  //Check to see if there is any right data
284  if(splitIndex <limits.top().second)
285  {
286  //There is; we have to branch again
287  limits.push(make_pair(
288  splitIndex+1,limits.top().second));
289  buildStatus.push(BUILT_NONE);
290 
291  //Set the child pointer, as we don't know
292  //the correct value until the next sort.
293  childPtr=&nodes[splitIndex].childRight;
294  }
295  else
296  {
297  //There is not. Set the right branch to null
298  nodes[splitIndex].childRight=(size_t)-1;
299  }
300 
301  break;
302  }
303  case BUILT_BOTH:
304  {
305  ASSERT(nodes[splitStack.top()].childLeft != (size_t)-2
306  && nodes[splitStack.top()].childRight!= (size_t)-2 );
307  maxDepth=std::max(maxDepth,limits.size());
308  //pop limits and build status.
309  limits.pop();
310  buildStatus.pop();
311  splitStack.pop();
312  ASSERT(limits.size() == buildStatus.size());
313 
314 
315  numSeen++;
316  break;
317  }
318  }
319 
320  if(!(numSeen%PROGRESS_REDUCE) && progress)
321  {
322  *progress= (unsigned int)((float)numSeen/(float)nodes.size()*100.0f);
323 
324  if(!(*callback)())
325  return false;
326  }
327 
328  }while(!limits.empty());
329 
330  return true;
331 }
332 
333 void K3DTreeExact::dump(std::ostream &strm, size_t depth, size_t offset) const
334 {
335  if(offset==(size_t)-1)
336  {
337  for(unsigned int ui=0;ui<indexedPoints.size();ui++)
338  {
339  strm << ui << " "<< indexedPoints[ui].first << std::endl;
340  }
341 
342  strm << "----------------" << std::endl;
343  offset=treeRoot;
344  }
345 
346  for(size_t ui=0;ui<depth; ui++)
347  strm << "\t";
348 
349  strm << offset << " : (" << indexedPoints[offset].first[0]
350  << "," << indexedPoints[offset].first[1] << "," << indexedPoints[offset].first[2]
351  << ")" << std::endl;
352 
353 
354 
355  for(size_t ui=0;ui<depth; ui++)
356  strm << "\t";
357  strm << "<l>" <<std::endl;
358 
359  if(nodes[offset].childLeft!=(size_t)-1)
360  {
361  dump(strm,depth+1,nodes[offset].childLeft);
362  }
363  for(size_t ui=0;ui<depth; ui++)
364  strm << "\t";
365  strm << "</l>" <<std::endl;
366 
367  for(size_t ui=0;ui<depth; ui++)
368  strm << "\t";
369  strm << "<r>" <<std::endl;
370 
371  if(nodes[offset].childRight!=(size_t)-1)
372  dump(strm,depth+1,nodes[offset].childRight);
373 
374  for(size_t ui=0;ui<depth; ui++)
375  strm << "\t";
376  strm << "</r>" <<std::endl;
377 }
378 
379 void K3DTreeExact::ptsInSphere(const Point3D &origin, float radius,
380  vector<size_t> &pts) const
381 {
382  if(!treeBounds.intersects(origin,radius))
383  return;
384 
385  //parent walking queue. This contains initial parent indices that
386  // lie within the sphere.
387 
388 
389 
390  const float sqrRadius=radius*radius;
391  //contains all completely contained points (these points and children are in sphere)
392  std::queue<size_t> idxQueue;
393  //queue of points whose children are partly in the sphere
394  std::queue<NodeWalk> nodeQueue;
395  nodeQueue.push(NodeWalk(treeRoot,treeBounds,0));
396 
397  while(!nodeQueue.empty())
398  {
399  size_t nodeIdx;
400  BoundCube curCube;
401  unsigned int depth,axis;
402 
403  nodeIdx = nodeQueue.front().index;
404  curCube=nodeQueue.front().cube;
405  depth = nodeQueue.front().depth;
406  axis=depth %3;
407  nodeQueue.pop() ;
408 
409 
410  //obtain the left and right cubes, and see if they
411  // -exist
412  // -intersect the spehre
413  // - if intersects, are they contained entirely by the sphere
414  BoundCube leftCube,rightCube;
415  if(nodes[nodeIdx].childLeft != (size_t) -1)
416  {
417  leftCube=curCube;
418  leftCube.bounds[axis][1]=indexedPoints[nodeIdx].first[axis];
419  if(leftCube.intersects(origin,sqrRadius) )
420  {
421  if(leftCube.containedInSphere(origin,radius))
422  {
423  ASSERT(indexedPoints[nodeIdx].first.sqrDist(origin) < radius*radius);
424  idxQueue.push(nodes[nodeIdx].childLeft);
425  }
426  else
427  nodeQueue.push(NodeWalk(nodes[nodeIdx].childLeft,leftCube,depth+1));
428  }
429  }
430 
431  if(nodes[nodeIdx].childRight != (size_t) -1)
432  {
433  rightCube=curCube;
434  rightCube.bounds[axis][0]=indexedPoints[nodeIdx].first[axis];
435  if(rightCube.intersects(origin,sqrRadius) )
436  {
437  if(rightCube.containedInSphere(origin,radius))
438  {
439 
440  ASSERT(indexedPoints[nodeIdx].first.sqrDist(origin) < sqrRadius);
441  idxQueue.push(nodes[nodeIdx].childRight);
442  }
443  else
444  nodeQueue.push(NodeWalk(nodes[nodeIdx].childRight,rightCube,depth+1));
445  }
446  }
447 
448  if(indexedPoints[nodeIdx].first.sqrDist(origin) < sqrRadius)
449  pts.push_back(nodeIdx);
450 
451  }
452 
453 
454  pts.reserve(idxQueue.size());
455  //Walk the idx queue to enumerate all children that are in the sphere
456  while(!idxQueue.empty())
457  {
458  size_t curIdx;
459  curIdx=idxQueue.front();
460  ASSERT(indexedPoints[curIdx].first.sqrDist(origin) < sqrRadius);
461  if(nodes[curIdx].childLeft != (size_t)-1)
462  idxQueue.push(nodes[curIdx].childLeft);
463 
464  if(nodes[curIdx].childRight !=(size_t) -1)
465  idxQueue.push(nodes[curIdx].childRight);
466 
467 
468  ASSERT(curIdx < nodes.size());
469  pts.push_back(curIdx);
470  idxQueue.pop();
471  }
472 
473 }
474 
476  const BoundCube &domainCube, bool shouldTag)
477 {
478  enum { NODE_FIRST_VISIT, //First visit is when you descend the tree
479  NODE_SECOND_VISIT, //Second visit is when you come back from ->Left()
480  NODE_THIRD_VISIT // Third visit is when you come back from ->Right()
481  };
482 
483  ASSERT(treeRoot!=(size_t)-1);
484  ASSERT(callback);
485  ASSERT(progress);
486 
487  size_t nodeStack[maxDepth+1];
488  float domainStack[maxDepth+1][2];
489  unsigned int visitStack[maxDepth+1];
490 
491  size_t bestPoint;
492  size_t curNode;
493 
494  BoundCube curDomain;
495  unsigned int visit;
496  unsigned int stackTop;
497  unsigned int curAxis;
498 
499  float bestDistSqr;
500  float tmpEdge;
501 
502  if(nodes.empty())
503  return -1;
504 
505  bestPoint=(size_t)-1;
506  bestDistSqr =std::numeric_limits<float>::max();
507  curDomain=domainCube;
508  visit=NODE_FIRST_VISIT;
509  curAxis=0;
510  stackTop=0;
511 
512  //Start at median of array, which is top of tree,
513  //by definition
514  curNode=treeRoot;
515 
516  //check root node
517  if(!nodes[curNode].tagged)
518  {
519  float tmpDistSqr;
520  tmpDistSqr = indexedPoints[curNode].first.sqrDist(searchPt);
521  if(tmpDistSqr < bestDistSqr)
522  {
523  bestDistSqr = tmpDistSqr;
524  bestPoint=curNode;
525  }
526  }
527 
528  do
529  {
530  switch(visit)
531  {
532  //Examine left branch
533  case NODE_FIRST_VISIT:
534  {
535  if(searchPt[curAxis] < indexedPoints[curNode].first[curAxis])
536  {
537  if(nodes[curNode].childLeft!=(size_t)-1)
538  {
539  //Check bounding box when shrunk overlaps best
540  //estimate sphere
541  tmpEdge= curDomain.bounds[curAxis][1];
542  curDomain.bounds[curAxis][1] = indexedPoints[curNode].first[curAxis];
543  if(!curDomain.intersects(searchPt,bestDistSqr))
544  {
545  curDomain.bounds[curAxis][1] = tmpEdge;
546  visit++;
547  continue;
548  }
549  //Preserve our current state.
550  nodeStack[stackTop]=curNode;
551  visitStack[stackTop] = NODE_SECOND_VISIT; //Oh, It will be. It will be.
552  domainStack[stackTop][1] = tmpEdge;
553  domainStack[stackTop][0]= curDomain.bounds[curAxis][0];
554  stackTop++;
555 
556  //Update the current information
557  curNode=nodes[curNode].childLeft;
558  visit=NODE_FIRST_VISIT;
559  curAxis++;
560  curAxis%=3;
561  continue;
562  }
563  }
564  else
565  {
566  if(nodes[curNode].childRight!=(size_t)-1)
567  {
568  //Check bounding box when shrunk overlaps best
569  //estimate sphere
570  tmpEdge= curDomain.bounds[curAxis][0];
571  curDomain.bounds[curAxis][0] = indexedPoints[curNode].first[curAxis];
572 
573  if(!curDomain.intersects(searchPt,bestDistSqr))
574  {
575  curDomain.bounds[curAxis][0] =tmpEdge;
576  visit++;
577  continue;
578  }
579 
580  //Preserve our current state.
581  nodeStack[stackTop]=curNode;
582  visitStack[stackTop] = NODE_SECOND_VISIT; //Oh, It will be. It will be.
583  domainStack[stackTop][0] = tmpEdge;
584  domainStack[stackTop][1]= curDomain.bounds[curAxis][1];
585  stackTop++;
586 
587  //Update the information
588  curNode=nodes[curNode].childRight;
589  visit=NODE_FIRST_VISIT;
590  curAxis++;
591  curAxis%=3;
592  continue;
593  }
594  }
595  visit++;
596  //Fall through
597  }
598  //Examine right branch
599  case NODE_SECOND_VISIT:
600  {
601  if(searchPt[curAxis]< indexedPoints[curNode].first[curAxis])
602  {
603  if(nodes[curNode].childRight!=(size_t)-1)
604  {
605  //Check bounding box when shrunk overlaps best
606  //estimate sphere
607  tmpEdge= curDomain.bounds[curAxis][0];
608  curDomain.bounds[curAxis][0] = indexedPoints[curNode].first[curAxis];
609 
610  if(!curDomain.intersects(searchPt,bestDistSqr))
611  {
612  curDomain.bounds[curAxis][0] = tmpEdge;
613  visit++;
614  continue;
615  }
616 
617  nodeStack[stackTop]=curNode;
618  visitStack[stackTop] = NODE_THIRD_VISIT;
619  domainStack[stackTop][0] = tmpEdge;
620  domainStack[stackTop][1]= curDomain.bounds[curAxis][1];
621  stackTop++;
622 
623  //Update the information
624  curNode=nodes[curNode].childRight;
625  visit=NODE_FIRST_VISIT;
626  curAxis++;
627  curAxis%=3;
628  continue;
629 
630  }
631  }
632  else
633  {
634  if(nodes[curNode].childLeft!=(size_t)-1)
635  {
636  //Check bounding box when shrunk overlaps best
637  //estimate sphere
638  tmpEdge= curDomain.bounds[curAxis][1];
639  curDomain.bounds[curAxis][1] = indexedPoints[curNode].first[curAxis];
640 
641  if(!curDomain.intersects(searchPt,bestDistSqr))
642  {
643  curDomain.bounds[curAxis][1] = tmpEdge;
644  visit++;
645  continue;
646  }
647  //Preserve our current state.
648  nodeStack[stackTop]=curNode;
649  visitStack[stackTop] = NODE_THIRD_VISIT;
650  domainStack[stackTop][1] = tmpEdge;
651  domainStack[stackTop][0]= curDomain.bounds[curAxis][0];
652  stackTop++;
653 
654  //Update the information
655  curNode=nodes[curNode].childLeft;
656  visit=NODE_FIRST_VISIT;
657  curAxis++;
658  curAxis%=3;
659  continue;
660 
661  }
662  }
663  visit++;
664  //Fall through
665  }
666  case NODE_THIRD_VISIT:
667  {
668  //Decide if we should promote the current node
669  //to "best" (ie nearest untagged) node.
670  //To promote, it musnt be tagged, and it must
671  //be closer than cur best estimate.
672  if(!nodes[curNode].tagged)
673  {
674  float tmpDistSqr;
675  tmpDistSqr = indexedPoints[curNode].first.sqrDist(searchPt);
676  if(tmpDistSqr < bestDistSqr)
677  {
678  bestDistSqr = tmpDistSqr;
679  bestPoint=curNode;
680  }
681  }
682 
683  //DEBUG
684  ASSERT(stackTop%3 == curAxis)
685  //
686  if(curAxis)
687  curAxis--;
688  else
689  curAxis=2;
690 
691 
692 
693  ASSERT(stackTop < maxDepth+1);
694  if(stackTop)
695  {
696  stackTop--;
697  visit=visitStack[stackTop];
698  curNode=nodeStack[stackTop];
699  curDomain.bounds[curAxis][0]=domainStack[stackTop][0];
700  curDomain.bounds[curAxis][1]=domainStack[stackTop][1];
701  ASSERT((stackTop)%3 == curAxis);
702  }
703 
704  break;
705  }
706  default:
707  ASSERT(false);
708 
709 
710  }
711 
712 
713  //Keep going until we meet the root node for the third time (one left, one right, one finish)
714  }while(!(curNode== treeRoot && visit== NODE_THIRD_VISIT));
715 
716  if(bestPoint != (size_t) -1)
717  nodes[bestPoint].tagged|=shouldTag;
718  return bestPoint;
719 
720 }
721 
722 
723 
725  const BoundCube &domainCube, float radius, vector<size_t> &result)
726 
727 {
728  //TODO: Benchmark competing, threadable implementation where we track our own tags
729  // for the calculation
730  result.clear();
731 
732 
733  //Find all pts within sphere of "radius" from searchPt
734  float sqrRad=radius*radius;
735  do
736  {
737 
738  //Find next untagged point
739  size_t resIdx;
740  resIdx =findNearestUntagged(searchPt,domainCube, radius);
741 
742  //if not a point, then we are done
743  if(resIdx == (size_t)-1)
744  break;
745 
746  if(searchPt.sqrDist(getPtRef(resIdx)) > sqrRad)
747  {
748  tag(resIdx,false);
749  break;
750  }
751 
752  ASSERT(resIdx < size());
753  result.push_back(resIdx);
754  }while(true);
755 
756 
757  //The points we found were untagged intially, re-untag them
758  for(size_t ui=0;ui<result.size();ui++)
759  {
760  tag(result[ui],false);
761  }
762 
763 }
764 
766 {
767  size_t count=0;
768 
769  #pragma omp parallel for reduction(+:count)
770  for(size_t ui=0;ui<nodes.size();ui++)
771  {
772  if(nodes[ui].tagged)
773  count++;
774 
775  }
776 
777  return count;
778 }
779 
780 void K3DTreeExact::clearTags(std::vector<size_t> &tagsToClear)
781 {
782 #pragma omp parallel for
783  for(size_t ui=0;ui<tagsToClear.size();ui++)
784  nodes[tagsToClear[ui]].tagged=false;
785 }
786 
788 {
789 #pragma omp parallel for
790  for(size_t ui=0;ui<nodes.size();ui++)
791  nodes[ui].tagged=false;
792 }
793 
794 
795 #ifdef DEBUG
796 #include <iostream>
797 #include <atomprobe/io/dataFiles.h>
798 using std::cerr;
799 using std::endl;
800 bool dummyCallback()
801 {
802  return true;
803 }
804 
805 bool inSphereTest()
806 {
807  const float CENTRE_POS=0.5f;
808 
809  const unsigned int NPTS=20;
810  Point3D p,centre;
811  centre = Point3D(CENTRE_POS,CENTRE_POS,CENTRE_POS);
812 
813  vector<Point3D> spherePts;
814  vector<Point3D> allPts;
815 
816  allPts.reserve(NPTS*NPTS*NPTS);
817  spherePts.reserve(allPts.size()*CENTRE_POS);
818 
819  //Build a grid of points in [0,1]
820  for(size_t ui=0;ui<NPTS;ui++)
821  {
822  for(size_t uj=0;uj<NPTS;uj++)
823  {
824  for(size_t uk=0;uk<NPTS;uk++)
825  {
826  p= Point3D((float)ui,(float)uj,(float)uk);
827  p*=1.0/(float)NPTS;
828 
829  if(p.sqrDist(centre) < CENTRE_POS*CENTRE_POS)
830  spherePts.push_back(p);
831  allPts.push_back(p);
832  }
833  }
834  }
835 
836 
837  unsigned int dummyProg=0;
838 
839  K3DTreeExact tree;
840  tree.resetPts(allPts);
841  tree.setCallback(dummyCallback);
842  tree.setProgressPointer(&dummyProg);
843 
844  tree.build();
845  vector<size_t> spherePtIdx;
846  tree.ptsInSphere(centre,CENTRE_POS,spherePtIdx);
847 
848  TEST(spherePts.size() == spherePtIdx.size(), "Check brute force and KD methods match in size");
849 
850 
851  //Ensure that all points lie within the sphere
852  for(size_t ui=0;ui<spherePtIdx.size();ui++)
853  {
854  const Point3D *pt;
855  pt=tree.getPt(spherePtIdx[ui]);
856  ASSERT(pt->sqrDist(centre) < CENTRE_POS*CENTRE_POS);
857  }
858 
859 
860 
861  return true;
862 }
863 
864 bool K3DTreeExact::runUnitTests()
865 {
866 
867  TEST(inSphereTest(),"In-sphere test");
868 
869  return true;
870 }
871 #endif
float sqrDist(const Point3D &pt) const
Returns the square of distance to another Point3D.
Definition: point3D.cpp:272
void clearTags(std::vector< size_t > &tagsToClear)
bool getTag(size_t treeIndex) const
void getBoundCube(BoundCube &b)
obtain the bounding rectangular prism volume for all elements in the KD tree
void setProgressPointer(unsigned int *p)
void setAxis(unsigned int Axis)
bool callback()
Definition: kd-example.cpp:37
bool intersects(const Point3D &pt, float sqrRad) const
Checks if a point intersects a sphere of centre Pt, radius^2 sqrRad.
Definition: boundcube.cpp:434
3D specific KD tree
Definition: K3DTree-exact.h:54
#define TEST(f, g)
Definition: aptAssert.h:49
size_t getOrigIndex(size_t treeIndex) const
void findUntaggedInRadius(const Point3D &queryPt, const BoundCube &b, float radius, std::vector< size_t > &result)
Find untagged points within a given radius.
A 3D point data class storage.
Definition: point3D.h:39
void setCallback(bool(*cb)(void))
const Point3D * getPt(size_t index) const
Functor allowing for sorting of points in 3D.
static BoundCube getIonDataLimits(const std::vector< IonHit > &points)
Definition: ionhit.cpp:188
NodeWalk(unsigned int idx, const BoundCube &bc, unsigned int dpth)
const size_t PROGRESS_REDUCE
Definition: mesh.cpp:61
bool operator()(const std::pair< Point3D, size_t > &p1, const std::pair< Point3D, size_t > &p2) const
unsigned int depth
void dump(std::ostream &, size_t depth=0, size_t offset=-1) const
Textual output of tree. Tabs are used to separate different levels of the tree.
K3DTreeExact()
KD Tree constructor. Tree is uninitialised at start.
Helper class to define a bounding cube.
Definition: boundcube.h:29
bool containedInSphere(const Point3D &origin, float radius) const
Returns true if this box is contained by the sphere, represented by the origin+radius.
Definition: boundcube.cpp:379
void resetPts(std::vector< Point3D > &pts, bool clear=true)
Supply points to KD tree. Ouput vector will be erased if clear=true.
bool build()
Build the KD tree using the previously supplied points.
#define ASSERT(f)
size_t index
BoundCube cube
unsigned int progress
Definition: kd-example.cpp:26
void tag(size_t treeIndex, bool tagVal=true)
size_t findNearestUntagged(const Point3D &queryPt, const BoundCube &b, bool tag=true)
Find the nearest "untagged" point&#39;s internal index.
const Point3D & getPtRef(size_t index) const
void ptsInSphere(const Point3D &origin, float radius, std::vector< size_t > &pts) const
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