From Ferdinand Mitterbauer: "Building a project using libkdtree++ using
[libkdtree/libkdtree.git] / examples / test_kdtree.cpp
1 #define KDTREE_DEFINE_OSTREAM_OPERATORS
3 // Make SURE all our asserts() are checked
4 #undef NDEBUG
6 #include <kdtree++/kdtree.hpp>
8 #include <deque>
9 #include <iostream>
10 #include <vector>
11 #include <limits>
12 #include <functional>
13 #include <set>
15 // used to ensure all triplets that are accessed via the operator<< are initialised.
16 std::set<const void*> registered;
18 struct triplet
19 {
20   typedef int value_type;
22   triplet(value_type a, value_type b, value_type c)
23   {
24     d[0] = a;
25     d[1] = b;
26     d[2] = c;
27     bool reg_ok = (registered.find(this) == registered.end());
28     assert(reg_ok);
29     registered.insert(this).second;
30   }
32   triplet(const triplet & x)
33   {
34     d[0] = x.d[0];
35     d[1] = x.d[1];
36     d[2] = x.d[2];
37     bool reg_ok = (registered.find(this) == registered.end());
38     assert(reg_ok);
39     registered.insert(this).second;
40   }
42   ~triplet()
43   {
44     bool unreg_ok = (registered.find(this) != registered.end());
45     assert(unreg_ok);
46     registered.erase(this);
47   }
49   double distance_to(triplet const& x) const
50   {
51      double dist = 0;
52      for (int i = 0; i != 3; ++i)
53         dist += (d[i]-x.d[i])*(d[i]-x.d[i]);
54      return std::sqrt(dist);
55   }
57   inline value_type operator[](size_t const N) const { return d[N]; }
59   value_type d[3];
60 };
64 // same as triplet, except with the values reversed.
65 struct alternate_triplet
66 {
67   typedef int value_type;
69   alternate_triplet(const triplet & x)
70   {
71     d[0] = x.d[2];
72     d[1] = x.d[1];
73     d[2] = x.d[0];
74   }
76   inline value_type operator[](size_t const N) const { return d[2-N]; }
78   value_type d[3];
79 };
81 inline bool operator==(triplet const& A, triplet const& B) {
82   return A.d[0] == B.d[0] && A.d[1] == B.d[1] && A.d[2] == B.d[2];
83 }
85 std::ostream& operator<<(std::ostream& out, triplet const& T)
86 {
87   assert(registered.find(&T) != registered.end());
88   return out << '(' << T.d[0] << ',' << T.d[1] << ',' << T.d[2] << ')';
89 }
91 inline double tac( triplet t, size_t k ) { return t[k]; }
93 // use tac as a class instead of a function,
94 // can access more than one type with just 1 definition.
95 struct alternate_tac
96 {
97    typedef double result_type;
98    double operator()( triplet const& t, size_t k ) const { return t[k]; }
99    double operator()( alternate_triplet const& t, size_t k ) const { return t[k]; }
100 };
103 typedef KDTree::KDTree<3, triplet, std::pointer_to_binary_function<triplet,size_t,double> > tree_type;
105 struct Predicate
107    bool operator()( triplet const& t ) const
108    {
109       return t[0] > 3;  // anything, we are currently testing that it compiles.
110    }
111 };
113 // never finds anything
114 struct FalsePredicate
116    bool operator()( triplet const& t ) const { return false; }
117 };
119 int main()
121    // check that it'll find nodes exactly MAX away
122    {
123       tree_type exact_dist(std::ptr_fun(tac));
124         triplet c0(5, 4, 0);
125         exact_dist.insert(c0);
126         triplet target(7,4,0);
128       std::pair<tree_type::const_iterator,double> found = exact_dist.find_nearest(target,2);
129       assert(found.first != exact_dist.end());
130       assert(found.second == 2);
131       std::cout << "Test find_nearest(), found at exact distance away from " << target << ", found " << *found.first << std::endl;
132    }
134    // do the same test, except use alternate_triplet as the search key
135    {
136       // NOTE: stores triplet, but we search with alternate_triplet
137       typedef KDTree::KDTree<3, triplet, alternate_tac> alt_tree;
139       triplet actual_target(7,0,0);
141       alt_tree tree;
142       tree.insert( triplet(0, 0, 7) );
143       tree.insert( triplet(0, 0, 7) );
144       tree.insert( triplet(0, 0, 7) );
145       tree.insert( triplet(3, 0, 0) );
146       tree.insert( actual_target );
147       tree.optimise();
149       alternate_triplet target( actual_target );
151       std::pair<alt_tree::const_iterator,double> found = tree.find_nearest(target);
152       assert(found.first != tree.end());
153       std::cout << "Test with alternate search type, found: " << *found.first << ", wanted " << actual_target << std::endl;
154       assert(found.second == 0);
155       assert(*found.first == actual_target);
156    }
159    {
160       tree_type exact_dist(std::ptr_fun(tac));
161         triplet c0(5, 2, 0);
162         exact_dist.insert(c0);
163         triplet target(7,4,0);
165         // call find_nearest without a range value - it found a compile error earlier.
166       std::pair<tree_type::const_iterator,double> found = exact_dist.find_nearest(target);
167       assert(found.first != exact_dist.end());
168       std::cout << "Test find_nearest(), found at exact distance away from " << target << ", found " << *found.first << " @ " << found.second << " should be " << std::sqrt(8) << std::endl;
169       assert(found.second == std::sqrt(8));
170    }
172    {
173       tree_type exact_dist(std::ptr_fun(tac));
174         triplet c0(5, 2, 0);
175         exact_dist.insert(c0);
176         triplet target(7,4,0);
178       std::pair<tree_type::const_iterator,double> found = exact_dist.find_nearest(target,std::sqrt(8));
179       assert(found.first != exact_dist.end());
180       std::cout << "Test find_nearest(), found at exact distance away from " << target << ", found " << *found.first << " @ " << found.second << " should be " << std::sqrt(8) << std::endl;
181       assert(found.second == std::sqrt(8));
182    }
184   tree_type src(std::ptr_fun(tac));
186   triplet c0(5, 4, 0); src.insert(c0);
187   triplet c1(4, 2, 1); src.insert(c1);
188   triplet c2(7, 6, 9); src.insert(c2);
189   triplet c3(2, 2, 1); src.insert(c3);
190   triplet c4(8, 0, 5); src.insert(c4);
191   triplet c5(5, 7, 0); src.insert(c5);
192   triplet c6(3, 3, 8); src.insert(c6);
193   triplet c7(9, 7, 3); src.insert(c7);
194   triplet c8(2, 2, 6); src.insert(c8);
195   triplet c9(2, 0, 6); src.insert(c9);
197   std::cout << src << std::endl;
199   src.erase(c0);
200   src.erase(c1);
201   src.erase(c3);
202   src.erase(c5);
204   src.optimise();
207   // test the efficient_replace_and_optimise()
208   tree_type eff_repl = src;
209   {
210      std::vector<triplet> vec;
211      // erased above as part of test vec.push_back(triplet(5, 4, 0));
212      // erased above as part of test vec.push_back(triplet(4, 2, 1));
213      vec.push_back(triplet(7, 6, 9));
214      // erased above as part of test vec.push_back(triplet(2, 2, 1));
215      vec.push_back(triplet(8, 0, 5));
216      // erased above as part of test vec.push_back(triplet(5, 7, 0));
217      vec.push_back(triplet(3, 3, 8));
218      vec.push_back(triplet(9, 7, 3));
219      vec.push_back(triplet(2, 2, 6));
220      vec.push_back(triplet(2, 0, 6));
222      eff_repl.clear();
223      eff_repl.efficient_replace_and_optimise(vec);
224   }
227   std::cout << std::endl << src << std::endl;
229   tree_type copied(src);
230   std::cout << copied << std::endl;
231   tree_type assigned;
232   assigned = src;
233   std::cout << assigned << std::endl;
235   for (int loop = 0; loop != 4; ++loop)
236     {
237       tree_type * target;
238       switch (loop)
239         {
240         case 0: std::cout << "Testing plain construction" << std::endl;
241           target = &src;
242           break;
244         case 1: std::cout << "Testing copy-construction" << std::endl;
245           target = &copied;
246           break;
248         case 2: std::cout << "Testing assign-construction" << std::endl;
249           target = &assigned;
250           break;
252    default:
253         case 4: std::cout << "Testing efficient-replace-and-optimise" << std::endl;
254           target = &eff_repl;
255           break;
256         }
257       tree_type & t = *target;
259       int i=0;
260       for (tree_type::const_iterator iter=t.begin(); iter!=t.end(); ++iter, ++i);
261       std::cout << "iterator walked through " << i << " nodes in total" << std::endl;
262       if (i!=6)
263         {
264           std::cerr << "Error: does not tally with the expected number of nodes (6)" << std::endl;
265           return 1;
266         }
267       i=0;
268       for (tree_type::const_reverse_iterator iter=t.rbegin(); iter!=t.rend(); ++iter, ++i);
269       std::cout << "reverse_iterator walked through " << i << " nodes in total" << std::endl;
270       if (i!=6)
271         {
272           std::cerr << "Error: does not tally with the expected number of nodes (6)" << std::endl;
273           return 1;
274         }
276       triplet s(5, 4, 3);
277       std::vector<triplet> v;
278       unsigned int const RANGE = 3;
280       size_t count = t.count_within_range(s, RANGE);
281       std::cout << "counted " << count
282                 << " nodes within range " << RANGE << " of " << s << ".\n";
283       t.find_within_range(s, RANGE, std::back_inserter(v));
285       std::cout << "found   " << v.size() << " nodes within range " << RANGE
286                 << " of " << s << ":\n";
287       std::vector<triplet>::const_iterator ci = v.begin();
288       for (; ci != v.end(); ++ci)
289         std::cout << *ci << " ";
290       std::cout << "\n" << std::endl;
292       std::cout << std::endl << t << std::endl;
294       // search for all the nodes at exactly 0 dist away
295       for (tree_type::const_iterator target = t.begin(); target != t.end(); ++target)
296       {
297          std::pair<tree_type::const_iterator,double> found = t.find_nearest(*target,0);
298          assert(found.first != t.end());
299          assert(*found.first == *target);
300          std::cout << "Test find_nearest(), found at exact distance away from " << *target << ", found " << *found.first << std::endl;
301       }
303       {
304          const double small_dist = 0.0001;
305          std::pair<tree_type::const_iterator,double> notfound = t.find_nearest(s,small_dist);
306          std::cout << "Test find_nearest(), nearest to " << s << " within " << small_dist << " should not be found" << std::endl;
308          if (notfound.first != t.end())
309          {
310             std::cout << "ERROR found a node at dist " << notfound.second << " : " << *notfound.first << std::endl;
311             std::cout << "Actual distance = " << s.distance_to(*notfound.first) << std::endl;
312          }
314          assert(notfound.first == t.end());
315       }
317       {
318          std::pair<tree_type::const_iterator,double> nif = t.find_nearest_if(s,std::numeric_limits<double>::max(),Predicate());
319          std::cout << "Test find_nearest_if(), nearest to " << s << " @ " << nif.second << ": " << *nif.first << std::endl;
321          std::pair<tree_type::const_iterator,double> cantfind = t.find_nearest_if(s,std::numeric_limits<double>::max(),FalsePredicate());
322          std::cout << "Test find_nearest_if(), nearest to " << s << " should never be found (predicate too strong)" << std::endl;
323          assert(cantfind.first == t.end());
324       }
329       {
330       std::pair<tree_type::const_iterator,double> found = t.find_nearest(s,std::numeric_limits<double>::max());
331       std::cout << "Nearest to " << s << " @ " << found.second << " " << *found.first << std::endl;
332       std::cout << "Should be " << found.first->distance_to(s) << std::endl;
333       // NOTE: the assert does not check for an exact match, as it is not exact when -O2 or -O3 is
334       // switched on.  Some sort of optimisation makes the math inexact.
335       assert( fabs(found.second - found.first->distance_to(s)) < std::numeric_limits<double>::epsilon() );
336       }
338       {
339       triplet s2(10, 10, 2);
340       std::pair<tree_type::const_iterator,double> found = t.find_nearest(s2,std::numeric_limits<double>::max());
341       std::cout << "Nearest to " << s2 << " @ " << found.second << " " << *found.first << std::endl;
342       std::cout << "Should be " << found.first->distance_to(s2) << std::endl;
343       // NOTE: the assert does not check for an exact match, as it is not exact when -O2 or -O3 is
344       // switched on.  Some sort of optimisation makes the math inexact.
345       assert( fabs(found.second - found.first->distance_to(s2)) < std::numeric_limits<double>::epsilon() );
346       }
348       std::cout << std::endl;
350       std::cout << t << std::endl;
352       // Testing iterators
353       {
354         std::cout << "Testing iterators" << std::endl;
356         t.erase(c2);
357         t.erase(c4);
358         t.erase(c6);
359         t.erase(c7);
360         t.erase(c8);
361         //    t.erase(c9);
363         std::cout << std::endl << t << std::endl;
365         std::cout << "Forward iterator test..." << std::endl;
366         std::vector<triplet> forwards;
367         for (tree_type::iterator i = t.begin(); i != t.end(); ++i)
368           { std::cout << *i << " " << std::flush; forwards.push_back(*i); }
369         std::cout << std::endl;
370         std::cout << "Reverse iterator test..." << std::endl;
371         std::vector<triplet> backwards;
372         for (tree_type::reverse_iterator i = t.rbegin(); i != t.rend(); ++i)
373           { std::cout << *i << " " << std::flush; backwards.push_back(*i); }
374         std::cout << std::endl;
375         std::reverse(backwards.begin(),backwards.end());
376         assert(backwards == forwards);
377       }
378     }
381   // Walter reported that the find_within_range() wasn't giving results that were within
382   // the specified range... this is the test.
383   {
384      tree_type tree(std::ptr_fun(tac));
385      tree.insert( triplet(28.771200,16.921600,-2.665970) );
386      tree.insert( triplet(28.553101,18.649700,-2.155560) );
387      tree.insert( triplet(28.107500,20.341400,-1.188940) );
388      tree.optimise();
390      std::deque< triplet > vectors;
391      triplet sv(18.892500,20.341400,-1.188940);
392      tree.find_within_range(sv, 10.0f, std::back_inserter(vectors));
394      std::cout << std::endl << "Test find_with_range( " << sv << ", 10.0f) found " << vectors.size() << " candidates." << std::endl;
396      // double-check the ranges
397      for (std::deque<triplet>::iterator v = vectors.begin(); v != vectors.end(); ++v)
398      {
399         double dist = sv.distance_to(*v);
400         std::cout << "  " << *v << " dist=" << dist << std::endl;
401         if (dist > 10.0f)
402            std::cout << "    This point is too far! But that is by design, its within a 'box' with a 'radius' of 10, not a sphere with a radius of 10" << std::endl;
403         // Not a valid test, it can be greater than 10 if the point is in the corners of the box.
404         // assert(dist <= 10.0f);
405      }
406   }
409   return 0;
412 /* COPYRIGHT --
413  *
414  * This file is part of libkdtree++, a C++ template KD-Tree sorting container.
415  * libkdtree++ is (c) 2004-2007 Martin F. Krafft <libkdtree@pobox.madduck.net>
416  * and Sylvain Bougerel <sylvain.bougerel.devel@gmail.com> distributed under the
417  * terms of the Artistic License 2.0. See the ./COPYING file in the source tree
418  * root for more information.
419  *
420  * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
421  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES
422  * OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
423  */