CDT  1.4.5
C++ library for constrained Delaunay triangulation
Loading...
Searching...
No Matches
KDTree.h
1
6
7#ifndef KDTREE_KDTREE_H
8#define KDTREE_KDTREE_H
9
10#include "CDTUtils.h"
11
12#include <limits>
13
14namespace CDT
15{
16namespace KDTree
17{
18
20{
21 enum Enum
22 {
23 X,
24 Y,
25 };
26};
27
39template <
40 typename TCoordType,
41 size_t NumVerticesInLeaf,
42 size_t InitialStackDepth,
43 size_t StackDepthIncrement>
44class KDTree
45{
46public:
47 typedef TCoordType coord_type;
48 typedef CDT::V2d<coord_type> point_type;
49 typedef CDT::VertInd point_index;
50 typedef std::pair<point_type, point_index> value_type;
51 typedef std::vector<point_index> point_data_vec;
52 typedef point_data_vec::const_iterator pd_cit;
53 typedef CDT::VertInd node_index;
54 typedef CDT::array<node_index, 2> children_type;
55
57 struct Node
58 {
59 children_type children;
60 point_data_vec data;
63 {
64 setChildren(node_index(0), node_index(0));
65 data.reserve(NumVerticesInLeaf);
66 }
67
68 void setChildren(const node_index c1, const node_index c2)
69 {
70 children[0] = c1;
71 children[1] = c2;
72 }
73
74 bool isLeaf() const
75 {
76 return children[0] == children[1];
77 }
78 };
79
82 : m_rootDir(NodeSplitDirection::X)
83 , m_min(point_type(
84 -std::numeric_limits<coord_type>::max(),
85 -std::numeric_limits<coord_type>::max()))
86 , m_max(point_type(
87 std::numeric_limits<coord_type>::max(),
88 std::numeric_limits<coord_type>::max()))
89 , m_size(0)
90 , m_isRootBoxInitialized(false)
91 , m_tasksStack(InitialStackDepth, NearestTask())
92 {
93 m_root = addNewNode();
94 }
95
97 KDTree(const point_type& min, const point_type& max)
98 : m_rootDir(NodeSplitDirection::X)
99 , m_min(min)
100 , m_max(max)
101 , m_size(0)
102 , m_isRootBoxInitialized(true)
103 , m_tasksStack(InitialStackDepth, NearestTask())
104 {
105 m_root = addNewNode();
106 }
107
108 CDT::VertInd size() const
109 {
110 return m_size;
111 }
112
117 void
118 insert(const point_index& iPoint, const std::vector<point_type>& points)
119 {
120 ++m_size;
121 // if point is outside root, extend tree by adding new roots
122 const point_type& pos = points[iPoint];
123 while(!isInsideBox(pos, m_min, m_max))
124 {
125 extendTree(pos);
126 }
127 // now insert the point into the tree
128 node_index node = m_root;
129 point_type min = m_min;
130 point_type max = m_max;
131 NodeSplitDirection::Enum dir = m_rootDir;
132
133 // below: initialized only to suppress warnings
134 NodeSplitDirection::Enum newDir(NodeSplitDirection::X);
135 coord_type mid(0);
136 point_type newMin, newMax;
137 while(true)
138 {
139 if(m_nodes[node].isLeaf())
140 {
141 // add point if capacity is not reached
142 point_data_vec& pd = m_nodes[node].data;
143 if(pd.size() < NumVerticesInLeaf)
144 {
145 pd.push_back(iPoint);
146 return;
147 }
148 // initialize bbox first time the root capacity is reached
149 if(!m_isRootBoxInitialized)
150 {
151 initializeRootBox(points);
152 min = m_min;
153 max = m_max;
154 }
155 // split a full leaf node
156 calcSplitInfo(min, max, dir, mid, newDir, newMin, newMax);
157 const node_index c1 = addNewNode(), c2 = addNewNode();
158 Node& n = m_nodes[node];
159 n.setChildren(c1, c2);
160 point_data_vec& c1data = m_nodes[c1].data;
161 point_data_vec& c2data = m_nodes[c2].data;
162 // move node's points to children
163 for(pd_cit it = n.data.begin(); it != n.data.end(); ++it)
164 {
165 whichChild(points[*it], mid, dir) == 0
166 ? c1data.push_back(*it)
167 : c2data.push_back(*it);
168 }
169 n.data = point_data_vec();
170 }
171 else
172 {
173 calcSplitInfo(min, max, dir, mid, newDir, newMin, newMax);
174 }
175 // add the point to a child
176 const std::size_t iChild = whichChild(points[iPoint], mid, dir);
177 iChild == 0 ? max = newMax : min = newMin;
178 node = m_nodes[node].children[iChild];
179 dir = newDir;
180 }
181 }
182
187 value_type nearest(
188 const point_type& point,
189 const std::vector<point_type>& points) const
190 {
191 value_type out;
192 int iTask = -1;
193 coord_type minDistSq = std::numeric_limits<coord_type>::max();
194 m_tasksStack[++iTask] = NearestTask(
195 m_root,
196 m_min,
197 m_max,
198 m_rootDir,
199 distanceSquaredToBox(point, m_min, m_max));
200 while(iTask != -1)
201 {
202 const NearestTask t = m_tasksStack[iTask--];
203 if(t.distSq > minDistSq)
204 continue;
205 const Node& n = m_nodes[t.node];
206 if(n.isLeaf())
207 {
208 for(pd_cit it = n.data.begin(); it != n.data.end(); ++it)
209 {
210 const point_type& p = points[*it];
211 const coord_type distSq = CDT::distanceSquared(point, p);
212 if(distSq < minDistSq)
213 {
214 minDistSq = distSq;
215 out.first = p;
216 out.second = *it;
217 }
218 }
219 }
220 else
221 {
222 coord_type mid(0);
223 NodeSplitDirection::Enum newDir(NodeSplitDirection::X);
224 point_type newMin = t.min, newMax = t.max;
225 coord_type dSqFarther = std::numeric_limits<coord_type>::max();
226 switch(t.dir)
227 {
228 case NodeSplitDirection::X:
229 {
230 mid = (t.min.x + t.max.x) / coord_type(2);
231 newDir = NodeSplitDirection::Y;
232 newMin.x = mid;
233 newMax.x = mid;
234 const coord_type dx = point.x - mid;
235 const coord_type dy = std::max(
236 std::max(t.min.y - point.y, coord_type(0)),
237 point.y - t.max.y);
238 dSqFarther = dx * dx + dy * dy;
239 break;
240 }
241 case NodeSplitDirection::Y:
242 {
243 mid = (t.min.y + t.max.y) / coord_type(2);
244 newDir = NodeSplitDirection::X;
245 newMin.y = mid;
246 newMax.y = mid;
247 const coord_type dx = std::max(
248 std::max(t.min.x - point.x, coord_type(0)),
249 point.x - t.max.x);
250 const coord_type dy = point.y - mid;
251 dSqFarther = dx * dx + dy * dy;
252 break;
253 }
254 }
255
256 if(iTask + 2 >= static_cast<int>(m_tasksStack.size()))
257 {
258 m_tasksStack.resize(
259 m_tasksStack.size() + StackDepthIncrement);
260 }
261
262 // put the closest node on top of the stack
263 if(isAfterSplit(point, mid, t.dir))
264 {
265 if(dSqFarther <= minDistSq)
266 {
267 m_tasksStack[++iTask] = NearestTask(
268 n.children[0], t.min, newMax, newDir, dSqFarther);
269 }
270 m_tasksStack[++iTask] = NearestTask(
271 n.children[1], newMin, t.max, newDir, t.distSq);
272 }
273 else
274 {
275 if(dSqFarther <= minDistSq)
276 {
277 m_tasksStack[++iTask] = NearestTask(
278 n.children[1], newMin, t.max, newDir, dSqFarther);
279 }
280 m_tasksStack[++iTask] = NearestTask(
281 n.children[0], t.min, newMax, newDir, t.distSq);
282 }
283 }
284 }
285 return out;
286 }
287
288private:
290 node_index addNewNode()
291 {
292 const node_index newNodeIndex = static_cast<node_index>(m_nodes.size());
293 m_nodes.push_back(Node());
294 return newNodeIndex;
295 }
296
297 static bool isAfterSplit(
298 const point_type& point,
299 const coord_type& split,
300 const NodeSplitDirection::Enum dir)
301 {
302 return dir == NodeSplitDirection::X ? point.x > split : point.y > split;
303 }
304
307 static std::size_t whichChild(
308 const point_type& point,
309 const coord_type& split,
310 const NodeSplitDirection::Enum dir)
311 {
312 return isAfterSplit(point, split, dir);
313 }
314
316 static void calcSplitInfo(
317 const point_type& min,
318 const point_type& max,
319 const NodeSplitDirection::Enum dir,
320 coord_type& midOut,
321 NodeSplitDirection::Enum& newDirOut,
322 point_type& newMinOut,
323 point_type& newMaxOut)
324 {
325 newMaxOut = max;
326 newMinOut = min;
327 switch(dir)
328 {
329 case NodeSplitDirection::X:
330 midOut = (min.x + max.x) / coord_type(2);
331 newDirOut = NodeSplitDirection::Y;
332 newMinOut.x = midOut;
333 newMaxOut.x = midOut;
334 return;
335 case NodeSplitDirection::Y:
336 midOut = (min.y + max.y) / coord_type(2);
337 newDirOut = NodeSplitDirection::X;
338 newMinOut.y = midOut;
339 newMaxOut.y = midOut;
340 return;
341 }
342 }
343
345 static bool isInsideBox(
346 const point_type& p,
347 const point_type& min,
348 const point_type& max)
349 {
350 return p.x >= min.x && p.x <= max.x && p.y >= min.y && p.y <= max.y;
351 }
352
355 void extendTree(const point_type& point)
356 {
357 const node_index newRoot = addNewNode();
358 const node_index newLeaf = addNewNode();
359 switch(m_rootDir)
360 {
361 case NodeSplitDirection::X:
362 m_rootDir = NodeSplitDirection::Y;
363 if(point.y < m_min.y)
364 {
365 m_min.y -= m_max.y - m_min.y;
366 m_nodes[newRoot].setChildren(newLeaf, m_root);
367 }
368 else
369 {
370 m_max.y += m_max.y - m_min.y;
371 m_nodes[newRoot].setChildren(m_root, newLeaf);
372 }
373 break;
374 case NodeSplitDirection::Y:
375 m_rootDir = NodeSplitDirection::X;
376 if(point.x < m_min.x)
377 {
378 m_min.x -= m_max.x - m_min.x;
379 m_nodes[newRoot].setChildren(newLeaf, m_root);
380 }
381 else
382 {
383 m_max.x += m_max.x - m_min.x;
384 m_nodes[newRoot].setChildren(m_root, newLeaf);
385 }
386 break;
387 }
388 m_root = newRoot;
389 }
390
392 void initializeRootBox(const std::vector<point_type>& points)
393 {
394 const point_data_vec& data = m_nodes[m_root].data;
395 m_min = points[data.front()];
396 m_max = m_min;
397 for(pd_cit it = data.begin(); it != data.end(); ++it)
398 {
399 const point_type& p = points[*it];
400 m_min = point_type(std::min(m_min.x, p.x), std::min(m_min.y, p.y));
401 m_max = point_type(std::max(m_max.x, p.x), std::max(m_max.y, p.y));
402 }
403 // Make sure bounding box does not have a zero size by adding padding:
404 // zero-size bounding box cannot be extended properly
405 const TCoordType padding(1);
406 if(m_min.x == m_max.x)
407 {
408 m_min.x -= padding;
409 m_max.x += padding;
410 }
411 if(m_min.y == m_max.y)
412 {
413 m_min.y -= padding;
414 m_max.y += padding;
415 }
416 m_isRootBoxInitialized = true;
417 }
418
419 static coord_type distanceSquaredToBox(
420 const point_type& p,
421 const point_type& min,
422 const point_type& max)
423 {
424 const coord_type dx =
425 std::max(std::max(min.x - p.x, coord_type(0)), p.x - max.x);
426 const coord_type dy =
427 std::max(std::max(min.y - p.y, coord_type(0)), p.y - max.y);
428 return dx * dx + dy * dy;
429 }
430
431private:
432 node_index m_root;
433 std::vector<Node> m_nodes;
434 NodeSplitDirection::Enum m_rootDir;
435 point_type m_min;
436 point_type m_max;
437 CDT::VertInd m_size;
438
439 bool m_isRootBoxInitialized;
440
441 // used for nearest query
442 struct NearestTask
443 {
444 node_index node;
445 point_type min, max;
446 NodeSplitDirection::Enum dir;
447 coord_type distSq;
448 NearestTask()
449 : dir(NodeSplitDirection::X)
450 {}
451 NearestTask(
452 const node_index node,
453 const point_type& min,
454 const point_type& max,
455 const NodeSplitDirection::Enum dir,
456 const coord_type distSq = std::numeric_limits<coord_type>::max())
457 : node(node)
458 , min(min)
459 , max(max)
460 , dir(dir)
461 , distSq(distSq)
462 {}
463 };
464 // allocated in class (not in the 'nearest' method) for better performance
465 mutable std::vector<NearestTask> m_tasksStack;
466};
467
468} // namespace KDTree
469} // namespace CDT
470
471#endif // header guard
Utilities and helpers.
KDTree()
Default constructor.
Definition KDTree.h:81
void insert(const point_index &iPoint, const std::vector< point_type > &points)
Insert a point into kd-tree.
Definition KDTree.h:118
value_type nearest(const point_type &point, const std::vector< point_type > &points) const
Query kd-tree for a nearest neighbor point.
Definition KDTree.h:187
KDTree(const point_type &min, const point_type &max)
Constructor with bounding box known in advance.
Definition KDTree.h:97
Namespace containing triangulation functionality.
IndexSizeType VertInd
Vertex index.
Definition CDTUtils.h:207
CDT_EXPORT T distanceSquared(const V2d< T > &a, const V2d< T > &b)
Squared distance between two 2D points.
Definition CDTUtils.hpp:256
Stores kd-tree node data.
Definition KDTree.h:58
point_data_vec data
points' data if leaf
Definition KDTree.h:60
bool isLeaf() const
Check if node is a leaf (has no valid children)
Definition KDTree.h:74
children_type children
two children if not leaf; {0,0} if leaf
Definition KDTree.h:59
void setChildren(const node_index c1, const node_index c2)
Children setter for convenience.
Definition KDTree.h:68
Node()
Create empty leaf.
Definition KDTree.h:62
2D vector
Definition CDTUtils.h:145
T y
Y-coordinate.
Definition CDTUtils.h:147
T x
X-coordinate.
Definition CDTUtils.h:146