ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
kdtree_index.h
Go to the documentation of this file.
1 /***********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
5  * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
6  *
7  * THE BSD LICENSE
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *************************************************************************/
30 
31 #ifndef FLANN_KDTREE_INDEX_H_
32 #define FLANN_KDTREE_INDEX_H_
33 
34 #include <algorithm>
35 #include <map>
36 #include <cassert>
37 #include <cstring>
38 #include <stdarg.h>
39 #include <cmath>
40 
41 #include "FLANN/general.h"
44 #include "FLANN/util/matrix.h"
45 #include "FLANN/util/result_set.h"
46 #include "FLANN/util/heap.h"
47 #include "FLANN/util/allocator.h"
48 #include "FLANN/util/random.h"
49 #include "FLANN/util/saving.h"
50 
51 
52 namespace flann
53 {
54 
56 {
57  KDTreeIndexParams(int trees = 4)
58  {
59  (*this)["algorithm"] = FLANN_INDEX_KDTREE;
60  (*this)["trees"] = trees;
61  }
62 };
63 
64 
71 template <typename Distance>
72 class KDTreeIndex : public NNIndex<Distance>
73 {
74 public:
75  typedef typename Distance::ElementType ElementType;
76  typedef typename Distance::ResultType DistanceType;
77 
79 
80  typedef bool needs_kdtree_distance;
81 
82 
91  BaseClass(params, d), mean_(NULL), var_(NULL)
92  {
93  trees_ = get_param(index_params_,"trees",4);
94  }
95 
96 
105  Distance d = Distance() ) : BaseClass(params,d ), mean_(NULL), var_(NULL)
106  {
107  trees_ = get_param(index_params_,"trees",4);
108 
109  setDataset(dataset);
110  }
111 
112  KDTreeIndex(const KDTreeIndex& other) : BaseClass(other),
113  trees_(other.trees_)
114  {
115  tree_roots_.resize(other.tree_roots_.size());
116  for (size_t i=0;i<tree_roots_.size();++i) {
117  copyTree(tree_roots_[i], other.tree_roots_[i]);
118  }
119  }
120 
122  {
123  this->swap(other);
124  return *this;
125  }
126 
130  virtual ~KDTreeIndex()
131  {
132  freeIndex();
133  }
134 
135  BaseClass* clone() const
136  {
137  return new KDTreeIndex(*this);
138  }
139 
140  using BaseClass::buildIndex;
141 
142  void addPoints(const Matrix<ElementType>& points, float rebuild_threshold = 2)
143  {
144  assert(points.cols==veclen_);
145 
146  size_t old_size = size_;
148 
149  if (rebuild_threshold>1 && size_at_build_*rebuild_threshold<size_) {
150  buildIndex();
151  }
152  else {
153  for (size_t i=old_size;i<size_;++i) {
154  for (int j = 0; j < trees_; j++) {
155  addPointToTree(tree_roots_[j], i);
156  }
157  }
158  }
159  }
160 
162  {
163  return FLANN_INDEX_KDTREE;
164  }
165 
166 
167  template<typename Archive>
168  void serialize(Archive& ar)
169  {
170  ar.setObject(this);
171 
172  ar & *static_cast<NNIndex<Distance>*>(this);
173 
174  ar & trees_;
175 
176  if (Archive::is_loading::value) {
177  tree_roots_.resize(trees_);
178  }
179  for (size_t i=0;i<tree_roots_.size();++i) {
180  if (Archive::is_loading::value) {
181  tree_roots_[i] = new(pool_) Node();
182  }
183  ar & *tree_roots_[i];
184  }
185 
186  if (Archive::is_loading::value) {
187  index_params_["algorithm"] = getType();
188  index_params_["trees"] = trees_;
189  }
190  }
191 
192 
193  void saveIndex(FILE* stream)
194  {
195  serialization::SaveArchive sa(stream);
196  sa & *this;
197  }
198 
199 
200  void loadIndex(FILE* stream)
201  {
202  freeIndex();
203  serialization::LoadArchive la(stream);
204  la & *this;
205  }
206 
211  int usedMemory() const
212  {
213  return int(pool_.usedMemory+pool_.wastedMemory+size_*sizeof(int)); // pool memory and vind array memory
214  }
215 
225  void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) const
226  {
227  int maxChecks = searchParams.checks;
228  float epsError = 1+searchParams.eps;
229 
230  if (maxChecks==FLANN_CHECKS_UNLIMITED) {
231  if (removed_) {
232  getExactNeighbors<true>(result, vec, epsError);
233  }
234  else {
235  getExactNeighbors<false>(result, vec, epsError);
236  }
237  }
238  else {
239  if (removed_) {
240  getNeighbors<true>(result, vec, maxChecks, epsError);
241  }
242  else {
243  getNeighbors<false>(result, vec, maxChecks, epsError);
244  }
245  }
246  }
247 
248 protected:
249 
254  {
255  // Create a permutable array of indices to the input vectors.
256  std::vector<int> ind(size_);
257  for (size_t i = 0; i < size_; ++i) {
258  ind[i] = int(i);
259  }
260 
261  mean_ = new DistanceType[veclen_];
262  var_ = new DistanceType[veclen_];
263 
264  tree_roots_.resize(trees_);
265  /* Construct the randomized trees. */
266  for (int i = 0; i < trees_; i++) {
267  /* Randomize the order of vectors to allow for unbiased sampling. */
268  auto rng = std::default_random_engine{};
269  std::shuffle(ind.begin(), ind.end(), rng);
270  tree_roots_[i] = divideTree(&ind[0], int(size_) );
271  }
272  delete[] mean_;
273  delete[] var_;
274  }
275 
276  void freeIndex()
277  {
278  for (size_t i=0;i<tree_roots_.size();++i) {
279  // using placement new, so call destructor explicitly
280  if (tree_roots_[i]!=NULL) tree_roots_[i]->~Node();
281  }
282  pool_.free();
283  }
284 
285 
286 private:
287 
288  /*--------------------- Internal Data Structures --------------------------*/
289  struct Node
290  {
294  int divfeat;
298  DistanceType divval;
306  Node* child1, *child2;
307  Node(){
308  child1 = NULL;
309  child2 = NULL;
310  }
311  ~Node() {
312  if (child1 != NULL) { child1->~Node(); child1 = NULL; }
313 
314  if (child2 != NULL) { child2->~Node(); child2 = NULL; }
315  }
316 
317  private:
318  template<typename Archive>
319  void serialize(Archive& ar)
320  {
321  typedef KDTreeIndex<Distance> Index;
322  Index* obj = static_cast<Index*>(ar.getObject());
323 
324  ar & divfeat;
325  ar & divval;
326 
327  bool leaf_node = false;
328  if (Archive::is_saving::value) {
329  leaf_node = ((child1==NULL) && (child2==NULL));
330  }
331  ar & leaf_node;
332 
333  if (leaf_node) {
334  if (Archive::is_loading::value) {
335  point = obj->points_[divfeat];
336  }
337  }
338 
339  if (!leaf_node) {
340  if (Archive::is_loading::value) {
341  child1 = new(obj->pool_) Node();
342  child2 = new(obj->pool_) Node();
343  }
344  ar & *child1;
345  ar & *child2;
346  }
347  }
348  friend struct serialization::access;
349  };
350  typedef Node* NodePtr;
351  typedef BranchStruct<NodePtr, DistanceType> BranchSt;
352  typedef BranchSt* Branch;
353 
354 
355  void copyTree(NodePtr& dst, const NodePtr& src)
356  {
357  dst = new(pool_) Node();
358  dst->divfeat = src->divfeat;
359  dst->divval = src->divval;
360  if (src->child1==NULL && src->child2==NULL) {
361  dst->point = points_[dst->divfeat];
362  dst->child1 = NULL;
363  dst->child2 = NULL;
364  }
365  else {
366  copyTree(dst->child1, src->child1);
367  copyTree(dst->child2, src->child2);
368  }
369  }
370 
380  NodePtr divideTree(int* ind, int count)
381  {
382  NodePtr node = new(pool_) Node(); // allocate memory
383 
384  /* If too few exemplars remain, then make this a leaf node. */
385  if (count == 1) {
386  node->child1 = node->child2 = NULL; /* Mark as leaf node. */
387  node->divfeat = *ind; /* Store index of this vec. */
388  node->point = points_[*ind];
389  }
390  else {
391  int idx;
392  int cutfeat;
393  DistanceType cutval;
394  meanSplit(ind, count, idx, cutfeat, cutval);
395 
396  node->divfeat = cutfeat;
397  node->divval = cutval;
398  node->child1 = divideTree(ind, idx);
399  node->child2 = divideTree(ind+idx, count-idx);
400  }
401 
402  return node;
403  }
404 
405 
411  void meanSplit(int* ind, int count, int& index, int& cutfeat, DistanceType& cutval)
412  {
413  memset(mean_,0,veclen_*sizeof(DistanceType));
414  memset(var_,0,veclen_*sizeof(DistanceType));
415 
416  /* Compute mean values. Only the first SAMPLE_MEAN values need to be
417  sampled to get a good estimate.
418  */
419  int cnt = std::min((int)SAMPLE_MEAN+1, count);
420  for (int j = 0; j < cnt; ++j) {
421  ElementType* v = points_[ind[j]];
422  for (size_t k=0; k<veclen_; ++k) {
423  mean_[k] += v[k];
424  }
425  }
426  DistanceType div_factor = DistanceType(1)/cnt;
427  for (size_t k=0; k<veclen_; ++k) {
428  mean_[k] *= div_factor;
429  }
430 
431  /* Compute variances (no need to divide by count). */
432  for (int j = 0; j < cnt; ++j) {
433  ElementType* v = points_[ind[j]];
434  for (size_t k=0; k<veclen_; ++k) {
435  DistanceType dist = v[k] - mean_[k];
436  var_[k] += dist * dist;
437  }
438  }
439  /* Select one of the highest variance indices at random. */
440  cutfeat = selectDivision(var_);
441  cutval = mean_[cutfeat];
442 
443  int lim1, lim2;
444  planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
445 
446  if (lim1>count/2) index = lim1;
447  else if (lim2<count/2) index = lim2;
448  else index = count/2;
449 
450  /* If either list is empty, it means that all remaining features
451  * are identical. Split in the middle to maintain a balanced tree.
452  */
453  if ((lim1==count)||(lim2==0)) index = count/2;
454  }
455 
456 
461  int selectDivision(DistanceType* v)
462  {
463  int num = 0;
464  size_t topind[RAND_DIM];
465 
466  /* Create a list of the indices of the top RAND_DIM values. */
467  for (size_t i = 0; i < veclen_; ++i) {
468  if ((num < RAND_DIM)||(v[i] > v[topind[num-1]])) {
469  /* Put this element at end of topind. */
470  if (num < RAND_DIM) {
471  topind[num++] = i; /* Add to list. */
472  }
473  else {
474  topind[num-1] = i; /* Replace last element. */
475  }
476  /* Bubble end value down to right location by repeated swapping. */
477  int j = num - 1;
478  while (j > 0 && v[topind[j]] > v[topind[j-1]]) {
479  std::swap(topind[j], topind[j-1]);
480  --j;
481  }
482  }
483  }
484  /* Select a random integer in range [0,num-1], and return that index. */
485  int rnd = rand_int(num);
486  return (int)topind[rnd];
487  }
488 
489 
499  void planeSplit(int* ind, int count, int cutfeat, DistanceType cutval, int& lim1, int& lim2)
500  {
501  /* Move vector indices for left subtree to front of list. */
502  int left = 0;
503  int right = count-1;
504  for (;; ) {
505  while (left<=right && points_[ind[left]][cutfeat]<cutval) ++left;
506  while (left<=right && points_[ind[right]][cutfeat]>=cutval) --right;
507  if (left>right) break;
508  std::swap(ind[left], ind[right]); ++left; --right;
509  }
510  lim1 = left;
511  right = count-1;
512  for (;; ) {
513  while (left<=right && points_[ind[left]][cutfeat]<=cutval) ++left;
514  while (left<=right && points_[ind[right]][cutfeat]>cutval) --right;
515  if (left>right) break;
516  std::swap(ind[left], ind[right]); ++left; --right;
517  }
518  lim2 = left;
519  }
520 
525  template<bool with_removed>
526  void getExactNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, float epsError) const
527  {
528  // checkID -= 1; /* Set a different unique ID for each search. */
529 
530  if (trees_ > 1) {
531  fprintf(stderr,"It doesn't make any sense to use more than one tree for exact search");
532  }
533  if (trees_>0) {
534  searchLevelExact<with_removed>(result, vec, tree_roots_[0], 0.0, epsError);
535  }
536  }
537 
543  template<bool with_removed>
544  void getNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, int maxCheck, float epsError) const
545  {
546  int i;
547  BranchSt branch;
548 
549  int checkCount = 0;
550  Heap<BranchSt>* heap = new Heap<BranchSt>((int)size_);
551  DynamicBitset checked(size_);
552 
553  /* Search once through each tree down to root. */
554  for (i = 0; i < trees_; ++i) {
555  searchLevel<with_removed>(result, vec, tree_roots_[i], 0, checkCount, maxCheck, epsError, heap, checked);
556  }
557 
558  /* Keep searching other branches from heap until finished. */
559  while ( heap->popMin(branch) && (checkCount < maxCheck || !result.full() )) {
560  searchLevel<with_removed>(result, vec, branch.node, branch.mindist, checkCount, maxCheck, epsError, heap, checked);
561  }
562 
563  delete heap;
564 
565  }
566 
572  template<bool with_removed>
573  void searchLevel(ResultSet<DistanceType>& result_set, const ElementType* vec, NodePtr node, DistanceType mindist, int& checkCount, int maxCheck,
574  float epsError, Heap<BranchSt>* heap, DynamicBitset& checked) const
575  {
576  if (result_set.worstDist()<mindist) {
577  // printf("Ignoring branch, too far\n");
578  return;
579  }
580 
581  /* If this is a leaf node, then do check and return. */
582  if ((node->child1 == NULL)&&(node->child2 == NULL)) {
583  int index = node->divfeat;
584  if (with_removed) {
585  if (removed_points_.test(index)) return;
586  }
587  /* Do not check same node more than once when searching multiple trees. */
588  if ( checked.test(index) || ((checkCount>=maxCheck)&& result_set.full()) ) return;
589  checked.set(index);
590  checkCount++;
591 
592  DistanceType dist = distance_(node->point, vec, veclen_);
593  result_set.addPoint(dist,index);
594  return;
595  }
596 
597  /* Which child branch should be taken first? */
598  ElementType val = vec[node->divfeat];
599  DistanceType diff = val - node->divval;
600  NodePtr bestChild = (diff < 0) ? node->child1 : node->child2;
601  NodePtr otherChild = (diff < 0) ? node->child2 : node->child1;
602 
603  /* Create a branch record for the branch not taken. Add distance
604  of this feature boundary (we don't attempt to correct for any
605  use of this feature in a parent node, which is unlikely to
606  happen and would have only a small effect). Don't bother
607  adding more branches to heap after halfway point, as cost of
608  adding exceeds their value.
609  */
610 
611  DistanceType new_distsq = mindist + distance_.accum_dist(val, node->divval, node->divfeat);
612  // if (2 * checkCount < maxCheck || !result.full()) {
613  if ((new_distsq*epsError < result_set.worstDist())|| !result_set.full()) {
614  heap->insert( BranchSt(otherChild, new_distsq) );
615  }
616 
617  /* Call recursively to search next level down. */
618  searchLevel<with_removed>(result_set, vec, bestChild, mindist, checkCount, maxCheck, epsError, heap, checked);
619  }
620 
624  template<bool with_removed>
625  void searchLevelExact(ResultSet<DistanceType>& result_set, const ElementType* vec, const NodePtr node, DistanceType mindist, const float epsError) const
626  {
627  /* If this is a leaf node, then do check and return. */
628  if ((node->child1 == NULL)&&(node->child2 == NULL)) {
629  int index = node->divfeat;
630  if (with_removed) {
631  if (removed_points_.test(index)) return; // ignore removed points
632  }
633  DistanceType dist = distance_(node->point, vec, veclen_);
634  result_set.addPoint(dist,index);
635 
636  return;
637  }
638 
639  /* Which child branch should be taken first? */
640  ElementType val = vec[node->divfeat];
641  DistanceType diff = val - node->divval;
642  NodePtr bestChild = (diff < 0) ? node->child1 : node->child2;
643  NodePtr otherChild = (diff < 0) ? node->child2 : node->child1;
644 
645  /* Create a branch record for the branch not taken. Add distance
646  of this feature boundary (we don't attempt to correct for any
647  use of this feature in a parent node, which is unlikely to
648  happen and would have only a small effect). Don't bother
649  adding more branches to heap after halfway point, as cost of
650  adding exceeds their value.
651  */
652 
653  DistanceType new_distsq = mindist + distance_.accum_dist(val, node->divval, node->divfeat);
654 
655  /* Call recursively to search next level down. */
656  searchLevelExact<with_removed>(result_set, vec, bestChild, mindist, epsError);
657 
658  if (mindist*epsError<=result_set.worstDist()) {
659  searchLevelExact<with_removed>(result_set, vec, otherChild, new_distsq, epsError);
660  }
661  }
662 
663  void addPointToTree(NodePtr node, int ind)
664  {
665  ElementType* point = points_[ind];
666 
667  if ((node->child1==NULL) && (node->child2==NULL)) {
668  ElementType* leaf_point = node->point;
669  ElementType max_span = 0;
670  size_t div_feat = 0;
671  for (size_t i=0;i<veclen_;++i) {
672  ElementType span = std::abs(point[i]-leaf_point[i]);
673  if (span > max_span) {
674  max_span = span;
675  div_feat = i;
676  }
677  }
678  NodePtr left = new(pool_) Node();
679  left->child1 = left->child2 = NULL;
680  NodePtr right = new(pool_) Node();
681  right->child1 = right->child2 = NULL;
682 
683  if (point[div_feat]<leaf_point[div_feat]) {
684  left->divfeat = ind;
685  left->point = point;
686  right->divfeat = node->divfeat;
687  right->point = node->point;
688  }
689  else {
690  left->divfeat = node->divfeat;
691  left->point = node->point;
692  right->divfeat = ind;
693  right->point = point;
694  }
695  node->divfeat = div_feat;
696  node->divval = (point[div_feat]+leaf_point[div_feat])/2;
697  node->child1 = left;
698  node->child2 = right;
699  }
700  else {
701  if (point[node->divfeat]<node->divval) {
702  addPointToTree(node->child1,ind);
703  }
704  else {
705  addPointToTree(node->child2,ind);
706  }
707  }
708  }
709 private:
710  void swap(KDTreeIndex& other)
711  {
712  BaseClass::swap(other);
713  std::swap(trees_, other.trees_);
714  std::swap(tree_roots_, other.tree_roots_);
715  std::swap(pool_, other.pool_);
716  }
717 
718 private:
719 
720  enum
721  {
727  SAMPLE_MEAN = 100,
735  RAND_DIM=5
736  };
737 
738 
742  int trees_;
743 
744  DistanceType* mean_;
745  DistanceType* var_;
746 
750  std::vector<NodePtr> tree_roots_;
751 
759  PooledAllocator pool_;
760 
762 }; // class KDTreeIndex
763 
764 }
765 
766 #endif //FLANN_KDTREE_INDEX_H_
int count
int points
double Distance(const Point3D< Real > &p1, const Point3D< Real > &p2)
cmdLineReadable * params[]
#define NULL
core::Tensor result
Definition: VtkUtils.cpp:76
Generic handle to any of the 8 types of E57 element objects.
bool test(size_t index) const
Distance::ElementType ElementType
Definition: kdtree_index.h:75
void addPoints(const Matrix< ElementType > &points, float rebuild_threshold=2)
Incrementally add points to the index.
Definition: kdtree_index.h:142
KDTreeIndex(const Matrix< ElementType > &dataset, const IndexParams &params=KDTreeIndexParams(), Distance d=Distance())
Definition: kdtree_index.h:104
KDTreeIndex & operator=(KDTreeIndex other)
Definition: kdtree_index.h:121
void findNeighbors(ResultSet< DistanceType > &result, const ElementType *vec, const SearchParams &searchParams) const
Definition: kdtree_index.h:225
flann_algorithm_t getType() const
Definition: kdtree_index.h:161
BaseClass * clone() const
Definition: kdtree_index.h:135
Distance::ResultType DistanceType
Definition: kdtree_index.h:76
void loadIndex(FILE *stream)
Definition: kdtree_index.h:200
void saveIndex(FILE *stream)
Definition: kdtree_index.h:193
KDTreeIndex(const IndexParams &params=KDTreeIndexParams(), Distance d=Distance())
Definition: kdtree_index.h:90
NNIndex< Distance > BaseClass
Definition: kdtree_index.h:78
virtual void buildIndex()
Definition: nn_index.h:125
void serialize(Archive &ar)
Definition: kdtree_index.h:168
virtual ~KDTreeIndex()
Definition: kdtree_index.h:130
KDTreeIndex(const KDTreeIndex &other)
Definition: kdtree_index.h:112
int usedMemory() const
Definition: kdtree_index.h:211
std::vector< ElementType * > points_
Definition: nn_index.h:876
size_t veclen_
Definition: nn_index.h:846
void swap(NNIndex &other)
Definition: nn_index.h:802
DynamicBitset removed_points_
Definition: nn_index.h:861
size_t size_
Definition: nn_index.h:836
void setDataset(const Matrix< ElementType > &dataset)
Definition: nn_index.h:746
Distance distance_
Definition: nn_index.h:823
virtual void buildIndex()
Definition: nn_index.h:125
void extendDataset(const Matrix< ElementType > &new_points)
Definition: nn_index.h:763
IndexParams index_params_
Definition: nn_index.h:851
size_t size_at_build_
Definition: nn_index.h:841
int min(int a, int b)
Definition: cutil_math.h:53
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
@ FLANN_CHECKS_UNLIMITED
Definition: defines.h:147
flann_algorithm_t
Definition: defines.h:80
@ FLANN_INDEX_KDTREE
Definition: defines.h:82
static double dist(double x1, double y1, double x2, double y2)
Definition: lsd.c:207
void swap(optional< T > &x, optional< T > &y) noexcept(noexcept(x.swap(y)))
Definition: Optional.h:890
T get_param(const IndexParams &params, std::string name, const T &default_value)
Definition: params.h:95
int rand_int(int high=RAND_MAX, int low=0)
Definition: random.h:74
std::map< std::string, any > IndexParams
Definition: params.h:51
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
#define USING_BASECLASS_SYMBOLS
Definition: nn_index.h:887
struct Index Index
Definition: sqlite3.c:14646
KDTreeIndexParams(int trees=4)
Definition: kdtree_index.h:57
Definition: lsd.c:149