The spica renderer
kdtree_detail.h
1 #ifndef _SPICA_KDTREE_DETAIL_H_
2 #define _SPICA_KDTREE_DETAIL_H_
3 
4 #include <cmath>
5 #include <algorithm>
6 
7 #include "core/common.h"
8 #include "core/vector3d.h"
9 
10 namespace spica {
11 
12  template <class Ty>
13  KdTree<Ty>::KdTree()
14  : _nodes(nullptr)
15  , _numCopies(nullptr)
16  {
17  }
18 
19  template <class Ty>
20  KdTree<Ty>::KdTree(const KdTree& kdtree)
21  : _nodes(nullptr)
22  , _numCopies(nullptr)
23  {
24  this->operator=(kdtree);
25  }
26 
27  template <class Ty>
28  KdTree<Ty>::~KdTree()
29  {
30  release();
31  }
32 
33  template <class Ty>
34  KdTree<Ty>& KdTree<Ty>::operator=(const KdTree& kdtree) {
35  release();
36  _numCopies = kdtree._numCopies;
37  (*_numCopies) += 1;
38  _nodes = kdtree._nodes;
39  }
40 
41  template <class Ty>
42  void KdTree<Ty>::release() {
43  if (_numCopies != nullptr) {
44  if (*_numCopies == 0) {
45  delete[] _nodes;
46  delete _numCopies;
47  _numCopies = nullptr;
48  } else {
49  (*_numCopies) -= 1;
50  }
51  }
52  }
53 
54  template <class Ty>
55  void KdTree<Ty>::add(const Ty& point) {
56  _nodes = addRec(_nodes, point, 0);
57  }
58 
59  template <class Ty>
60  typename KdTree<Ty>::KdTreeNode* KdTree<Ty>::addRec(KdTreeNode* node, const Ty& point, int dim) {
61  if (node == nullptr) {
62  KdTreeNode* ret = new KdTreeNode;
63  ret->point = point;
64  ret->axis = dim;
65  return ret;
66  } else {
67  if (point[dim] < node->point[dim]) {
68  node->left = addRec(node->left, point, (dim + 1) % 3);
69  } else {
70  node->right = addRec(node->right, point, (dim + 1) % 3);
71  }
72  return node;
73  }
74  }
75 
76  template <class Ty>
77  void KdTree<Ty>::construct(const std::vector<Ty>& points) {
78  // Compute tree size
79  const int numPoints = static_cast<int>(points.size());
80  int numNodes;
81  for (numNodes = 1; numNodes < numPoints; numNodes <<= 1) ;
82 
83  _nodes = new KdTreeNode[numNodes];
84 
85  std::vector<const Ty*> pointers(numPoints);
86  for (int i = 0; i < numPoints; i++) {
87  pointers[i] = &points[i];
88  }
89  constructRec(pointers, 0, 0, numPoints, 0);
90  _numCopies = new int(0);
91  }
92 
93  template <class Ty>
94  typename KdTree<Ty>::KdTreeNode* KdTree<Ty>::constructRec(std::vector<const Ty*>& points, const int nodeID, const int startID, const int endID, const int dim) {
95  if (startID >= endID) {
96  return NULL;
97  }
98 
99  std::sort(points.begin() + startID, points.begin() + endID, AxisComparator(dim));
100 
101  int mid = (startID + endID) / 2;
102  KdTreeNode* node = &_nodes[nodeID];
103  node->axis = dim;
104  node->point = (*points[mid]);
105  node->left = constructRec(points, nodeID * 2 + 1, startID, mid, (dim + 1) % 3);
106  node->right = constructRec(points, nodeID * 2 + 2, mid + 1, endID, (dim + 1) % 3);
107  return node;
108  }
109 
110  template <class Ty>
111  void KdTree<Ty>::knnSearch(const Ty& point, const KnnQuery& query, std::vector<Ty>* results) const {
112  PriorityQueue que;
113  KnnQuery qq = query;
114  if ((qq.type & EPSILON_BALL) == 0) qq.epsilon = INFTY;
115 
116  knnSearchRec(&_nodes[0], point, qq, &que);
117 
118  while (!que.empty()) {
119  results->push_back(que.top().t);
120  que.pop();
121  }
122  }
123 
124  template <class Ty>
125  void KdTree<Ty>::knnSearchRec(typename KdTree<Ty>::KdTreeNode* node, const Ty& point, KnnQuery& query, PriorityQueue* results) const {
126  if (node == NULL) {
127  return;
128  }
129 
130  const double dist = distance(node->point, point);
131  if (dist < query.epsilon) {
132  results->push(OrderedType(dist, node->point));
133  if ((query.type & K_NEAREST) != 0 && results->size() > query.k) {
134  results->pop();
135 
136  query.epsilon = distance(results->top().t, point);
137  }
138  }
139 
140  int axis = node->axis;
141  double delta = point[axis] - node->point[axis];
142  if (delta < 0.0) {
143  knnSearchRec(node->left, point, query, results);
144  if (std::abs(delta) <= query.epsilon) {
145  knnSearchRec(node->right, point, query, results);
146  }
147  } else {
148  knnSearchRec(node->right, point, query, results);
149  if (std::abs(delta) <= query.epsilon) {
150  knnSearchRec(node->left, point, query, results);
151  }
152  }
153  }
154 
155  template <class Ty>
156  double KdTree<Ty>::distance(const Ty& p1, const Ty& p2) {
157  return Ty::distance(p1, p2);
158  }
159 
160  template <>
161  inline double KdTree<Vector3d>::distance(const Vector3d& p1, const Vector3d& p2) {
162  return (p1 - p2).norm();
163  }
164 
165 }
166 
167 #endif // _SPICA_KDTREE_DETAIL_H_