libatomprobe
Library for Atom Probe Tomography (APT) computation
K3DTree-approx.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014 Daniel Haley
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
18 
20 
21 #ifdef DEBUG
22  #include <cstdlib>
23  #include <iostream>
24  using std::endl;
25  using std::cerr;
26 #endif
27 
28 #include <limits>
29 #include <cstring>
30 
31 using std::vector;
32 
33 namespace AtomProbe
34 {
35 
36 
37 
38 //Axis compare
39 //==========
41 {
42 }
43 
44 void AxisCompare::setAxis(unsigned int sortAxis)
45 {
46  axis=sortAxis;
47 }
48 
49 //==========
50 
51 //K3D node
52 //==========
53 void K3DNodeApprox::setLoc(const Point3D &locNew)
54 {
55  loc=locNew;
56 }
57 
59 {
60  return loc;
61 }
62 
64 {
65 
66  if(childLeft)
67  {
68  childLeft->deleteChildren();
69  delete childLeft;
70  childLeft=0;
71  }
72 
73  if(childRight)
74  {
75  childRight->deleteChildren();
76  delete childRight;
77  childRight=0;
78  }
79 
80 
81 }
82 
83 void K3DNodeApprox::dump(std::ostream &strm, unsigned int depth) const
84 {
85  for(unsigned int ui=0;ui<depth; ui++)
86  strm << "\t";
87 
88  strm << "(" << loc.getValue(0)
89  << "," << loc.getValue(1) << "," << loc.getValue(2)
90  << ")" << std::endl;
91 
92  if(childLeft)
93  childLeft->dump(strm,depth+1);
94 
95  if(childRight)
96  childRight->dump(strm,depth+1);
97 }
98 
99 //===========
100 
101 //K3D Tree
102 //=============
103 K3DTreeApprox::K3DTreeApprox() : maxDepth(0),root(0)
104 {
105  treeSize=0;
106 }
107 
108 
110 {
111  kill();
112 }
113 
114 
116 {
117  if(root)
118  {
119  root->deleteChildren();
120  delete root;
121  root=0;
122  treeSize=0;
123  }
124 }
125 
126 //Build the KD tree
127 void K3DTreeApprox::build(const vector<Point3D> &origPts)
128 {
129  vector<Point3D> pts;
130  pts=origPts;
131 
132  //che. to see if the pts vector is empty
133  if(!pts.size())
134  {
135  maxDepth=0;
136  return;
137  }
138 
139  if(root)
140  kill();
141 
142  treeSize=pts.size();
143  maxDepth=1;
144  root=buildRecurse(pts.begin(), pts.end(),0);
145 
146 }
147 
148 //Build the KD tree, shuffling original
149 void K3DTreeApprox::buildByRef(vector<Point3D> &pts)
150 {
151  //che. to see if the pts vector is empty
152  if(pts.empty())
153  {
154  maxDepth=0;
155  return;
156  }
157 
158  if(root)
159  kill();
160 
161  treeSize=pts.size();
162  maxDepth=1;
163  root=buildRecurse(pts.begin(), pts.end(),0);
164 
165 }
166 
167 K3DNodeApprox*K3DTreeApprox::buildRecurse(vector<Point3D>::iterator pts_start, vector<Point3D>::iterator pts_end, unsigned int depth)
168 {
169 
170  K3DNodeApprox*node= new K3DNodeApprox;
171  unsigned int curAxis=depth%3;
172  unsigned int ptsSize=pts_end - pts_start - 1;//pts.size()-1
173  node->setAxis(curAxis);
174 
175  //if we are deeper, then record that
176  if(depth > maxDepth)
177  maxDepth=depth;
178 
179  unsigned int median =(ptsSize)/2;
180 
181  //set up initial node
182  AxisCompare axisCmp;
183  axisCmp.setAxis(curAxis);
184 
185  //Find the median value in the current axis
186  sort(pts_start,pts_end,axisCmp);
187 
188  //allocate node (this stores a copy of the point) and set.
189  node->setLoc(*(pts_start + median));
190 
191 
192  if(median)
193  node->setLeft(buildRecurse(pts_start,pts_start + median,depth+1));
194  else
195  node->setLeft(0);
196 
197  if(median!=ptsSize)
198  node->setRight(buildRecurse(pts_start + median + 1, pts_end,depth+1));
199  else
200  node->setRight(0);
201 
202  return node;
203 
204 }
205 
206 void K3DTreeApprox::dump( std::ostream &strm) const
207 {
208  if(root)
209  root->dump(strm,0);
210 }
211 
212 
213 const Point3D *K3DTreeApprox::findNearest(const Point3D &searchPt, const BoundCube &domainCube,
214  const float deadDistSqr) const
215 {
216  enum { NODE_FIRST_VISIT, //First visit is when you descend the tree
217  NODE_SECOND_VISIT, //Second visit is when you come back from ->Left()
218  NODE_THIRD_VISIT // Third visit is when you come back from ->Right()
219  };
220 
221 
222  //The stacks are for the nodes above the current Node.
223  const K3DNodeApprox*nodeStack[maxDepth+1];
224  float domainStack[maxDepth+1][2];
225  unsigned int visitStack[maxDepth+1];
226 
227  const Point3D *bestPoint;
228  const K3DNodeApprox*curNode;
229 
230  BoundCube curDomain;
231  unsigned int visit;
232  unsigned int stackTop;
233  unsigned int curAxis;
234 
235  float bestDistSqr;
236  float tmpEdge;
237 
238  //Set the root as the best estimate
239 
240  bestPoint =0;
241  bestDistSqr =std::numeric_limits<float>::max();
242  curDomain=domainCube;
243  visit=NODE_FIRST_VISIT;
244  curAxis=0;
245 
246  stackTop=0;
247 /*
248  nodeStack[0]=root;
249  visitStack[0]=NODE_FIRST_VISIT;
250  domainStack[0][0]=curDomain.bounds[0][0];
251  domainStack[0][1]=curDomain.bounds[0][1];
252 */
253  curNode=root;
254 
255  do
256  {
257 
258  switch(visit)
259  {
260  //Examine left branch
261  case NODE_FIRST_VISIT:
262  {
263  if(searchPt[curAxis] < curNode->getLocVal(curAxis))
264  {
265  if(curNode->left())
266  {
267  //Check bounding box when shrunk overlaps best
268  //estimate sphere
269  tmpEdge= curDomain.bounds[curAxis][1];
270  curDomain.bounds[curAxis][1] = curNode->getLocVal(curAxis);
271  if(bestPoint && !curDomain.intersects(*bestPoint,bestDistSqr))
272  {
273  curDomain.bounds[curAxis][1] = tmpEdge;
274  visit++;
275  continue;
276  }
277 
278  //Preserve our current state.
279  nodeStack[stackTop]=curNode;
280  visitStack[stackTop] = NODE_SECOND_VISIT; //Oh, It will be. It will be.
281  domainStack[stackTop][1] = tmpEdge;
282  domainStack[stackTop][0]= curDomain.bounds[curAxis][0];
283  stackTop++;
284 
285  //Update the current information
286  curNode=curNode->left();
287  visit=NODE_FIRST_VISIT;
288  curAxis++;
289  curAxis%=3;
290  continue;
291  }
292  }
293  else
294  {
295  if(curNode->right())
296  {
297  //Check bounding box when shrunk overlaps best
298  //estimate sphere
299  tmpEdge= curDomain.bounds[curAxis][0];
300  curDomain.bounds[curAxis][0] = curNode->getLocVal(curAxis);
301 
302  if(bestPoint && !curDomain.intersects(*bestPoint,bestDistSqr))
303  {
304  curDomain.bounds[curAxis][0] =tmpEdge;
305  visit++;
306  continue;
307  }
308  //Preserve our current state.
309  nodeStack[stackTop]=curNode;
310  visitStack[stackTop] = NODE_SECOND_VISIT; //Oh, It will be. It will be.
311  domainStack[stackTop][0] = tmpEdge;
312  domainStack[stackTop][1]= curDomain.bounds[curAxis][1];
313  stackTop++;
314 
315  //Update the information
316  curNode=curNode->right();
317  visit=NODE_FIRST_VISIT;
318  curAxis++;
319  curAxis%=3;
320  continue;
321  }
322 
323  }
324  visit++;
325  //Fall through
326  }
327  //Examine right branch
328  case NODE_SECOND_VISIT:
329  {
330  if(searchPt[curAxis]< curNode->getLocVal(curAxis))
331  {
332  if(curNode->right())
333  {
334  //Check bounding box when shrunk overlaps best
335  //estimate sphere
336  tmpEdge= curDomain.bounds[curAxis][0];
337  curDomain.bounds[curAxis][0] = curNode->getLocVal(curAxis);
338 
339  if(bestPoint && !curDomain.intersects(*bestPoint,bestDistSqr))
340  {
341  curDomain.bounds[curAxis][0] = tmpEdge;
342  visit++;
343  continue;
344  }
345 
346  nodeStack[stackTop]=curNode;
347  visitStack[stackTop] = NODE_THIRD_VISIT; //Oh, It will be. It will be.
348  domainStack[stackTop][0] = tmpEdge;
349  domainStack[stackTop][1]= curDomain.bounds[curAxis][1];
350  stackTop++;
351 
352  //Update the information
353  curNode=curNode->right();
354  visit=NODE_FIRST_VISIT;
355  curAxis++;
356  curAxis%=3;
357  continue;
358 
359  }
360  }
361  else
362  {
363  if(curNode->left())
364  {
365  //Check bounding box when shrunk overlaps best
366  //estimate sphere
367  tmpEdge= curDomain.bounds[curAxis][1];
368  curDomain.bounds[curAxis][1] = curNode->getLocVal(curAxis);
369 
370  if(bestPoint && !curDomain.intersects(*bestPoint,bestDistSqr))
371  {
372  curDomain.bounds[curAxis][1] = tmpEdge;
373  visit++;
374  continue;
375  }
376  //Preserve our current state.
377  nodeStack[stackTop]=curNode;
378  visitStack[stackTop] = NODE_THIRD_VISIT; //Oh, It will be. It will be.
379  domainStack[stackTop][1] = tmpEdge;
380  domainStack[stackTop][0]= curDomain.bounds[curAxis][0];
381  stackTop++;
382 
383  //Update the information
384  curNode=curNode->left();
385  visit=NODE_FIRST_VISIT;
386  curAxis++;
387  curAxis%=3;
388  continue;
389 
390  }
391  }
392  visit++;
393  //Fall through
394  }
395  //Go up
396  case NODE_THIRD_VISIT:
397  {
398  float tmpDistSqr;
399  tmpDistSqr = curNode->sqrDist(searchPt);
400  if(tmpDistSqr < bestDistSqr && tmpDistSqr > deadDistSqr)
401  {
402  bestDistSqr = tmpDistSqr;
403  bestPoint=curNode->getLocRef();
404  }
405 
406  //DEBUG
407  ASSERT(stackTop%3 == curAxis)
408  //
409  if(curAxis)
410  curAxis--;
411  else
412  curAxis=2;
413 
414 
415 
416  ASSERT(stackTop < maxDepth+1);
417  if(stackTop)
418  {
419  stackTop--;
420  visit=visitStack[stackTop];
421  curNode=nodeStack[stackTop];
422  curDomain.bounds[curAxis][0]=domainStack[stackTop][0];
423  curDomain.bounds[curAxis][1]=domainStack[stackTop][1];
424  }
425  ASSERT((stackTop)%3 == curAxis)
426  break;
427  }
428  default:
429  ASSERT(false);
430 
431 
432  }
433 
434  //Keep going until we meet the root nde for the third time (one left, one right, one finish)
435  }while(!(curNode==root && visit== NODE_THIRD_VISIT));
436 
437  //Check the root node, as we miss this on the way up
438  float rootDist = root->sqrDist(searchPt);
439  if(rootDist < bestDistSqr && rootDist > deadDistSqr)
440  bestPoint = root->getLocRef();
441 
442  return bestPoint;
443 
444 }
445 
446 
447 void K3DTreeApprox::findKNearest(const Point3D &searchPt, const BoundCube &domainCube,
448  unsigned int num, vector<const Point3D *> &bestPts,
449  float deadDistSqr) const
450 {
451  //find the N nearest points
452  bestPts.clear();
453  bestPts.reserve(num);
454 
455  const Point3D *p;
456  for(unsigned int ui=0; ui<num; ui++)
457  {
458  float sqrDist;
459  p= findNearest(searchPt, domainCube,
460  deadDistSqr);
461 
462  if(!p)
463  return;
464  else
465  bestPts.push_back(p);
466 
467  sqrDist = p->sqrDist(searchPt);
468  deadDistSqr = sqrDist+std::numeric_limits<float>::epsilon();
469 
470  }
471 
472 }
473 //=============
474 
475 #ifdef DEBUG
476 bool K3DTreeApprox::test()
477 {
478  vector<Point3D> pts;
479 
480  pts.push_back(Point3D(0,0,0));
481  pts.push_back(Point3D(0,0.5,0.5));
482  pts.push_back(Point3D(100,0,0));
483 
484  BoundCube bc(pts);
485 
486  K3DTreeApprox k3dTree;
487  k3dTree.build(pts);
488 
489  const Point3D *p;
490  p = k3dTree.findNearest(pts[0],bc,0.00001);
491 
492  TEST( p->sqrDist(pts[1]) < 0.01, "findNearest check");
493 
494  return true;
495 }
496 #endif
497 
498 }
float sqrDist(const Point3D &pt) const
Returns the square of distance to another Point3D.
Definition: point3D.cpp:272
float getLocVal(unsigned int pos) const
Obtain the coordinates at dimension "pos".
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
float sqrDist(const Point3D &pt) const
Obtain the distance between this node and another point.
#define TEST(f, g)
Definition: aptAssert.h:49
const Point3D * getLocRef() const
Return a pointer to the point from the node.
void findKNearest(const Point3D &sourcePoint, const BoundCube &treeDomain, unsigned int numNNs, std::vector< const Point3D *> &results, float deadDistSqr=0.0f) const
Find the nearest N points.
K3DNodeApprox * left() const
Obtain pointer to left child.
void dump(std::ostream &) const
Textual output of tree. tabs are used to separate different levels of the tree.
A 3D point data class storage.
Definition: point3D.h:39
void setLeft(K3DNodeApprox *node)
Set the left child node.
K3DNodeApprox * right() const
Obtain pointer to right child.
~K3DTreeApprox()
Cleans up tree, deallocates nodes.
void setLoc(const Point3D &)
Set the point data associated with this node.
const Point3D * findNearest(const Point3D &, const BoundCube &, float deadDistSqr) const
Find the nearest point to a given P that lies outsid some dead zone.
void deleteChildren()
Recursively delete this node and all children.
Helper class to define a bounding cube.
Definition: boundcube.h:29
void kill()
Destroy the tree contents.
#define ASSERT(f)
3D specific KD tree
void dump(std::ostream &, unsigned int) const
print the node data out to a given stream
void buildByRef(std::vector< Point3D > &pts)
Builds a balanced KD tree from a list of points.
void setAxis(unsigned int Axis)
Node Class for storing point.
Functor allowing for sorting of points in 3D.
Point3D getLoc() const
Return the point data from the ndoe.
void setRight(K3DNodeApprox *node)
Set the right child node.
void build(const std::vector< Point3D > &pts)
Builds a balanced KD tree from a list of points.
void setAxis(unsigned int newAxis)
Set the axis that this node operates on.