From 04ea29528040ec1e85c04d1be1db27a94f29a472 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 14 Nov 2017 11:37:30 +0100 Subject: [PATCH] add kd tree class add mesh decimation algorithm --- src/3rdParty/libkdtree/AUTHORS | 3 + src/3rdParty/libkdtree/CMakeLists.txt | 37 + src/3rdParty/libkdtree/COPYING | 176 +++ src/3rdParty/libkdtree/ChangeLog | 236 ++++ src/3rdParty/libkdtree/INSTALL | 234 ++++ src/3rdParty/libkdtree/README | 121 ++ src/3rdParty/libkdtree/README.Python | 12 + .../libkdtree/examples/CMakeLists.txt | 3 + .../examples/test_find_within_range.cpp | 108 ++ .../libkdtree/examples/test_hayne.cpp | 116 ++ .../libkdtree/examples/test_kdtree.cpp | 423 ++++++ src/3rdParty/libkdtree/kdtree++/allocator.hpp | 96 ++ src/3rdParty/libkdtree/kdtree++/function.hpp | 89 ++ src/3rdParty/libkdtree/kdtree++/iterator.hpp | 258 ++++ src/3rdParty/libkdtree/kdtree++/kdtree.hpp | 1247 +++++++++++++++++ src/3rdParty/libkdtree/kdtree++/node.hpp | 362 +++++ src/3rdParty/libkdtree/kdtree++/region.hpp | 131 ++ .../libkdtree/python-bindings/CMakeLists.txt | 19 + .../libkdtree/python-bindings/gen-swig-hpp.py | 264 ++++ .../python-bindings/py-kdtree.hpp.tmpl | 145 ++ .../python-bindings/py-kdtree.i.tmpl | 27 + .../python-bindings/py-kdtree_test.cpp | 96 ++ .../python-bindings/py-kdtree_test.py | 400 ++++++ src/Mod/Mesh/App/CMakeLists.txt | 6 +- src/Mod/Mesh/App/Core/Decimation.cpp | 97 ++ src/Mod/Mesh/App/Core/Decimation.h | 46 + src/Mod/Mesh/App/Core/KDTree.cpp | 155 ++ src/Mod/Mesh/App/Core/KDTree.h | 60 + src/Mod/Mesh/App/Core/Simplify.h | 518 +++++++ src/Mod/Mesh/App/Mesh.cpp | 7 + src/Mod/Mesh/App/Mesh.h | 1 + src/Mod/Mesh/App/MeshPy.xml | 13 + src/Mod/Mesh/App/MeshPyImp.cpp | 63 +- src/Mod/Mesh/Gui/MeshSelection.cpp | 2 +- src/Mod/Test/Gui/CMakeLists.txt | 2 + 35 files changed, 5546 insertions(+), 27 deletions(-) create mode 100644 src/3rdParty/libkdtree/AUTHORS create mode 100644 src/3rdParty/libkdtree/CMakeLists.txt create mode 100644 src/3rdParty/libkdtree/COPYING create mode 100644 src/3rdParty/libkdtree/ChangeLog create mode 100644 src/3rdParty/libkdtree/INSTALL create mode 100644 src/3rdParty/libkdtree/README create mode 100644 src/3rdParty/libkdtree/README.Python create mode 100644 src/3rdParty/libkdtree/examples/CMakeLists.txt create mode 100644 src/3rdParty/libkdtree/examples/test_find_within_range.cpp create mode 100644 src/3rdParty/libkdtree/examples/test_hayne.cpp create mode 100644 src/3rdParty/libkdtree/examples/test_kdtree.cpp create mode 100644 src/3rdParty/libkdtree/kdtree++/allocator.hpp create mode 100644 src/3rdParty/libkdtree/kdtree++/function.hpp create mode 100644 src/3rdParty/libkdtree/kdtree++/iterator.hpp create mode 100644 src/3rdParty/libkdtree/kdtree++/kdtree.hpp create mode 100644 src/3rdParty/libkdtree/kdtree++/node.hpp create mode 100644 src/3rdParty/libkdtree/kdtree++/region.hpp create mode 100644 src/3rdParty/libkdtree/python-bindings/CMakeLists.txt create mode 100644 src/3rdParty/libkdtree/python-bindings/gen-swig-hpp.py create mode 100644 src/3rdParty/libkdtree/python-bindings/py-kdtree.hpp.tmpl create mode 100644 src/3rdParty/libkdtree/python-bindings/py-kdtree.i.tmpl create mode 100644 src/3rdParty/libkdtree/python-bindings/py-kdtree_test.cpp create mode 100644 src/3rdParty/libkdtree/python-bindings/py-kdtree_test.py create mode 100644 src/Mod/Mesh/App/Core/Decimation.cpp create mode 100644 src/Mod/Mesh/App/Core/Decimation.h create mode 100644 src/Mod/Mesh/App/Core/KDTree.cpp create mode 100644 src/Mod/Mesh/App/Core/KDTree.h create mode 100644 src/Mod/Mesh/App/Core/Simplify.h diff --git a/src/3rdParty/libkdtree/AUTHORS b/src/3rdParty/libkdtree/AUTHORS new file mode 100644 index 0000000000..d63b3c9e9d --- /dev/null +++ b/src/3rdParty/libkdtree/AUTHORS @@ -0,0 +1,3 @@ +Martin F. Krafft +Paul Harris +Sylvain Bougerel diff --git a/src/3rdParty/libkdtree/CMakeLists.txt b/src/3rdParty/libkdtree/CMakeLists.txt new file mode 100644 index 0000000000..a84b167ccd --- /dev/null +++ b/src/3rdParty/libkdtree/CMakeLists.txt @@ -0,0 +1,37 @@ +project (libkdtree CXX) +cmake_minimum_required (VERSION 2.6.0) + +option (BUILD_PYTHON_BINDINGS "Build Python bindings (requires SWIG)") + +if (WIN32) + + # Maximum warning level + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + + # Be strict about warnings... make them errors + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX") + + # Detect 64-bit portability issues + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Wp64") + +else (WIN32) + + # Maximum warning level + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + + # turn on debugging + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") + +endif (WIN32) + +if (BUILD_PYTHON_BINDINGS) + add_subdirectory (python-bindings) +endif (BUILD_PYTHON_BINDINGS) + +include_directories (${PROJECT_SOURCE_DIR}) + +add_subdirectory(examples) + +file (GLOB KDTREE_HEADERS kdtree++/*.hpp) +install (FILES ${KDTREE_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include) + diff --git a/src/3rdParty/libkdtree/COPYING b/src/3rdParty/libkdtree/COPYING new file mode 100644 index 0000000000..7bf5ef81a1 --- /dev/null +++ b/src/3rdParty/libkdtree/COPYING @@ -0,0 +1,176 @@ +"The Artistic Licence 2.0" +Copyright (c) 2000-2006, The Perl Foundation. +http://www.perlfoundation.org/legal/licenses/artistic-2_0.html + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble +~~~~~~~~ +This license establishes the terms under which a given free software Package +may be copied, modified, distributed, and/or redistributed. The intent is that +the Copyright Holder maintains some artistic control over the development of +that Package while still keeping the Package available as open source and free +software. + +You are always permitted to make arrangements wholly outside of this license +directly with the Copyright Holder of a given Package. If the terms of this +license do not permit the full use that you propose to make of the Package, +you should contact the Copyright Holder and seek a different licensing +arrangement. + +Definitions +~~~~~~~~~~~ +"Copyright Holder" means the individual(s) or organization(s) named in the +copyright notice for the entire Package. + +"Contributor" means any party that has contributed code or other material to +the Package, in accordance with the Copyright Holder's procedures. + +"You" and "your" means any person who would like to copy, distribute, or +modify the Package. + +"Package" means the collection of files distributed by the Copyright Holder, +and derivatives of that collection and/or of those files. A given Package may +consist of either the Standard Version, or a Modified Version. + +"Distribute" means providing a copy of the Package or making it accessible to +anyone else, or in the case of a company or organization, to others outside of +your company or organization. + +"Distributor Fee" means any fee that you charge for Distributing this Package +or providing support for this Package to another party. It does not mean +licensing fees. + +"Standard Version" refers to the Package if it has not been modified, or has +been modified only in ways explicitly requested by the Copyright Holder. + +"Modified Version" means the Package, if it has been changed, and such changes +were not explicitly requested by the Copyright Holder. + +"Original License" means this Artistic License as Distributed with the +Standard Version of the Package, in its current version or as it may be +modified by The Perl Foundation in the future. + +"Source" form means the source code, documentation source, and configuration +files for the Package. + +"Compiled" form means the compiled bytecode, object code, binary, or any other +form resulting from mechanical transformation or translation of the Source +form. + +Permission for Use and Modification Without Distribution +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(1) You are permitted to use the Standard Version and create and use Modified +Versions for any purpose without restriction, provided that you do not +Distribute the Modified Version. + +Permissions for Redistribution of the Standard Version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(2) You may Distribute verbatim copies of the Source form of the Standard +Version of this Package in any medium without restriction, either gratis or +for a Distributor Fee, provided that you duplicate all of the original +copyright notices and associated disclaimers. At your discretion, such +verbatim copies may or may not include a Compiled form of the Package. + +(3) You may apply any bug fixes, portability changes, and other modifications +made available from the Copyright Holder. The resulting Package will still be +considered the Standard Version, and as such will be subject to the Original +License. + +Distribution of Modified Versions of the Package as Source +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(4) You may Distribute your Modified Version as Source (either gratis or for +a Distributor Fee, and with or without a Compiled form of the Modified +Version) provided that you clearly document how it differs from the Standard +Version, including, but not limited to, documenting any non-standard features, +executables, or modules, and provided that you do at least ONE of the +following: + + (a) make the Modified Version available to the Copyright Holder of the + Standard Version, under the Original License, so that the Copyright + Holder may include your modifications in the Standard Version. + (b) ensure that installation of your Modified Version does not prevent the + user installing or running the Standard Version. In addition, the + Modified Version must bear a name that is different from the name of + the Standard Version. + (c) allow anyone who receives a copy of the Modified Version to make the + Source form of the Modified Version available to others under + (i) the Original License or + (ii) a license that permits the licensee to freely copy, modify and + redistribute the Modified Version using the same licensing terms + that apply to the copy that the licensee received, and requires + that the Source form of the Modified Version, and of any works + derived from it, be made freely available in that license fees + are prohibited but Distributor Fees are allowed. + +Distribution of Compiled Forms of the Standard Version or Modified Versions +without the Source +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(5) You may Distribute Compiled forms of the Standard Version without the +Source, provided that you include complete instructions on how to get the +Source of the Standard Version. Such instructions must be valid at the time of +your distribution. If these instructions, at any time while you are carrying +out such distribution, become invalid, you must provide new instructions on +demand or cease further distribution. If you provide valid instructions or +cease distribution within thirty days after you become aware that the +instructions are invalid, then you do not forfeit any of your rights under +this license. + +(6) You may Distribute a Modified Version in Compiled form without the Source, +provided that you comply with Section 4 with respect to the Source of the +Modified Version. + +Aggregating or Linking the Package +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(7) You may aggregate the Package (either the Standard Version or Modified +Version) with other packages and Distribute the resulting aggregation provided +that you do not charge a licensing fee for the Package. Distributor Fees are +permitted, and licensing fees for other components in the aggregation are +permitted. The terms of this license apply to the use and Distribution of the +Standard or Modified Versions as included in the aggregation. + +(8) You are permitted to link Modified and Standard Versions with other works, +to embed the Package in a larger work of your own, or to build stand-alone +binary or bytecode versions of applications that include the Package, and +Distribute the result without restriction, provided the result does not expose +a direct interface to the Package. + +Items That are Not Considered Part of a Modified Version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(9) Works (including, but not limited to, modules and scripts) that merely +extend or make use of the Package, do not, by themselves, cause the Package to +be a Modified Version. In addition, such works are not considered parts of the +Package itself, and are not subject to the terms of this license. + +General Provisions +~~~~~~~~~~~~~~~~~~ +(10) Any use, modification, and distribution of the Standard or Modified +Versions is governed by this Artistic License. By using, modifying or +distributing the Package, you accept this license. Do not use, modify, or +distribute the Package, if you do not accept this license. + +(11) If your Modified Version has been derived from a Modified Version made by +someone other than you, you are nevertheless required to ensure that your +Modified Version complies with the requirements of this license. + +(12) This license does not grant you the right to use any trademark, service +mark, tradename, or logo of the Copyright Holder. + +(13) This license includes the non-exclusive, worldwide, free-of-charge patent +license to make, have made, use, offer to sell, sell, import and otherwise +transfer the Package with respect to any patent claims licensable by the +Copyright Holder that are necessarily infringed by the Package. If you +institute patent litigation (including a cross-claim or counterclaim) against +any party alleging that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the date +that such litigation is filed. + +(14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER +AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. +UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY +OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/src/3rdParty/libkdtree/ChangeLog b/src/3rdParty/libkdtree/ChangeLog new file mode 100644 index 0000000000..30fc38e3ee --- /dev/null +++ b/src/3rdParty/libkdtree/ChangeLog @@ -0,0 +1,236 @@ +libkdtree++ ChangeLog +===================== + +2008-11-17 Sylvain Bougerel + + - Added #include in order to compile 'printf' statements in the + file 'examples/test_find_within_range.cpp'. + - Added patch from Max Fellermann in order to compile libkdtree++ with + clang++. + +2009-02-10 Paul Harris + + - Bug fix: was incorrectly casting a pointer when the search key type + was different to the stored type. + +2008-12-30 Paul Harris + + - New function: efficient_replace_and_optimise(). + Yes, its a long name. Sylvain doesn't like it. + The reason for the long name is that it is a dangerous function, + and it will resort whatever vector<> of data that you pass it. + So I wanted the user to really know what they were doing before + they called this function. + - Now calls sqrt() when required in order to search for items + in 'real' distance units... And so it will accept and return distance + in 'real' units (as opposed to squared units). + This is not an ideal solution, we have all sorts of ideas to improve + kdtree which will include less calls to sqrt() for more speed, and + the ability to change the standard euclidean distance measurements + for distance-on-a-sphere or whatever the user wants. + - Changed from using std::sort() to std::nth_element() when optimising + the tree. Performance boost. + - Added lots of tests to check that the find functions are working + correctly when fed edge-cases, including: + - Items that are exactly 'max' distance away from the target. + - When there are no value items to find. + - Templated the find functions so that the target/center point can be + anything that can be accessed via the Accessor. + - Fixes to make it compile. + + - And, a Python wrapper ! See README.Python + + - CMake support now can build the python wrapper and install the headers + and the python wrapper to a destination folder. Its simple, but neat. + Does not install python module into the python site packages or anything + like that. + +2008-11-17 Sylvain Bougerel + + - The version number of the library is now part of the headers. + - Fixed a bug with assignment operator. + - Fixed uninitialized memory problem with valgrind, when printing the + content of the tree. Due to the fact the _M_header was a _Link_type + instead of a _Node_base type and _M_root was a _Base_ptr type instead of + a _Link_type. + - Paul Harris fixed find() by ensuring that the correct node is being + matched during a find(). Thus, fixed a similar problem in erase. Paul + also added a new test courtesy of Hayne. + - Paul Harris augmented test_kdtree with various test on copy + construction, assignment, and formatting operator. + - Paul Harris added support for CMake, which should suit not only + MSVC users but others too. + - Paul Harris fixed bug with compiling with MSVC2005 with the 64bit + warnings turned on. + +2008-11-12 Sylvain Bougerel + + - Fix segfault on the regular iterator when _M_header->_M_right == + _M_root. Fix segfault on the reverse iterator when _M_header->_M_left == + _M_root. + + Besides, it also change the behavior when iterating past the end() or + rend(). Previously this would result in segfaults, now it makes the + iterator points to an undetermined location in the tree, similarly to + the current implementation of GNU libstdc++. + +2008-11-10 Sylvain Bougerel + + - kdtree++/iterator.hpp (KDTree): the decrement iterator was + ill-written. Its buggy behavior, and the use of non-standard + reverse_iterator initialiser needed to be fixed. These error were do to + a previous failed attempt by me at fixing the reverse_iterator. + + This time, I believe I got it right, however it needed the kdtree + structure to be modified. The reason is that without modification it is + not possible to distinguish the "header" from the "root" within the + iterator. This is required for the reverse_iterator to work properly. + + Now the kdtree has an additional pointer that points directly to the + root. The parent pointer of the header is permanently null. And + therefore the header can be distinguished from the root within the + iterator by checking the parent of the current node: if it is null, we + are at the header. + + +2008-11-10 Sylvain Bougerel (sylvain.bougerel.devel@gmail.com) + + - patch from Martin Shreiber to make libkdtree to compile with newer + version of g++-4.2 and g++4.3. + + - patch from Paul Harris to make libkdtree more exception transparent. + +2007-12-08 Sylvain Bougerel (sylvain.bougerel.devel@gmail.com) + + - fix bug where find_nearest() could return the wrong value if a + maximum distance greater than the root distance to the target value + was given in argument to the function. + + - find_nearest() still returns SQUARED value of the distance. You still + have to use sqrt() on the second member of the iterator. + + - find_nearest() behavior was slightly changed: if many nodes are at + the same distance from the target value, the node with the lowest + memory address will be returned. This is to catter for the + reimplementation of find_exact() coming soon. + +2007-12-02 Sylvain Bougerel (sylvain.bougerel.devel@gmail.com) + + - find_nearest() now returned the SQUARED value of the distance for + euclidean space calculation (the default). You have to use sqrt() on + the returned distance (i.e. iterator->second) if you want to read the + absolute distance returned by find_nearest. My apologies for not + making highlighting this beforehand. + + - Increased the performance of find and find_nearest/find_nearest_if by + about 50x to 100x depending on your compilation flags. + + - KDTree are not defined as: + KDTree<__K, _Val, _Acc, _Cmp, _Alloc> + but as: + KDTree<__K, _Val, _Acc, _Dist, _Cmp, _Alloc> + So pay attention to the _Dist functor. The distance functor calculate + the squared difference between 2 elements returned by the accessor. You + might have to change your code to reflect the new definition, or it wont + compile if you have set custom accessor and comparison functors. + + - The following functors are now accessible in the tree: + - the comparison functor, accessible by copy only + - the accessor functor, accessible by copy only + - the distance functor, accessible read-write, this means that + you can modify the behavior of find, find_nearest, + find_nearest_if within the same KDTree object. + + - find_exact has not be modified and retained the code of the former, + slower algorithm. I have to write some more code to do this. Pls wait a + little more. + + - The file accessor.hpp was renamed as function.hpp for it now boast + more than just the KDTree accessor + +2007-11-25 Sylvain Bougerel (sylvain.bougerel.devel@gmail.com) + + - fixed the reverse_iterator. Now it can be used. + +2007-10-24 Sylvain Bougerel (sylvain.bougerel.devel@gmail.com) + + - Removal of all the warnings that are yield by the compiler when + using the following flags: + -Wall -pedantic -ansi + Do not hesitate to suggest any flags for additional code checking. + + This release also feature numerous of enhancements by Paul Harris + (paulharris@computer.org): + - const kdtrees can be searched + - find_nearest_if() enforce validation of a predicate + - visit_within_range() walk the tree and calls + Visitor::operator() on template argument for + each node within the range + - find_exact() matches an kdtree::value_type by location and by + calling kdtree::value_type::operator==() (in case two different + items have the same location find_exact() will not return the + wrong item) + - erase_exact() is to erase() what find_exact() is to find() + - check_tree() and num_dist_calcs for debugging purpose plus + additional improvements on erase and region intersection + +2004-11-26 Paul Harris (paulharris@computer.org) + + - New feature: find_nearest() + - Accessors can now be initialised with the tree, so ptr_fun() + or functors can be used to access datapoints. + - Accessors now much more generic, so you can use the same + accessor to access multiple types. + - Range-constructors now optimise() automatically, simplifying + the construction of a filled tree. + - _Range is now more easy to construct. + +2004-11-15 Martin F. Krafft (libkdtree@pobox.madduck.net) + + - fixed numerous little bugs that led to compilation problems + - changed code to compile cleanly with GCC 3.4 and GCC 4.0 + +2004-11-06 Martin F. Krafft (libkdtree@pobox.madduck.net) + + - reverted to optimise() to prevent API change, and added an optimize() + passthrough method with an appropriate comment. + +2004-11-05 Paul Harris (paulharris@computer.org) + + - Renamed optimise() to optimize(). + - Added a full set of range constructors and insert(range) methods. + it now works with inserter(tree,tree.begin()) + - Target type no longer needs a default constructor. This also fixes + problems with empty trees (would crash if optimized). + - Some code cleanup (removed inlines, switched from const_iterator to + iterator, added this-> to ensure the methods are called). + - Added a new method: count_within_range(). + - Fixed bug in rend(). + +2004-11-04 Martin F. Krafft (libkdtree@pobox.madduck.net) + + - Integrated patch by Paul Harris to fix a logic error pertaining to + OutputIterators in find_within_range. find_within_range() now + returns the output iterator instead of a count. Thanks, Paul! + - Added another fix by Paul Harris to _M_get_j_max, which would cause + a dimensional overflow for trees with depths >= K. Thanks (again) Paul! + - Made some improvements to the autotools files. + +2004-05-11 Martin F. Krafft (libkdtree@pobox.madduck.net) + + - Fixed CFlags and Libs entries in pkgconfig file. + +2004-05-11 Martin F. Krafft (libkdtree@pobox.madduck.net) + + - Initial release. + + + COPYRIGHT -- + libkdtree++ is (c) 2004-2007 Martin F. Krafft and + Sylvain Bougerel and distributed under the + terms of the Artistic License 2.0. See the ./COPYING file in the source tree + root for more information. + + THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND + FITNESS FOR A PARTICULAR PURPOSE. diff --git a/src/3rdParty/libkdtree/INSTALL b/src/3rdParty/libkdtree/INSTALL new file mode 100644 index 0000000000..5458714e1e --- /dev/null +++ b/src/3rdParty/libkdtree/INSTALL @@ -0,0 +1,234 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/src/3rdParty/libkdtree/README b/src/3rdParty/libkdtree/README new file mode 100644 index 0000000000..3c015ba61f --- /dev/null +++ b/src/3rdParty/libkdtree/README @@ -0,0 +1,121 @@ +libkdtree++ README +================== + +libkdtree++ is (c) 2004-2007 Martin F. Krafft +and distributed under the terms of the Artistic License 2.0. +See the file LICENSE in the source distribution for more information. + +Please send bugreports to . + +Introduction +------------ + +libkdtree++ is a C++ template container implementation of k-dimensional space +sorting, using a kd-tree. It: + + - sports an unlimited number of dimensions (in theory) + - can store any data structure, access and comparison between the + individual dimensional components defaults to the bracket operator, in + the range [0, k-1] and the std::less functor by default, but other + accessors and comparator can be defined. + - has support for custom allocators + - implements iterators + - provides standard find as well as range queries + - has amortised O(lg n) time (O(n lg n) worst case) on most + operations (insert/erase/find optimised) and worst-case O(n) space. + - provides a means to rebalance and thus optimise the tree. + - exists in its own namespace + - uses STL coding style, basing a lot of the code on stl_tree.h + +Notes +----- + +Note that the library is not (yet) complete and it's not thoroughly tested. +However, given the effort and grief I went through in writing it, I would +like to make it available to folks, get people to test it, and hopefully have +some peeps submit improvements. If you have any suggestions, please write to +libkdtree-devel@lists.alioth.debian.org . + +It's not yet documented, although the usage should be fairly straight +forward. I am hoping to find someone else to document it as I suck at +documentation and as the author, it's exceptionally difficult to stay +didactically correct. + +Credits (Martin F. Kraft) +------------------------- + +While the library was written all by myself, it would not have been possible +without the help of a number of people. Foremost, I would like to thank the +folks from the #c++ channel on Freenode, specifically (in no particular order) +orbitz, quix, Erwin, pwned, wcstok, dasOp, Chaku, Adrinael, The_Vulture, and +LIM2 (if I left anyone out, let me know). Finally, I thank the Artificial +Intelligence Laboratory of the University of Zurich, Dr. Peter Eggenberger and +Gabriel Gómez for giving me the opportunity to write this stuff. + +Since libkdtree++ makes an effort to stay as close as possible to the feel of +a STL container, concepts and inspiration was gained from the SGI C++ +implementation of red-black trees (stl_tree.h). + +I also have to thank the Debian project for providing an amazingly reliable +and flexible developer station with their operating system. I am sorry for +everyone who has to use something else. + +Installation +------------ + +As there is no need to compile any files, you can just: + +$ ./configure +$ sudo make install + + +It now also supports cmake, which can be used to build the examples +and tests. +To build with cmake, do an out-of-source build like so: + +# ASSUMING you have decompressed it into a directory called libkdtree, +# and you are currently in that directory... + +$ cd .. # go up, out of the kdtree source directory +$ mkdir build +$ cd build +$ cmake ../libkdtree +$ make + + +You can use cmake to build the tests and examples on Windows with +Visual C++. Use the windows cmake to create a Visual C++ solution and +build that. + +Note that cmake and ./configure is not needed at all in order to use +kdtree in your application. As kdtree is a header-only library, you +just need to #include the kdtree.hpp + + +Read the following to make use of the library. + +Usage +----- + +A simple example program is provided in the ./examples directory +(/usr/share/doc/libkdtree++-dev/examples on Debian). + +For those using the ./configure system, the library supports pkg-config. +Thus, to compile with the library, + + #include + +and append the output of `pkg-config libkdtree++ --cflags` to your $CPPFLAGS. + +Each call to erase() and insert() unbalances the tree. It is possible that +nodes will not be found while the tree is unbalanced. You rebalance the +tree by calling optimize(), and you should call it before you need to search +the tree (this includes erase(value) calls, which search the tree). + +It is ok to call insert(value) many times and optimize() at the end, but +every erase() call should be followed with optimize(). + +These notes are a bit out of date, please check the webpage and mailing list +for more info. Documentation is on the TODO list. + +Have fun. diff --git a/src/3rdParty/libkdtree/README.Python b/src/3rdParty/libkdtree/README.Python new file mode 100644 index 0000000000..3e1d7dacb4 --- /dev/null +++ b/src/3rdParty/libkdtree/README.Python @@ -0,0 +1,12 @@ +Stand-alone Python bindings, contributed by Willi Richert + +To make them: +$ cd python-bindings +$ make +You will find then two files kdtree.py _kdtree.so in the current directory. +These are all you need to use libkdtree++. +Please examine the test files to get a grip to the usage. + +To run the tests, type: +python py-kdtree_test.py + diff --git a/src/3rdParty/libkdtree/examples/CMakeLists.txt b/src/3rdParty/libkdtree/examples/CMakeLists.txt new file mode 100644 index 0000000000..9b4cc4ff0c --- /dev/null +++ b/src/3rdParty/libkdtree/examples/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable (test_hayne test_hayne.cpp) +add_executable (test_kdtree test_kdtree.cpp) +add_executable (test_find_within_range test_find_within_range.cpp) diff --git a/src/3rdParty/libkdtree/examples/test_find_within_range.cpp b/src/3rdParty/libkdtree/examples/test_find_within_range.cpp new file mode 100644 index 0000000000..62214f606b --- /dev/null +++ b/src/3rdParty/libkdtree/examples/test_find_within_range.cpp @@ -0,0 +1,108 @@ +// Thanks to James Remillard +// +#include +#include +#include +#include +#include + +using namespace std; + +struct kdtreeNode +{ + typedef double value_type; + + double xyz[3]; + size_t index; + + value_type operator[](size_t n) const + { + return xyz[n]; + } + + double distance( const kdtreeNode &node) + { + double x = xyz[0] - node.xyz[0]; + double y = xyz[1] - node.xyz[1]; + double z = xyz[2] - node.xyz[2]; + +// this is not correct return sqrt( x*x+y*y+z*z); + +// this is what kdtree checks with find_within_range() +// the "manhattan distance" from the search point. +// effectively, distance is the maximum distance in any one dimension. + return max(fabs(x),max(fabs(y),fabs(z))); + + } +}; + +int main(int argc,char *argv[]) +{ + vector pts; + + typedef KDTree::KDTree<3,kdtreeNode> treeType; + + treeType tree; + + // make random 3d points + for ( size_t n = 0; n < 10000; ++n) + { + kdtreeNode node; + node.xyz[0] = double(rand())/RAND_MAX; + node.xyz[1] = double(rand())/RAND_MAX; + node.xyz[2] = double(rand())/RAND_MAX; + node.index = n; + + tree.insert( node); + pts.push_back( node); + } + + for (size_t r = 0; r < 1000; ++r) + { + kdtreeNode refNode; + refNode.xyz[0] = double(rand())/RAND_MAX; + refNode.xyz[1] = double(rand())/RAND_MAX; + refNode.xyz[2] = double(rand())/RAND_MAX; + + double limit = double(rand())/RAND_MAX; + + // find the correct return list by checking every single point + set correctCloseList; + + for ( size_t i= 0; i < pts.size(); ++i) + { + double dist = refNode.distance( pts[i]); + if ( dist < limit) + correctCloseList.insert( i ); + } + + // now do the same with the kdtree. + vector howClose; + tree.find_within_range(refNode,limit,back_insert_iterator >(howClose)); + + // make sure no extra points are returned, and the return has no missing points. + for ( size_t i = 0; i < howClose.size(); ++i) + { + set::iterator hit = correctCloseList.find( howClose[i].index); + + if ( hit != correctCloseList.end()) + { + correctCloseList.erase(hit); + } + else + { + // point that is too far away - fail! + assert(false); + printf("fail, extra points.\n"); + } + } + + // fail, not all of the close enough points returned. + assert( correctCloseList.size() == 0); + if ( correctCloseList.size() > 0) + { + printf("fail, missing points.\n"); + } + } +} + diff --git a/src/3rdParty/libkdtree/examples/test_hayne.cpp b/src/3rdParty/libkdtree/examples/test_hayne.cpp new file mode 100644 index 0000000000..6771727fdc --- /dev/null +++ b/src/3rdParty/libkdtree/examples/test_hayne.cpp @@ -0,0 +1,116 @@ +#define KDTREE_SIZE_T unsigned int +#include + +#include +#include +#include +#include + +using namespace std; + +struct duplet +{ + typedef int value_type; + + inline value_type operator[](int const N) const { return d[N]; } + + inline bool operator==(duplet const& other) const + { + return this->d[0] == other.d[0] && this->d[1] == other.d[1]; + } + + inline bool operator!=(duplet const& other) const + { + return this->d[0] != other.d[0] || this->d[1] != other.d[1]; + } + + friend ostream & operator<<(ostream & o, duplet const& d) + { + return o << "(" << d[0] << "," << d[1] << ")"; + } + + value_type d[2]; +}; + +typedef KDTree::KDTree<2, duplet, std::pointer_to_binary_function > duplet_tree_type; + +inline double return_dup( duplet d, int k ) { return d[k]; } + + + +int main() +{ + duplet_tree_type dupl_tree_test(std::ptr_fun(return_dup)); + std::vector vDuplets; + + //srand(time(0)); + int randy1 = 0; + int randy2 = 0; + for (int i=0; i<700; i++) + { + //create coordinate for new duplet + randy1+=2; + randy1=randy1%255; + randy2+=3; + randy2=randy2%255; + //randy1 = rand() % 255; + //randy2 = rand() % 255; + + //new duplet + duplet super_dupre = { {randy1, randy2} }; + + //check if duplet with same coordinate already in vector/tree. If not: insert in vector and tree + duplet_tree_type::iterator pItr = dupl_tree_test.find_nearest(super_dupre,std::numeric_limits::max()).first; + if (*pItr!=super_dupre) + { + dupl_tree_test.insert(super_dupre); + vDuplets.push_back(super_dupre); + } + } + + dupl_tree_test.optimise(); + + size_t elements; + + while (vDuplets.size() > 0) //delete all duplets from tree which are in the vector + { + elements = vDuplets.size(); + + duplet element_to_erase = vDuplets.back(); + vDuplets.pop_back(); + + if (vDuplets.size() == 147) + cout << "THIS IS THE BUG TRIGGER" << endl; + + cout << vDuplets.size() << " : Deleting " << element_to_erase << endl; + + assert( find(dupl_tree_test.begin(),dupl_tree_test.end(), element_to_erase) != dupl_tree_test.end() ); + assert(dupl_tree_test.find(element_to_erase) != dupl_tree_test.end()); + + duplet_tree_type::iterator will = dupl_tree_test.find(element_to_erase); + duplet_tree_type::iterator should = dupl_tree_test.find_exact(element_to_erase); + + cout << " tree will delete: " << *will << endl; + cout << " tree should delete: " << *should << endl; + + assert(*will == *should); + + dupl_tree_test.erase(element_to_erase); //erase() : will probably erase wrong element sooner or later + //dupl_tree_test.erase_exact(element_to_erase); --> this works + + // now check that it cannot find the element UNLESS there is another one with the identical location in the list... + if (find(vDuplets.begin(),vDuplets.end(),element_to_erase) == vDuplets.end()) + { + duplet_tree_type::iterator not_there = dupl_tree_test.find(element_to_erase); + if (not_there != dupl_tree_test.end()) + { + cout << "SHOULD NOT HAVE FOUND THIS: " << *not_there << endl; + assert(0); + } + else + { + cout << " find() double-check passed." << endl; + } + } + } +} diff --git a/src/3rdParty/libkdtree/examples/test_kdtree.cpp b/src/3rdParty/libkdtree/examples/test_kdtree.cpp new file mode 100644 index 0000000000..d62ac970e5 --- /dev/null +++ b/src/3rdParty/libkdtree/examples/test_kdtree.cpp @@ -0,0 +1,423 @@ +#define KDTREE_DEFINE_OSTREAM_OPERATORS + +// Make SURE all our asserts() are checked +#undef NDEBUG + +#include + +#include +#include +#include +#include +#include +#include + +// used to ensure all triplets that are accessed via the operator<< are initialised. +std::set registered; + +struct triplet +{ + typedef int value_type; + + triplet(value_type a, value_type b, value_type c) + { + d[0] = a; + d[1] = b; + d[2] = c; + bool reg_ok = (registered.find(this) == registered.end()); + assert(reg_ok); + registered.insert(this).second; + } + + triplet(const triplet & x) + { + d[0] = x.d[0]; + d[1] = x.d[1]; + d[2] = x.d[2]; + bool reg_ok = (registered.find(this) == registered.end()); + assert(reg_ok); + registered.insert(this).second; + } + + ~triplet() + { + bool unreg_ok = (registered.find(this) != registered.end()); + assert(unreg_ok); + registered.erase(this); + } + + double distance_to(triplet const& x) const + { + double dist = 0; + for (int i = 0; i != 3; ++i) + dist += (d[i]-x.d[i])*(d[i]-x.d[i]); + return std::sqrt(dist); + } + + inline value_type operator[](size_t const N) const { return d[N]; } + + value_type d[3]; +}; + + + +// same as triplet, except with the values reversed. +struct alternate_triplet +{ + typedef int value_type; + + alternate_triplet(const triplet & x) + { + d[0] = x.d[2]; + d[1] = x.d[1]; + d[2] = x.d[0]; + } + + inline value_type operator[](size_t const N) const { return d[2-N]; } + + value_type d[3]; +}; + +inline bool operator==(triplet const& A, triplet const& B) { + return A.d[0] == B.d[0] && A.d[1] == B.d[1] && A.d[2] == B.d[2]; +} + +std::ostream& operator<<(std::ostream& out, triplet const& T) +{ + assert(registered.find(&T) != registered.end()); + return out << '(' << T.d[0] << ',' << T.d[1] << ',' << T.d[2] << ')'; +} + +inline double tac( triplet t, size_t k ) { return t[k]; } + +// use tac as a class instead of a function, +// can access more than one type with just 1 definition. +struct alternate_tac +{ + typedef double result_type; + double operator()( triplet const& t, size_t k ) const { return t[k]; } + double operator()( alternate_triplet const& t, size_t k ) const { return t[k]; } +}; + + +typedef KDTree::KDTree<3, triplet, std::pointer_to_binary_function > tree_type; + +struct Predicate +{ + bool operator()( triplet const& t ) const + { + return t[0] > 3; // anything, we are currently testing that it compiles. + } +}; + +// never finds anything +struct FalsePredicate +{ + bool operator()( triplet const& t ) const { return false; } +}; + +int main() +{ + // check that it'll find nodes exactly MAX away + { + tree_type exact_dist(std::ptr_fun(tac)); + triplet c0(5, 4, 0); + exact_dist.insert(c0); + triplet target(7,4,0); + + std::pair found = exact_dist.find_nearest(target,2); + assert(found.first != exact_dist.end()); + assert(found.second == 2); + std::cout << "Test find_nearest(), found at exact distance away from " << target << ", found " << *found.first << std::endl; + } + + // do the same test, except use alternate_triplet as the search key + { + // NOTE: stores triplet, but we search with alternate_triplet + typedef KDTree::KDTree<3, triplet, alternate_tac> alt_tree; + + triplet actual_target(7,0,0); + + alt_tree tree; + tree.insert( triplet(0, 0, 7) ); + tree.insert( triplet(0, 0, 7) ); + tree.insert( triplet(0, 0, 7) ); + tree.insert( triplet(3, 0, 0) ); + tree.insert( actual_target ); + tree.optimise(); + + alternate_triplet target( actual_target ); + + std::pair found = tree.find_nearest(target); + assert(found.first != tree.end()); + std::cout << "Test with alternate search type, found: " << *found.first << ", wanted " << actual_target << std::endl; + assert(found.second == 0); + assert(*found.first == actual_target); + } + + + { + tree_type exact_dist(std::ptr_fun(tac)); + triplet c0(5, 2, 0); + exact_dist.insert(c0); + triplet target(7,4,0); + + // call find_nearest without a range value - it found a compile error earlier. + std::pair found = exact_dist.find_nearest(target); + assert(found.first != exact_dist.end()); + std::cout << "Test find_nearest(), found at exact distance away from " << target << ", found " << *found.first << " @ " << found.second << " should be " << std::sqrt(8) << std::endl; + assert(found.second == std::sqrt(8)); + } + + { + tree_type exact_dist(std::ptr_fun(tac)); + triplet c0(5, 2, 0); + exact_dist.insert(c0); + triplet target(7,4,0); + + std::pair found = exact_dist.find_nearest(target,std::sqrt(8)); + assert(found.first != exact_dist.end()); + std::cout << "Test find_nearest(), found at exact distance away from " << target << ", found " << *found.first << " @ " << found.second << " should be " << std::sqrt(8) << std::endl; + assert(found.second == std::sqrt(8)); + } + + tree_type src(std::ptr_fun(tac)); + + triplet c0(5, 4, 0); src.insert(c0); + triplet c1(4, 2, 1); src.insert(c1); + triplet c2(7, 6, 9); src.insert(c2); + triplet c3(2, 2, 1); src.insert(c3); + triplet c4(8, 0, 5); src.insert(c4); + triplet c5(5, 7, 0); src.insert(c5); + triplet c6(3, 3, 8); src.insert(c6); + triplet c7(9, 7, 3); src.insert(c7); + triplet c8(2, 2, 6); src.insert(c8); + triplet c9(2, 0, 6); src.insert(c9); + + std::cout << src << std::endl; + + src.erase(c0); + src.erase(c1); + src.erase(c3); + src.erase(c5); + + src.optimise(); + + + // test the efficient_replace_and_optimise() + tree_type eff_repl = src; + { + std::vector vec; + // erased above as part of test vec.push_back(triplet(5, 4, 0)); + // erased above as part of test vec.push_back(triplet(4, 2, 1)); + vec.push_back(triplet(7, 6, 9)); + // erased above as part of test vec.push_back(triplet(2, 2, 1)); + vec.push_back(triplet(8, 0, 5)); + // erased above as part of test vec.push_back(triplet(5, 7, 0)); + vec.push_back(triplet(3, 3, 8)); + vec.push_back(triplet(9, 7, 3)); + vec.push_back(triplet(2, 2, 6)); + vec.push_back(triplet(2, 0, 6)); + + eff_repl.clear(); + eff_repl.efficient_replace_and_optimise(vec); + } + + + std::cout << std::endl << src << std::endl; + + tree_type copied(src); + std::cout << copied << std::endl; + tree_type assigned; + assigned = src; + std::cout << assigned << std::endl; + + for (int loop = 0; loop != 4; ++loop) + { + tree_type * target; + switch (loop) + { + case 0: std::cout << "Testing plain construction" << std::endl; + target = &src; + break; + + case 1: std::cout << "Testing copy-construction" << std::endl; + target = &copied; + break; + + case 2: std::cout << "Testing assign-construction" << std::endl; + target = &assigned; + break; + + default: + case 4: std::cout << "Testing efficient-replace-and-optimise" << std::endl; + target = &eff_repl; + break; + } + tree_type & t = *target; + + int i=0; + for (tree_type::const_iterator iter=t.begin(); iter!=t.end(); ++iter, ++i); + std::cout << "iterator walked through " << i << " nodes in total" << std::endl; + if (i!=6) + { + std::cerr << "Error: does not tally with the expected number of nodes (6)" << std::endl; + return 1; + } + i=0; + for (tree_type::const_reverse_iterator iter=t.rbegin(); iter!=t.rend(); ++iter, ++i); + std::cout << "reverse_iterator walked through " << i << " nodes in total" << std::endl; + if (i!=6) + { + std::cerr << "Error: does not tally with the expected number of nodes (6)" << std::endl; + return 1; + } + + triplet s(5, 4, 3); + std::vector v; + unsigned int const RANGE = 3; + + size_t count = t.count_within_range(s, RANGE); + std::cout << "counted " << count + << " nodes within range " << RANGE << " of " << s << ".\n"; + t.find_within_range(s, RANGE, std::back_inserter(v)); + + std::cout << "found " << v.size() << " nodes within range " << RANGE + << " of " << s << ":\n"; + std::vector::const_iterator ci = v.begin(); + for (; ci != v.end(); ++ci) + std::cout << *ci << " "; + std::cout << "\n" << std::endl; + + std::cout << std::endl << t << std::endl; + + // search for all the nodes at exactly 0 dist away + for (tree_type::const_iterator target = t.begin(); target != t.end(); ++target) + { + std::pair found = t.find_nearest(*target,0); + assert(found.first != t.end()); + assert(*found.first == *target); + std::cout << "Test find_nearest(), found at exact distance away from " << *target << ", found " << *found.first << std::endl; + } + + { + const double small_dist = 0.0001; + std::pair notfound = t.find_nearest(s,small_dist); + std::cout << "Test find_nearest(), nearest to " << s << " within " << small_dist << " should not be found" << std::endl; + + if (notfound.first != t.end()) + { + std::cout << "ERROR found a node at dist " << notfound.second << " : " << *notfound.first << std::endl; + std::cout << "Actual distance = " << s.distance_to(*notfound.first) << std::endl; + } + + assert(notfound.first == t.end()); + } + + { + std::pair nif = t.find_nearest_if(s,std::numeric_limits::max(),Predicate()); + std::cout << "Test find_nearest_if(), nearest to " << s << " @ " << nif.second << ": " << *nif.first << std::endl; + + std::pair cantfind = t.find_nearest_if(s,std::numeric_limits::max(),FalsePredicate()); + std::cout << "Test find_nearest_if(), nearest to " << s << " should never be found (predicate too strong)" << std::endl; + assert(cantfind.first == t.end()); + } + + + + + { + std::pair found = t.find_nearest(s,std::numeric_limits::max()); + std::cout << "Nearest to " << s << " @ " << found.second << " " << *found.first << std::endl; + std::cout << "Should be " << found.first->distance_to(s) << std::endl; + // NOTE: the assert does not check for an exact match, as it is not exact when -O2 or -O3 is + // switched on. Some sort of optimisation makes the math inexact. + assert( fabs(found.second - found.first->distance_to(s)) < std::numeric_limits::epsilon() ); + } + + { + triplet s2(10, 10, 2); + std::pair found = t.find_nearest(s2,std::numeric_limits::max()); + std::cout << "Nearest to " << s2 << " @ " << found.second << " " << *found.first << std::endl; + std::cout << "Should be " << found.first->distance_to(s2) << std::endl; + // NOTE: the assert does not check for an exact match, as it is not exact when -O2 or -O3 is + // switched on. Some sort of optimisation makes the math inexact. + assert( fabs(found.second - found.first->distance_to(s2)) < std::numeric_limits::epsilon() ); + } + + std::cout << std::endl; + + std::cout << t << std::endl; + + // Testing iterators + { + std::cout << "Testing iterators" << std::endl; + + t.erase(c2); + t.erase(c4); + t.erase(c6); + t.erase(c7); + t.erase(c8); + // t.erase(c9); + + std::cout << std::endl << t << std::endl; + + std::cout << "Forward iterator test..." << std::endl; + std::vector forwards; + for (tree_type::iterator i = t.begin(); i != t.end(); ++i) + { std::cout << *i << " " << std::flush; forwards.push_back(*i); } + std::cout << std::endl; + std::cout << "Reverse iterator test..." << std::endl; + std::vector backwards; + for (tree_type::reverse_iterator i = t.rbegin(); i != t.rend(); ++i) + { std::cout << *i << " " << std::flush; backwards.push_back(*i); } + std::cout << std::endl; + std::reverse(backwards.begin(),backwards.end()); + assert(backwards == forwards); + } + } + + + // Walter reported that the find_within_range() wasn't giving results that were within + // the specified range... this is the test. + { + tree_type tree(std::ptr_fun(tac)); + tree.insert( triplet(28.771200,16.921600,-2.665970) ); + tree.insert( triplet(28.553101,18.649700,-2.155560) ); + tree.insert( triplet(28.107500,20.341400,-1.188940) ); + tree.optimise(); + + std::deque< triplet > vectors; + triplet sv(18.892500,20.341400,-1.188940); + tree.find_within_range(sv, 10.0f, std::back_inserter(vectors)); + + std::cout << std::endl << "Test find_with_range( " << sv << ", 10.0f) found " << vectors.size() << " candidates." << std::endl; + + // double-check the ranges + for (std::deque::iterator v = vectors.begin(); v != vectors.end(); ++v) + { + double dist = sv.distance_to(*v); + std::cout << " " << *v << " dist=" << dist << std::endl; + if (dist > 10.0f) + 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; + // Not a valid test, it can be greater than 10 if the point is in the corners of the box. + // assert(dist <= 10.0f); + } + } + + + return 0; +} + +/* COPYRIGHT -- + * + * This file is part of libkdtree++, a C++ template KD-Tree sorting container. + * libkdtree++ is (c) 2004-2007 Martin F. Krafft + * and Sylvain Bougerel distributed under the + * terms of the Artistic License 2.0. See the ./COPYING file in the source tree + * root for more information. + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ diff --git a/src/3rdParty/libkdtree/kdtree++/allocator.hpp b/src/3rdParty/libkdtree/kdtree++/allocator.hpp new file mode 100644 index 0000000000..ae7a5e934a --- /dev/null +++ b/src/3rdParty/libkdtree/kdtree++/allocator.hpp @@ -0,0 +1,96 @@ +/** \file + * Defines the allocator interface as used by the KDTree class. + * + * \author Martin F. Krafft + */ + +#ifndef INCLUDE_KDTREE_ALLOCATOR_HPP +#define INCLUDE_KDTREE_ALLOCATOR_HPP + +#include + +#include "node.hpp" + +namespace KDTree +{ + + template + class _Alloc_base + { + public: + typedef _Node<_Tp> _Node_; + typedef typename _Node_::_Base_ptr _Base_ptr; + typedef _Alloc allocator_type; + + _Alloc_base(allocator_type const& __A) + : _M_node_allocator(__A) {} + + allocator_type + get_allocator() const + { + return _M_node_allocator; + } + + + class NoLeakAlloc + { + _Alloc_base * base; + _Node_ * new_node; + + public: + NoLeakAlloc(_Alloc_base * b) : base(b), new_node(base->_M_allocate_node()) {} + + _Node_ * get() { return new_node; } + void disconnect() { new_node = NULL; } + + ~NoLeakAlloc() { if (new_node) base->_M_deallocate_node(new_node); } + }; + + + protected: + allocator_type _M_node_allocator; + + _Node_* + _M_allocate_node() + { + return _M_node_allocator.allocate(1); + } + + void + _M_deallocate_node(_Node_* const __P) + { + return _M_node_allocator.deallocate(__P, 1); + } + + void + _M_construct_node(_Node_* __p, _Tp const __V = _Tp(), + _Base_ptr const __PARENT = NULL, + _Base_ptr const __LEFT = NULL, + _Base_ptr const __RIGHT = NULL) + { + new (__p) _Node_(__V, __PARENT, __LEFT, __RIGHT); + } + + void + _M_destroy_node(_Node_* __p) + { + _M_node_allocator.destroy(__p); + } + }; + +} // namespace KDTree + +#endif // include guard + +/* COPYRIGHT -- + * + * This file is part of libkdtree++, a C++ template KD-Tree sorting container. + * libkdtree++ is (c) 2004-2007 Martin F. Krafft + * and Sylvain Bougerel distributed under the + * terms of the Artistic License 2.0. See the ./COPYING file in the source tree + * root for more information. + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ diff --git a/src/3rdParty/libkdtree/kdtree++/function.hpp b/src/3rdParty/libkdtree/kdtree++/function.hpp new file mode 100644 index 0000000000..d7fa2f06c7 --- /dev/null +++ b/src/3rdParty/libkdtree/kdtree++/function.hpp @@ -0,0 +1,89 @@ +/** \file + * Defines the various functors and interfaces used for KDTree. + * + * \author Martin F. Krafft + * \author Sylvain Bougerel + */ + +#ifndef INCLUDE_KDTREE_ACCESSOR_HPP +#define INCLUDE_KDTREE_ACCESSOR_HPP + +#include + +namespace KDTree +{ + template + struct _Bracket_accessor + { + typedef typename _Val::value_type result_type; + + result_type + operator()(_Val const& V, size_t const N) const + { + return V[N]; + } + }; + + template + struct always_true + { + bool operator() (const _Tp& ) const { return true; } + }; + + template + struct squared_difference + { + typedef _Dist distance_type; + + distance_type + operator() (const _Tp& __a, const _Tp& __b) const + { + distance_type d=__a - __b; + return d*d; + } + }; + + template + struct squared_difference_counted + { + typedef _Dist distance_type; + + squared_difference_counted() + : _M_count(0) + { } + + void reset () + { _M_count = 0; } + + long& + count () const + { return _M_count; } + + distance_type + operator() (const _Tp& __a, const _Tp& __b) const + { + distance_type d=__a - __b; + ++_M_count; + return d*d; + } + + private: + mutable long _M_count; + }; + +} // namespace KDTree + +#endif // include guard + +/* COPYRIGHT -- + * + * This file is part of libkdtree++, a C++ template KD-Tree sorting container. + * libkdtree++ is (c) 2004-2007 Martin F. Krafft + * and Sylvain Bougerel distributed under the + * terms of the Artistic License 2.0. See the ./COPYING file in the source tree + * root for more information. + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ diff --git a/src/3rdParty/libkdtree/kdtree++/iterator.hpp b/src/3rdParty/libkdtree/kdtree++/iterator.hpp new file mode 100644 index 0000000000..801dc40ae4 --- /dev/null +++ b/src/3rdParty/libkdtree/kdtree++/iterator.hpp @@ -0,0 +1,258 @@ +/** \file + * Defines interfaces for iterators as used by the KDTree class. + * + * \author Martin F. Krafft + */ + +#ifndef INCLUDE_KDTREE_ITERATOR_HPP +#define INCLUDE_KDTREE_ITERATOR_HPP + +#include + +#include + +namespace KDTree +{ + template + class _Iterator; + + template + inline bool + operator==(_Iterator<_Val, _Ref, _Ptr> const&, + _Iterator<_Val, _Ref, _Ptr> const&); + + template + inline bool + operator==(_Iterator<_Val, const _Val&, const _Val*> const&, + _Iterator<_Val, _Val&, _Val*> const&); + + template + inline bool + operator==(_Iterator<_Val, _Val&, _Val*> const&, + _Iterator<_Val, const _Val&, const _Val*> const&); + + template + inline bool + operator!=(_Iterator<_Val, _Ref, _Ptr> const&, + _Iterator<_Val, _Ref, _Ptr> const&); + + template + inline bool + operator!=(_Iterator<_Val, const _Val&, const _Val*> const&, + _Iterator<_Val, _Val&, _Val*> const&); + + template + inline bool + operator!=(_Iterator<_Val, _Val&, _Val*> const&, + _Iterator<_Val, const _Val&, const _Val*> const&); + + class _Base_iterator + { + protected: + typedef _Node_base::_Base_const_ptr _Base_const_ptr; + _Base_const_ptr _M_node; + + inline _Base_iterator(_Base_const_ptr const __N = NULL) + : _M_node(__N) {} + inline _Base_iterator(_Base_iterator const& __THAT) + : _M_node(__THAT._M_node) {} + + inline void + _M_increment() + { + if (_M_node->_M_right) + { + _M_node = _M_node->_M_right; + while (_M_node->_M_left) _M_node = _M_node->_M_left; + } + else + { + _Base_const_ptr __p = _M_node->_M_parent; + while (__p && _M_node == __p->_M_right) + { + _M_node = __p; + __p = _M_node->_M_parent; + } + if (__p) // (__p) provide undetermined behavior on end()++ rather + // than a seg fault, similar to standard iterator. + _M_node = __p; + } + } + + inline void + _M_decrement() + { + if (!_M_node->_M_parent) // clearly identify the header node + { + _M_node = _M_node->_M_right; + } + else if (_M_node->_M_left) + { + _Base_const_ptr x = _M_node->_M_left; + while (x->_M_right) x = x->_M_right; + _M_node = x; + } + else + { + _Base_const_ptr __p = _M_node->_M_parent; + while (__p && _M_node == __p->_M_left) // see below + { + _M_node = __p; + __p = _M_node->_M_parent; + } + if (__p) // (__p) provide undetermined behavior on rend()++ rather + // than a seg fault, similar to standard iterator. + _M_node = __p; + } + } + + template + friend class KDTree; + }; + + template + class _Iterator : protected _Base_iterator + { + public: + typedef _Val value_type; + typedef _Ref reference; + typedef _Ptr pointer; + typedef _Iterator<_Val, _Val&, _Val*> iterator; + typedef _Iterator<_Val, _Val const&, _Val const*> const_iterator; + typedef _Iterator<_Val, _Ref, _Ptr> _Self; + typedef _Node<_Val> const* _Link_const_type; + typedef std::bidirectional_iterator_tag iterator_category; + typedef ptrdiff_t difference_type; + + inline _Iterator() + : _Base_iterator() {} + inline _Iterator(_Link_const_type const __N) + : _Base_iterator(__N) {} + inline _Iterator(iterator const& __THAT) + : _Base_iterator(__THAT) {} + + _Link_const_type get_raw_node() const + { + return _Link_const_type(_M_node); + } + + reference + operator*() const + { + return _Link_const_type(_M_node)->_M_value; + } + + pointer + operator->() const + { + return &(operator*()); + } + + _Self + operator++() + { + _M_increment(); + return *this; + } + + _Self + operator++(int) + { + _Self ret = *this; + _M_increment(); + return ret; + } + + _Self& + operator--() + { + _M_decrement(); + return *this; + } + + _Self + operator--(int) + { + _Self ret = *this; + _M_decrement(); + return ret; + } + + friend bool + operator== <>(_Iterator<_Val, _Ref, _Ptr> const&, + _Iterator<_Val, _Ref, _Ptr> const&); + + friend bool + operator== <>(_Iterator<_Val, const _Val&, const _Val*> const&, + _Iterator<_Val, _Val&, _Val*> const&); + + friend bool + operator== <>(_Iterator<_Val, _Val&, _Val*> const&, + _Iterator<_Val, const _Val&, const _Val*> const&); + + friend bool + operator!= <>(_Iterator<_Val, _Ref, _Ptr> const&, + _Iterator<_Val, _Ref, _Ptr> const&); + + friend bool + operator!= <>(_Iterator<_Val, const _Val&, const _Val*> const&, + _Iterator<_Val, _Val&, _Val*> const&); + + friend bool + operator!= <>(_Iterator<_Val, _Val&, _Val*> const&, + _Iterator<_Val, const _Val&, const _Val*> const&); + }; + + template + inline bool + operator==(_Iterator<_Val, _Ref, _Ptr> const& __X, + _Iterator<_Val, _Ref, _Ptr> const& __Y) + { return __X._M_node == __Y._M_node; } + + template + inline bool + operator==(_Iterator<_Val, const _Val&, const _Val*> const& __X, + _Iterator<_Val, _Val&, _Val*> const& __Y) + { return __X._M_node == __Y._M_node; } + + template + inline bool + operator==(_Iterator<_Val, _Val&, _Val*> const& __X, + _Iterator<_Val, const _Val&, const _Val*> const& __Y) + { return __X._M_node == __Y._M_node; } + + template + inline bool + operator!=(_Iterator<_Val, _Ref, _Ptr> const& __X, + _Iterator<_Val, _Ref, _Ptr> const& __Y) + { return __X._M_node != __Y._M_node; } + + template + inline bool + operator!=(_Iterator<_Val, const _Val&, const _Val*> const& __X, + _Iterator<_Val, _Val&, _Val*> const& __Y) + { return __X._M_node != __Y._M_node; } + + template + inline bool + operator!=(_Iterator<_Val, _Val&, _Val*> const& __X, + _Iterator<_Val, const _Val&, const _Val*> const& __Y) + { return __X._M_node != __Y._M_node; } + +} // namespace KDTree + +#endif // include guard + +/* COPYRIGHT -- + * + * This file is part of libkdtree++, a C++ template KD-Tree sorting container. + * libkdtree++ is (c) 2004-2007 Martin F. Krafft + * and Sylvain Bougerel distributed under the + * terms of the Artistic License 2.0. See the ./COPYING file in the source tree + * root for more information. + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ diff --git a/src/3rdParty/libkdtree/kdtree++/kdtree.hpp b/src/3rdParty/libkdtree/kdtree++/kdtree.hpp new file mode 100644 index 0000000000..501bfd518a --- /dev/null +++ b/src/3rdParty/libkdtree/kdtree++/kdtree.hpp @@ -0,0 +1,1247 @@ +/** \file + * Defines the interface for the KDTree class. + * + * \author Martin F. Krafft + * + * Paul Harris figured this stuff out (below) + * Notes: + * This is similar to a binary tree, but its not the same. + * There are a few important differences: + * + * * Each level is sorted by a different criteria (this is fundamental to the design). + * + * * It is possible to have children IDENTICAL to its parent in BOTH branches + * This is different to a binary tree, where identical children are always to the right + * So, KDTree has the relationships: + * * The left branch is <= its parent (in binary tree, this relationship is a plain < ) + * * The right branch is <= its parent (same as binary tree) + * + * This is done for mostly for performance. + * Its a LOT easier to maintain a consistent tree if we use the <= relationship. + * Note that this relationship only makes a difference when searching for an exact + * item with find() or find_exact, other search, erase and insert functions don't notice + * the difference. + * + * In the case of binary trees, you can safely assume that the next identical item + * will be the child leaf, + * but in the case of KDTree, the next identical item might + * be a long way down a subtree, because of the various different sort criteria. + * + * So erase()ing a node from a KDTree could require serious and complicated + * tree rebalancing to maintain consistency... IF we required binary-tree-like relationships. + * + * This has no effect on insert()s, a < test is good enough to keep consistency. + * + * It has an effect on find() searches: + * * Instead of using compare(child,node) for a < relationship and following 1 branch, + * we must use !compare(node,child) for a <= relationship, and test BOTH branches, as + * we could potentially go down both branches. + * + * It has no real effect on bounds-based searches (like find_nearest, find_within_range) + * as it compares vs a boundary and would follow both branches if required. + * + * This has no real effect on erase()s, a < test is good enough to keep consistency. + */ + +#ifndef INCLUDE_KDTREE_KDTREE_HPP +#define INCLUDE_KDTREE_KDTREE_HPP + + +// +// This number is guarenteed to change with every release. +// +// KDTREE_VERSION % 100 is the patch level +// KDTREE_VERSION / 100 % 1000 is the minor version +// KDTREE_VERSION / 100000 is the major version +#define KDTREE_VERSION 700 +// +// KDTREE_LIB_VERSION must be defined to be the same as KDTREE_VERSION +// but as a *string* in the form "x_y[_z]" where x is the major version +// number, y is the minor version number, and z is the patch level if not 0. +#define KDTREE_LIB_VERSION "0_7_0" + + +#include + +#ifdef KDTREE_CHECK_PERFORMANCE_COUNTERS +# include +#endif +#include + +#ifdef KDTREE_DEFINE_OSTREAM_OPERATORS +# include +# include +#endif + +#include +#include +#include + +#include "function.hpp" +#include "allocator.hpp" +#include "iterator.hpp" +#include "node.hpp" +#include "region.hpp" + +namespace KDTree +{ + +#ifdef KDTREE_CHECK_PERFORMANCE + unsigned long long num_dist_calcs = 0; +#endif + + template , + typename _Dist = squared_difference, + typename _Cmp = std::less, + typename _Alloc = std::allocator<_Node<_Val> > > + class KDTree : protected _Alloc_base<_Val, _Alloc> + { + protected: + typedef _Alloc_base<_Val, _Alloc> _Base; + typedef typename _Base::allocator_type allocator_type; + + typedef _Node_base* _Base_ptr; + typedef _Node_base const* _Base_const_ptr; + typedef _Node<_Val>* _Link_type; + typedef _Node<_Val> const* _Link_const_type; + + typedef _Node_compare<_Val, _Acc, _Cmp> _Node_compare_; + + public: + typedef _Region<__K, _Val, typename _Acc::result_type, _Acc, _Cmp> + _Region_; + typedef _Val value_type; + typedef value_type* pointer; + typedef value_type const* const_pointer; + typedef value_type& reference; + typedef value_type const& const_reference; + typedef typename _Acc::result_type subvalue_type; + typedef typename _Dist::distance_type distance_type; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + KDTree(_Acc const& __acc = _Acc(), _Dist const& __dist = _Dist(), + _Cmp const& __cmp = _Cmp(), const allocator_type& __a = allocator_type()) + : _Base(__a), _M_header(), + _M_count(0), _M_acc(__acc), _M_cmp(__cmp), _M_dist(__dist) + { + _M_empty_initialise(); + } + + KDTree(const KDTree& __x) + : _Base(__x.get_allocator()), _M_header(), _M_count(0), + _M_acc(__x._M_acc), _M_cmp(__x._M_cmp), _M_dist(__x._M_dist) + { + _M_empty_initialise(); + // this is slow: + // this->insert(begin(), __x.begin(), __x.end()); + // this->optimise(); + + // this is much faster, as it skips a lot of useless work + // do the optimisation before inserting + // Needs to be stored in a vector first as _M_optimise() + // sorts the data in the passed iterators directly. + std::vector temp; + temp.reserve(__x.size()); + std::copy(__x.begin(),__x.end(),std::back_inserter(temp)); + _M_optimise(temp.begin(), temp.end(), 0); + } + + template + KDTree(_InputIterator __first, _InputIterator __last, + _Acc const& acc = _Acc(), _Dist const& __dist = _Dist(), + _Cmp const& __cmp = _Cmp(), const allocator_type& __a = allocator_type()) + : _Base(__a), _M_header(), _M_count(0), + _M_acc(acc), _M_cmp(__cmp), _M_dist(__dist) + { + _M_empty_initialise(); + // this is slow: + // this->insert(begin(), __first, __last); + // this->optimise(); + + // this is much faster, as it skips a lot of useless work + // do the optimisation before inserting + // Needs to be stored in a vector first as _M_optimise() + // sorts the data in the passed iterators directly. + std::vector temp; + temp.reserve(std::distance(__first,__last)); + std::copy(__first,__last,std::back_inserter(temp)); + _M_optimise(temp.begin(), temp.end(), 0); + + // NOTE: this will BREAK users that are passing in + // read-once data via the iterator... + // We increment __first all the way to __last once within + // the distance() call, and again within the copy() call. + // + // This should end up using some funky C++ concepts or + // type traits to check that the iterators can be used in this way... + } + + + // this will CLEAR the tree and fill it with the contents + // of 'writable_vector'. it will use the passed vector directly, + // and will basically resort the vector many times over while + // optimising the tree. + // + // Paul: I use this when I have already built up a vector of data + // that I want to add, and I don't mind if its contents get shuffled + // by the kdtree optimise routine. + void efficient_replace_and_optimise( std::vector & writable_vector ) + { + this->clear(); + _M_optimise(writable_vector.begin(), writable_vector.end(), 0); + } + + + + KDTree& + operator=(const KDTree& __x) + { + if (this != &__x) + { + _M_acc = __x._M_acc; + _M_dist = __x._M_dist; + _M_cmp = __x._M_cmp; + // this is slow: + // this->insert(begin(), __x.begin(), __x.end()); + // this->optimise(); + + // this is much faster, as it skips a lot of useless work + // do the optimisation before inserting + // Needs to be stored in a vector first as _M_optimise() + // sorts the data in the passed iterators directly. + std::vector temp; + temp.reserve(__x.size()); + std::copy(__x.begin(),__x.end(),std::back_inserter(temp)); + efficient_replace_and_optimise(temp); + } + return *this; + } + + ~KDTree() + { + this->clear(); + } + + allocator_type + get_allocator() const + { + return _Base::get_allocator(); + } + + size_type + size() const + { + return _M_count; + } + + size_type + max_size() const + { + return size_type(-1); + } + + bool + empty() const + { + return this->size() == 0; + } + + void + clear() + { + _M_erase_subtree(_M_get_root()); + _M_set_leftmost(&_M_header); + _M_set_rightmost(&_M_header); + _M_set_root(NULL); + _M_count = 0; + } + + /*! \brief Comparator for the values in the KDTree. + + The comparator shall not be modified, it could invalidate the tree. + \return a copy of the comparator used by the KDTree. + */ + _Cmp + value_comp() const + { return _M_cmp; } + + /*! \brief Accessor to the value's elements. + + This accessor shall not be modified, it could invalidate the tree. + \return a copy of the accessor used by the KDTree. + */ + _Acc + value_acc() const + { return _M_acc; } + + /*! \brief Distance calculator between 2 value's element. + + This functor can be modified. It's modification will only affect the + behavior of the find and find_nearest functions. + \return a reference to the distance calculator used by the KDTree. + */ + const _Dist& + value_distance() const + { return _M_dist; } + + _Dist& + value_distance() + { return _M_dist; } + + // typedef _Iterator<_Val, reference, pointer> iterator; + typedef _Iterator<_Val, const_reference, const_pointer> const_iterator; + // No mutable iterator at this stage + typedef const_iterator iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef std::reverse_iterator reverse_iterator; + + // Note: the static_cast in end() is invalid (_M_header is not convertable to a _Link_type), but + // thats ok as it just means undefined behaviour if the user dereferences the end() iterator. + + const_iterator begin() const { return const_iterator(_M_get_leftmost()); } + const_iterator end() const { return const_iterator(static_cast<_Link_const_type>(&_M_header)); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + iterator + insert(iterator /* ignored */, const_reference __V) + { + return this->insert(__V); + } + + iterator + insert(const_reference __V) + { + if (!_M_get_root()) + { + _Link_type __n = _M_new_node(__V, &_M_header); + ++_M_count; + _M_set_root(__n); + _M_set_leftmost(__n); + _M_set_rightmost(__n); + return iterator(__n); + } + return _M_insert(_M_get_root(), __V, 0); + } + + template + void insert(_InputIterator __first, _InputIterator __last) { + for (; __first != __last; ++__first) + this->insert(*__first); + } + + void + insert(iterator __pos, size_type __n, const value_type& __x) + { + for (; __n > 0; --__n) + this->insert(__pos, __x); + } + + template + void + insert(iterator __pos, _InputIterator __first, _InputIterator __last) { + for (; __first != __last; ++__first) + this->insert(__pos, *__first); + } + + // Note: this uses the find() to location the item you want to erase. + // find() compares by equivalence of location ONLY. See the comments + // above find_exact() for why you may not want this. + // + // If you want to erase ANY item that has the same location as __V, + // then use this function. + // + // If you want to erase a PARTICULAR item, and not any other item + // that might happen to have the same location, then you should use + // erase_exact(). + void + erase(const_reference __V) { + const_iterator b = this->find(__V); + this->erase(b); + } + + void + erase_exact(const_reference __V) { + this->erase(this->find_exact(__V)); + } + + // note: kept as const because its easier to const-cast it away + void + erase(const_iterator const& __IT) + { + assert(__IT != this->end()); + _Link_const_type target = __IT.get_raw_node(); + _Link_const_type n = target; + size_type level = 0; + while ((n = _S_parent(n)) != &_M_header) + ++level; + _M_erase( const_cast<_Link_type>(target), level ); + _M_delete_node( const_cast<_Link_type>(target) ); + --_M_count; + } + +/* this does not work since erasure changes sort order + void + erase(const_iterator __A, const_iterator const& __B) + { + if (0 && __A == this->begin() && __B == this->end()) + { + this->clear(); + } + else + { + while (__A != __B) + this->erase(__A++); + } + } +*/ + + // compares via equivalence + // so if you are looking for any item with the same location, + // according to the standard accessor comparisions, + // then this is the function for you. + template + const_iterator + find(SearchVal const& __V) const + { + if (!_M_get_root()) return this->end(); + return _M_find(_M_get_root(), __V, 0); + } + + // compares via equality + // if you are looking for a particular item in the tree, + // and (for example) it has an ID that is checked via an == comparison + // eg + // struct Item + // { + // size_type unique_id; + // bool operator==(Item const& a, Item const& b) { return a.unique_id == b.unique_id; } + // Location location; + // }; + // Two items may be equivalent in location. find() would return + // either one of them. But no two items have the same ID, so + // find_exact() would always return the item with the same location AND id. + // + template + const_iterator + find_exact(SearchVal const& __V) const + { + if (!_M_get_root()) return this->end(); + return _M_find_exact(_M_get_root(), __V, 0); + } + + size_type + count_within_range(const_reference __V, subvalue_type const __R) const + { + if (!_M_get_root()) return 0; + _Region_ __region(__V, __R, _M_acc, _M_cmp); + return this->count_within_range(__region); + } + + size_type + count_within_range(_Region_ const& __REGION) const + { + if (!_M_get_root()) return 0; + + _Region_ __bounds(__REGION); + return _M_count_within_range(_M_get_root(), + __REGION, __bounds, 0); + } + + template + Visitor + visit_within_range(SearchVal const& V, subvalue_type const R, Visitor visitor) const + { + if (!_M_get_root()) return visitor; + _Region_ region(V, R, _M_acc, _M_cmp); + return this->visit_within_range(region, visitor); + } + + template + Visitor + visit_within_range(_Region_ const& REGION, Visitor visitor) const + { + if (_M_get_root()) + { + _Region_ bounds(REGION); + return _M_visit_within_range(visitor, _M_get_root(), REGION, bounds, 0); + } + return visitor; + } + + const_iterator + find_within_range_iterative(const_reference __a, const_reference __b) + { + return const_iterator(begin()); + } + + template + _OutputIterator + find_within_range(SearchVal const& val, subvalue_type const range, + _OutputIterator out) const + { + if (!_M_get_root()) return out; + _Region_ region(val, range, _M_acc, _M_cmp); + return this->find_within_range(region, out); + } + + template + _OutputIterator + find_within_range(_Region_ const& region, + _OutputIterator out) const + { + if (_M_get_root()) + { + _Region_ bounds(region); + out = _M_find_within_range(out, _M_get_root(), + region, bounds, 0); + } + return out; + } + + template + std::pair + find_nearest (SearchVal const& __val) const + { + if (_M_get_root()) + { + std::pair*, + std::pair > + best = _S_node_nearest (__K, 0, __val, + _M_get_root(), &_M_header, _M_get_root(), + sqrt(_S_accumulate_node_distance + (__K, _M_dist, _M_acc, _M_get_root()->_M_value, __val)), + _M_cmp, _M_acc, _M_dist, + always_true()); + return std::pair + (best.first, best.second.second); + } + return std::pair(end(), 0); + } + + template + std::pair + find_nearest (SearchVal const& __val, distance_type __max) const + { + if (_M_get_root()) + { + bool root_is_candidate = false; + const _Node<_Val>* node = _M_get_root(); + { // scope to ensure we don't use 'root_dist' anywhere else + distance_type root_dist = sqrt(_S_accumulate_node_distance + (__K, _M_dist, _M_acc, _M_get_root()->_M_value, __val)); + if (root_dist <= __max) + { + root_is_candidate = true; + __max = root_dist; + } + } + std::pair*, + std::pair > + best = _S_node_nearest (__K, 0, __val, _M_get_root(), &_M_header, + node, __max, _M_cmp, _M_acc, _M_dist, + always_true()); + // make sure we didn't just get stuck with the root node... + if (root_is_candidate || best.first != _M_get_root()) + return std::pair + (best.first, best.second.second); + } + return std::pair(end(), __max); + } + + template + std::pair + find_nearest_if (SearchVal const& __val, distance_type __max, + _Predicate __p) const + { + if (_M_get_root()) + { + bool root_is_candidate = false; + const _Node<_Val>* node = _M_get_root(); + if (__p(_M_get_root()->_M_value)) + { + { // scope to ensure we don't use root_dist anywhere else + distance_type root_dist = sqrt(_S_accumulate_node_distance + (__K, _M_dist, _M_acc, _M_get_root()->_M_value, __val)); + if (root_dist <= __max) + { + root_is_candidate = true; + root_dist = __max; + } + } + } + std::pair*, + std::pair > + best = _S_node_nearest (__K, 0, __val, _M_get_root(), &_M_header, + node, __max, _M_cmp, _M_acc, _M_dist, __p); + // make sure we didn't just get stuck with the root node... + if (root_is_candidate || best.first != _M_get_root()) + return std::pair + (best.first, best.second.second); + } + return std::pair(end(), __max); + } + + void + optimise() + { + std::vector __v(this->begin(),this->end()); + this->clear(); + _M_optimise(__v.begin(), __v.end(), 0); + } + + void + optimize() + { // cater for people who cannot spell :) + this->optimise(); + } + + void check_tree() + { + _M_check_node(_M_get_root(),0); + } + + protected: + + void _M_check_children( _Link_const_type child, _Link_const_type parent, size_type const level, bool to_the_left ) + { + assert(parent); + if (child) + { + _Node_compare_ compare(level % __K, _M_acc, _M_cmp); + // REMEMBER! its a <= relationship for BOTH branches + // for left-case (true), child<=node --> !(node !(child_M_value,child->_M_value)); // check the left + assert(to_the_left || !compare(child->_M_value,parent->_M_value)); // check the right + // and recurse down the tree, checking everything + _M_check_children(_S_left(child),parent,level,to_the_left); + _M_check_children(_S_right(child),parent,level,to_the_left); + } + } + + void _M_check_node( _Link_const_type node, size_type const level ) + { + if (node) + { + // (comparing on this level) + // everything to the left of this node must be smaller than this + _M_check_children( _S_left(node), node, level, true ); + // everything to the right of this node must be larger than this + _M_check_children( _S_right(node), node, level, false ); + + _M_check_node( _S_left(node), level+1 ); + _M_check_node( _S_right(node), level+1 ); + } + } + + void _M_empty_initialise() + { + _M_set_leftmost(&_M_header); + _M_set_rightmost(&_M_header); + _M_header._M_parent = NULL; + _M_set_root(NULL); + } + + iterator + _M_insert_left(_Link_type __N, const_reference __V) + { + _S_set_left(__N, _M_new_node(__V)); ++_M_count; + _S_set_parent( _S_left(__N), __N ); + if (__N == _M_get_leftmost()) + _M_set_leftmost( _S_left(__N) ); + return iterator(_S_left(__N)); + } + + iterator + _M_insert_right(_Link_type __N, const_reference __V) + { + _S_set_right(__N, _M_new_node(__V)); ++_M_count; + _S_set_parent( _S_right(__N), __N ); + if (__N == _M_get_rightmost()) + _M_set_rightmost( _S_right(__N) ); + return iterator(_S_right(__N)); + } + + iterator + _M_insert(_Link_type __N, const_reference __V, + size_type const __L) + { + if (_Node_compare_(__L % __K, _M_acc, _M_cmp)(__V, __N->_M_value)) + { + if (!_S_left(__N)) + return _M_insert_left(__N, __V); + return _M_insert(_S_left(__N), __V, __L+1); + } + else + { + if (!_S_right(__N) || __N == _M_get_rightmost()) + return _M_insert_right(__N, __V); + return _M_insert(_S_right(__N), __V, __L+1); + } + } + + _Link_type + _M_erase(_Link_type dead_dad, size_type const level) + { + // find a new step_dad, he will become a drop-in replacement. + _Link_type step_dad = _M_get_erase_replacement(dead_dad, level); + + // tell dead_dad's parent that his new child is step_dad + if (dead_dad == _M_get_root()) + _M_set_root(step_dad); + else if (_S_left(_S_parent(dead_dad)) == dead_dad) + _S_set_left(_S_parent(dead_dad), step_dad); + else + _S_set_right(_S_parent(dead_dad), step_dad); + + // deal with the left and right edges of the tree... + // if the dead_dad was at the edge, then substitude... + // but if there IS no new dead, then left_most is the dead_dad's parent + if (dead_dad == _M_get_leftmost()) + _M_set_leftmost( (step_dad ? step_dad : _S_parent(dead_dad)) ); + if (dead_dad == _M_get_rightmost()) + _M_set_rightmost( (step_dad ? step_dad : _S_parent(dead_dad)) ); + + if (step_dad) + { + // step_dad gets dead_dad's parent + _S_set_parent(step_dad, _S_parent(dead_dad)); + + // first tell the children that step_dad is their new dad + if (_S_left(dead_dad)) + _S_set_parent(_S_left(dead_dad), step_dad); + if (_S_right(dead_dad)) + _S_set_parent(_S_right(dead_dad), step_dad); + + // step_dad gets dead_dad's children + _S_set_left(step_dad, _S_left(dead_dad)); + _S_set_right(step_dad, _S_right(dead_dad)); + } + + return step_dad; + } + + + + _Link_type + _M_get_erase_replacement(_Link_type node, size_type const level) + { + // if 'node' is null, then we can't do any better + if (_S_is_leaf(node)) + return NULL; + + std::pair<_Link_type,size_type> candidate; + // if there is nothing to the left, find a candidate on the right tree + if (!_S_left(node)) + candidate = _M_get_j_min( std::pair<_Link_type,size_type>(_S_right(node),level), level+1); + // ditto for the right + else if ((!_S_right(node))) + candidate = _M_get_j_max( std::pair<_Link_type,size_type>(_S_left(node),level), level+1); + // we have both children ... + else + { + // we need to do a little more work in order to find a good candidate + // this is actually a technique used to choose a node from either the + // left or right branch RANDOMLY, so that the tree has a greater change of + // staying balanced. + // If this were a true binary tree, we would always hunt down the right branch. + // See top for notes. + _Node_compare_ compare(level % __K, _M_acc, _M_cmp); + // compare the children based on this level's criteria... + // (this gives virtually random results) + if (compare(_S_right(node)->_M_value, _S_left(node)->_M_value)) + // the right is smaller, get our replacement from the SMALLEST on the right + candidate = _M_get_j_min(std::pair<_Link_type,size_type>(_S_right(node),level), level+1); + else + candidate = _M_get_j_max( std::pair<_Link_type,size_type>(_S_left(node),level), level+1); + } + + // we have a candidate replacement by now. + // remove it from the tree, but don't delete it. + // it must be disconnected before it can be reconnected. + _Link_type parent = _S_parent(candidate.first); + if (_S_left(parent) == candidate.first) + _S_set_left(parent, _M_erase(candidate.first, candidate.second)); + else + _S_set_right(parent, _M_erase(candidate.first, candidate.second)); + + return candidate.first; + } + + + + std::pair<_Link_type,size_type> + _M_get_j_min( std::pair<_Link_type,size_type> const node, size_type const level) + { + typedef std::pair<_Link_type,size_type> Result; + if (_S_is_leaf(node.first)) + return Result(node.first,level); + + _Node_compare_ compare(node.second % __K, _M_acc, _M_cmp); + Result candidate = node; + if (_S_left(node.first)) + { + Result left = _M_get_j_min(Result(_S_left(node.first), node.second), level+1); + if (compare(left.first->_M_value, candidate.first->_M_value)) + candidate = left; + } + if (_S_right(node.first)) + { + Result right = _M_get_j_min( Result(_S_right(node.first),node.second), level+1); + if (compare(right.first->_M_value, candidate.first->_M_value)) + candidate = right; + } + if (candidate.first == node.first) + return Result(candidate.first,level); + + return candidate; + } + + + + std::pair<_Link_type,size_type> + _M_get_j_max( std::pair<_Link_type,size_type> const node, size_type const level) + { + typedef std::pair<_Link_type,size_type> Result; + + if (_S_is_leaf(node.first)) + return Result(node.first,level); + + _Node_compare_ compare(node.second % __K, _M_acc, _M_cmp); + Result candidate = node; + if (_S_left(node.first)) + { + Result left = _M_get_j_max( Result(_S_left(node.first),node.second), level+1); + if (compare(candidate.first->_M_value, left.first->_M_value)) + candidate = left; + } + if (_S_right(node.first)) + { + Result right = _M_get_j_max(Result(_S_right(node.first),node.second), level+1); + if (compare(candidate.first->_M_value, right.first->_M_value)) + candidate = right; + } + + if (candidate.first == node.first) + return Result(candidate.first,level); + + return candidate; + } + + + void + _M_erase_subtree(_Link_type __n) + { + while (__n) + { + _M_erase_subtree(_S_right(__n)); + _Link_type __t = _S_left(__n); + _M_delete_node(__n); + __n = __t; + } + } + + const_iterator + _M_find(_Link_const_type node, const_reference value, size_type const level) const + { + // be aware! This is very different to normal binary searches, because of the <= + // relationship used. See top for notes. + // Basically we have to check ALL branches, as we may have an identical node + // in different branches. + const_iterator found = this->end(); + + _Node_compare_ compare(level % __K, _M_acc, _M_cmp); + if (!compare(node->_M_value,value)) // note, this is a <= test + { + // this line is the only difference between _M_find_exact() and _M_find() + if (_M_matches_node(node, value, level)) + return const_iterator(node); // return right away + if (_S_left(node)) + found = _M_find(_S_left(node), value, level+1); + } + if ( _S_right(node) && found == this->end() && !compare(value,node->_M_value)) // note, this is a <= test + found = _M_find(_S_right(node), value, level+1); + return found; + } + + const_iterator + _M_find_exact(_Link_const_type node, const_reference value, size_type const level) const + { + // be aware! This is very different to normal binary searches, because of the <= + // relationship used. See top for notes. + // Basically we have to check ALL branches, as we may have an identical node + // in different branches. + const_iterator found = this->end(); + + _Node_compare_ compare(level % __K, _M_acc, _M_cmp); + if (!compare(node->_M_value,value)) // note, this is a <= test + { + // this line is the only difference between _M_find_exact() and _M_find() + if (value == *const_iterator(node)) + return const_iterator(node); // return right away + if (_S_left(node)) + found = _M_find_exact(_S_left(node), value, level+1); + } + + // note: no else! items that are identical can be down both branches + if ( _S_right(node) && found == this->end() && !compare(value,node->_M_value)) // note, this is a <= test + found = _M_find_exact(_S_right(node), value, level+1); + return found; + } + + bool + _M_matches_node_in_d(_Link_const_type __N, const_reference __V, + size_type const __L) const + { + _Node_compare_ compare(__L % __K, _M_acc, _M_cmp); + return !(compare(__N->_M_value, __V) || compare(__V, __N->_M_value)); + } + + bool + _M_matches_node_in_other_ds(_Link_const_type __N, const_reference __V, + size_type const __L = 0) const + { + size_type __i = __L; + while ((__i = (__i + 1) % __K) != __L % __K) + if (!_M_matches_node_in_d(__N, __V, __i)) return false; + return true; + } + + bool + _M_matches_node(_Link_const_type __N, const_reference __V, + size_type __L = 0) const + { + return _M_matches_node_in_d(__N, __V, __L) + && _M_matches_node_in_other_ds(__N, __V, __L); + } + + size_type + _M_count_within_range(_Link_const_type __N, _Region_ const& __REGION, + _Region_ const& __BOUNDS, + size_type const __L) const + { + size_type count = 0; + if (__REGION.encloses(_S_value(__N))) + { + ++count; + } + if (_S_left(__N)) + { + _Region_ __bounds(__BOUNDS); + __bounds.set_high_bound(_S_value(__N), __L); + if (__REGION.intersects_with(__bounds)) + count += _M_count_within_range(_S_left(__N), + __REGION, __bounds, __L+1); + } + if (_S_right(__N)) + { + _Region_ __bounds(__BOUNDS); + __bounds.set_low_bound(_S_value(__N), __L); + if (__REGION.intersects_with(__bounds)) + count += _M_count_within_range(_S_right(__N), + __REGION, __bounds, __L+1); + } + + return count; + } + + + template + Visitor + _M_visit_within_range(Visitor visitor, + _Link_const_type N, _Region_ const& REGION, + _Region_ const& BOUNDS, + size_type const L) const + { + if (REGION.encloses(_S_value(N))) + { + visitor(_S_value(N)); + } + if (_S_left(N)) + { + _Region_ bounds(BOUNDS); + bounds.set_high_bound(_S_value(N), L); + if (REGION.intersects_with(bounds)) + visitor = _M_visit_within_range(visitor, _S_left(N), + REGION, bounds, L+1); + } + if (_S_right(N)) + { + _Region_ bounds(BOUNDS); + bounds.set_low_bound(_S_value(N), L); + if (REGION.intersects_with(bounds)) + visitor = _M_visit_within_range(visitor, _S_right(N), + REGION, bounds, L+1); + } + + return visitor; + } + + + + template + _OutputIterator + _M_find_within_range(_OutputIterator out, + _Link_const_type __N, _Region_ const& __REGION, + _Region_ const& __BOUNDS, + size_type const __L) const + { + if (__REGION.encloses(_S_value(__N))) + { + *out++ = _S_value(__N); + } + if (_S_left(__N)) + { + _Region_ __bounds(__BOUNDS); + __bounds.set_high_bound(_S_value(__N), __L); + if (__REGION.intersects_with(__bounds)) + out = _M_find_within_range(out, _S_left(__N), + __REGION, __bounds, __L+1); + } + if (_S_right(__N)) + { + _Region_ __bounds(__BOUNDS); + __bounds.set_low_bound(_S_value(__N), __L); + if (__REGION.intersects_with(__bounds)) + out = _M_find_within_range(out, _S_right(__N), + __REGION, __bounds, __L+1); + } + + return out; + } + + + template + void + _M_optimise(_Iter const& __A, _Iter const& __B, + size_type const __L) + { + if (__A == __B) return; + _Node_compare_ compare(__L % __K, _M_acc, _M_cmp); + _Iter __m = __A + (__B - __A) / 2; + std::nth_element(__A, __m, __B, compare); + this->insert(*__m); + if (__m != __A) _M_optimise(__A, __m, __L+1); + if (++__m != __B) _M_optimise(__m, __B, __L+1); + } + + _Link_const_type + _M_get_root() const + { + return const_cast<_Link_const_type>(_M_root); + } + + _Link_type + _M_get_root() + { + return _M_root; + } + + void _M_set_root(_Link_type n) + { + _M_root = n; + } + + _Link_const_type + _M_get_leftmost() const + { + return static_cast<_Link_type>(_M_header._M_left); + } + + void + _M_set_leftmost( _Node_base * a ) + { + _M_header._M_left = a; + } + + _Link_const_type + _M_get_rightmost() const + { + return static_cast<_Link_type>( _M_header._M_right ); + } + + void + _M_set_rightmost( _Node_base * a ) + { + _M_header._M_right = a; + } + + static _Link_type + _S_parent(_Base_ptr N) + { + return static_cast<_Link_type>( N->_M_parent ); + } + + static _Link_const_type + _S_parent(_Base_const_ptr N) + { + return static_cast<_Link_const_type>( N->_M_parent ); + } + + static void + _S_set_parent(_Base_ptr N, _Base_ptr p) + { + N->_M_parent = p; + } + + static void + _S_set_left(_Base_ptr N, _Base_ptr l) + { + N->_M_left = l; + } + + static _Link_type + _S_left(_Base_ptr N) + { + return static_cast<_Link_type>( N->_M_left ); + } + + static _Link_const_type + _S_left(_Base_const_ptr N) + { + return static_cast<_Link_const_type>( N->_M_left ); + } + + static void + _S_set_right(_Base_ptr N, _Base_ptr r) + { + N->_M_right = r; + } + + static _Link_type + _S_right(_Base_ptr N) + { + return static_cast<_Link_type>( N->_M_right ); + } + + static _Link_const_type + _S_right(_Base_const_ptr N) + { + return static_cast<_Link_const_type>( N->_M_right ); + } + + static bool + _S_is_leaf(_Base_const_ptr N) + { + return !_S_left(N) && !_S_right(N); + } + + static const_reference + _S_value(_Link_const_type N) + { + return N->_M_value; + } + + static const_reference + _S_value(_Base_const_ptr N) + { + return static_cast<_Link_const_type>(N)->_M_value; + } + + static _Link_const_type + _S_minimum(_Link_const_type __X) + { + return static_cast<_Link_const_type> ( _Node_base::_S_minimum(__X) ); + } + + static _Link_const_type + _S_maximum(_Link_const_type __X) + { + return static_cast<_Link_const_type>( _Node_base::_S_maximum(__X) ); + } + + + _Link_type + _M_new_node(const_reference __V, // = value_type(), + _Base_ptr const __PARENT = NULL, + _Base_ptr const __LEFT = NULL, + _Base_ptr const __RIGHT = NULL) + { + typename _Base::NoLeakAlloc noleak(this); + _Link_type new_node = noleak.get(); + _Base::_M_construct_node(new_node, __V, __PARENT, __LEFT, __RIGHT); + noleak.disconnect(); + return new_node; + } + + /* WHAT was this for? + _Link_type + _M_clone_node(_Link_const_type __X) + { + _Link_type __ret = _M_allocate_node(__X->_M_value); + // TODO + return __ret; + } + */ + + void + _M_delete_node(_Link_type __p) + { + _Base::_M_destroy_node(__p); + _Base::_M_deallocate_node(__p); + } + + _Link_type _M_root; + _Node_base _M_header; + size_type _M_count; + _Acc _M_acc; + _Cmp _M_cmp; + _Dist _M_dist; + +#ifdef KDTREE_DEFINE_OSTREAM_OPERATORS + friend std::ostream& + operator<<(std::ostream& o, + KDTree<__K, _Val, _Acc, _Dist, _Cmp, _Alloc> const& tree) + { + o << "meta node: " << tree._M_header << std::endl; + o << "root node: " << tree._M_root << std::endl; + + if (tree.empty()) + return o << "[empty " << __K << "d-tree " << &tree << "]"; + + o << "nodes total: " << tree.size() << std::endl; + o << "dimensions: " << __K << std::endl; + + typedef KDTree<__K, _Val, _Acc, _Dist, _Cmp, _Alloc> _Tree; + typedef typename _Tree::_Link_type _Link_type; + + std::stack<_Link_const_type> s; + s.push(tree._M_get_root()); + + while (!s.empty()) + { + _Link_const_type n = s.top(); + s.pop(); + o << *n << std::endl; + if (_Tree::_S_left(n)) s.push(_Tree::_S_left(n)); + if (_Tree::_S_right(n)) s.push(_Tree::_S_right(n)); + } + + return o; + } +#endif + + }; + + +} // namespace KDTree + +#endif // include guard + +/* COPYRIGHT -- + * + * This file is part of libkdtree++, a C++ template KD-Tree sorting container. + * libkdtree++ is (c) 2004-2007 Martin F. Krafft + * and Sylvain Bougerel distributed under the + * terms of the Artistic License 2.0. See the ./COPYING file in the source tree + * root for more information. + * Parts of this file are (c) 2004-2007 Paul Harris . + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ diff --git a/src/3rdParty/libkdtree/kdtree++/node.hpp b/src/3rdParty/libkdtree/kdtree++/node.hpp new file mode 100644 index 0000000000..b7eddb766e --- /dev/null +++ b/src/3rdParty/libkdtree/kdtree++/node.hpp @@ -0,0 +1,362 @@ +/** \file + * Defines interfaces for nodes as used by the KDTree class. + * + * \author Martin F. Krafft + */ + +#ifndef INCLUDE_KDTREE_NODE_HPP +#define INCLUDE_KDTREE_NODE_HPP + +#ifdef KDTREE_DEFINE_OSTREAM_OPERATORS +# include +#endif + +#include +#include + +namespace KDTree +{ + struct _Node_base + { + typedef _Node_base* _Base_ptr; + typedef _Node_base const* _Base_const_ptr; + + _Base_ptr _M_parent; + _Base_ptr _M_left; + _Base_ptr _M_right; + + _Node_base(_Base_ptr const __PARENT = NULL, + _Base_ptr const __LEFT = NULL, + _Base_ptr const __RIGHT = NULL) + : _M_parent(__PARENT), _M_left(__LEFT), _M_right(__RIGHT) {} + + static _Base_ptr + _S_minimum(_Base_ptr __x) + { + while (__x->_M_left) __x = __x->_M_left; + return __x; + } + + static _Base_ptr + _S_maximum(_Base_ptr __x) + { + while (__x->_M_right) __x = __x->_M_right; + return __x; + } + }; + + template + struct _Node : public _Node_base + { + using _Node_base::_Base_ptr; + typedef _Node* _Link_type; + + _Val _M_value; + + _Node(_Val const& __VALUE = _Val(), + _Base_ptr const __PARENT = NULL, + _Base_ptr const __LEFT = NULL, + _Base_ptr const __RIGHT = NULL) + : _Node_base(__PARENT, __LEFT, __RIGHT), _M_value(__VALUE) {} + +#ifdef KDTREE_DEFINE_OSTREAM_OPERATORS + + template + friend + std::basic_ostream& + operator<<(typename std::basic_ostream& out, + _Node_base const& node) + { + out << &node; + out << " parent: " << node._M_parent; + out << "; left: " << node._M_left; + out << "; right: " << node._M_right; + return out; + } + + template + friend + std::basic_ostream& + operator<<(typename std::basic_ostream& out, + _Node<_Val> const& node) + { + out << &node; + out << ' ' << node._M_value; + out << "; parent: " << node._M_parent; + out << "; left: " << node._M_left; + out << "; right: " << node._M_right; + return out; + } + +#endif + }; + + template + class _Node_compare + { + public: + _Node_compare(size_t const __DIM, _Acc const& acc, _Cmp const& cmp) + : _M_DIM(__DIM), _M_acc(acc), _M_cmp(cmp) {} + + bool + operator()(_Val const& __A, _Val const& __B) const + { + return _M_cmp(_M_acc(__A, _M_DIM), _M_acc(__B, _M_DIM)); + } + + private: + size_t _M_DIM; // don't make this const so that an assignment operator can be auto-generated + _Acc _M_acc; + _Cmp _M_cmp; + }; + + /*! Compare two values on the same dimension using a comparison functor _Cmp + and an accessor _Acc. + + The comparison functor and the accessor are references to the template + parameters of the KDTree. + */ + template + inline + bool + _S_node_compare (const size_t __dim, + const _Cmp& __cmp, const _Acc& __acc, + const _ValA& __a, const _ValB& __b) + { + return __cmp(__acc(__a, __dim), __acc(__b, __dim)); + } + + /*! Compute the distance between two values for one dimension only. + + The distance functor and the accessor are references to the template + parameters of the KDTree. + */ + template + inline + typename _Dist::distance_type + _S_node_distance (const size_t __dim, + const _Dist& __dist, const _Acc& __acc, + const _ValA& __a, const _ValB& __b) + { + return __dist(__acc(__a, __dim), __acc(__b, __dim)); + } + + /*! Compute the distance between two values and accumulate the result for all + dimensions. + + The distance functor and the accessor are references to the template + parameters of the KDTree. + */ + template + inline + typename _Dist::distance_type + _S_accumulate_node_distance (const size_t __dim, + const _Dist& __dist, const _Acc& __acc, + const _ValA& __a, const _ValB& __b) + { + typename _Dist::distance_type d = 0; + for (size_t i=0; i<__dim; ++i) + d += __dist(__acc(__a, i), __acc(__b, i)); + return d; + } + + /*! Descend on the left or the right of the node according to the comparison + between the node's value and the value. + + \note it's the caller responsibility to check if node is NULL. + */ + template + inline + _Node_base* + _S_node_descend (const size_t __dim, + const _Cmp& __cmp, const _Acc& __acc, + const _Val& __val, const _Node_base* __node) + { + if (_S_node_compare(__dim, __cmp, __acc, __val, static_cast* >(__node)->_M_value)) + return __node->_M_left; + else + return __node->_M_right; + } + + /*! Find the nearest node to __val from __node + + If many nodes are equidistant to __val, the node with the lowest memory + address is returned. + + \return the nearest node of __end node if no nearest node was found for the + given arguments. + */ + template + inline + std::pair*, + std::pair > + _S_node_nearest (const size_t __k, size_t __dim, SearchVal const& __val, + const _Node<_Val>* __node, const _Node_base* __end, + const _Node<_Val>* __best, typename _Dist::distance_type __max, + const _Cmp& __cmp, const _Acc& __acc, const _Dist& __dist, + _Predicate __p) + { + const _Node_base* pcur = __node; + const _Node_base* cur = _S_node_descend(__dim % __k, __cmp, __acc, __val, __node); + size_t cur_dim = __dim+1; + // find the smallest __max distance in direct descent + while (cur) + { + if (__p(static_cast* >(cur)->_M_value)) + { + typename _Dist::distance_type d = 0; + for (size_t i=0; i != __k; ++i) + d += _S_node_distance(i, __dist, __acc, __val, static_cast* >(cur)->_M_value); + d = sqrt(d); + if (d <= __max) + // ("bad candidate notes") + // Changed: removed this test: || ( d == __max && cur < __best )) + // Can't do this optimisation without checking that the current 'best' is not the root AND is not a valid candidate... + // This is because find_nearest() etc will call this function with the best set to _M_root EVEN IF _M_root is not a valid answer (eg too far away or doesn't pass the predicate test) + { + __best = static_cast* >(cur); + __max = d; + __dim = cur_dim; + } + } + pcur = cur; + cur = _S_node_descend(cur_dim % __k, __cmp, __acc, __val, cur); + ++cur_dim; + } + // Swap cur to prev, only prev is a valid node. + cur = pcur; + --cur_dim; + pcur = NULL; + // Probe all node's children not visited yet (siblings of the visited nodes). + const _Node_base* probe = cur; + const _Node_base* pprobe = probe; + const _Node_base* near_node; + const _Node_base* far_node; + size_t probe_dim = cur_dim; + if (_S_node_compare(probe_dim % __k, __cmp, __acc, __val, static_cast* >(probe)->_M_value)) + near_node = probe->_M_right; + else + near_node = probe->_M_left; + if (near_node + // only visit node's children if node's plane intersect hypersphere + && (sqrt(_S_node_distance(probe_dim % __k, __dist, __acc, __val, static_cast* >(probe)->_M_value)) <= __max)) + { + probe = near_node; + ++probe_dim; + } + while (cur != __end) + { + while (probe != cur) + { + if (_S_node_compare(probe_dim % __k, __cmp, __acc, __val, static_cast* >(probe)->_M_value)) + { + near_node = probe->_M_left; + far_node = probe->_M_right; + } + else + { + near_node = probe->_M_right; + far_node = probe->_M_left; + } + if (pprobe == probe->_M_parent) // going downward ... + { + if (__p(static_cast* >(probe)->_M_value)) + { + typename _Dist::distance_type d = 0; + for (size_t i=0; i < __k; ++i) + d += _S_node_distance(i, __dist, __acc, __val, static_cast* >(probe)->_M_value); + d = sqrt(d); + if (d <= __max) // CHANGED, see the above notes ("bad candidate notes") + { + __best = static_cast* >(probe); + __max = d; + __dim = probe_dim; + } + } + pprobe = probe; + if (near_node) + { + probe = near_node; + ++probe_dim; + } + else if (far_node && + // only visit node's children if node's plane intersect hypersphere + sqrt(_S_node_distance(probe_dim % __k, __dist, __acc, __val, static_cast* >(probe)->_M_value)) <= __max) + { + probe = far_node; + ++probe_dim; + } + else + { + probe = probe->_M_parent; + --probe_dim; + } + } + else // ... and going upward. + { + if (pprobe == near_node && far_node + // only visit node's children if node's plane intersect hypersphere + && sqrt(_S_node_distance(probe_dim % __k, __dist, __acc, __val, static_cast* >(probe)->_M_value)) <= __max) + { + pprobe = probe; + probe = far_node; + ++probe_dim; + } + else + { + pprobe = probe; + probe = probe->_M_parent; + --probe_dim; + } + } + } + pcur = cur; + cur = cur->_M_parent; + --cur_dim; + pprobe = cur; + probe = cur; + probe_dim = cur_dim; + if (cur != __end) + { + if (pcur == cur->_M_left) + near_node = cur->_M_right; + else + near_node = cur->_M_left; + if (near_node + // only visit node's children if node's plane intersect hypersphere + && (sqrt(_S_node_distance(cur_dim % __k, __dist, __acc, __val, static_cast* >(cur)->_M_value)) <= __max)) + { + probe = near_node; + ++probe_dim; + } + } + } + return std::pair*, + std::pair > + (__best, std::pair + (__dim, __max)); + } + + +} // namespace KDTree + +#endif // include guard + +/* COPYRIGHT -- + * + * This file is part of libkdtree++, a C++ template KD-Tree sorting container. + * libkdtree++ is (c) 2004-2007 Martin F. Krafft + * and Sylvain Bougerel distributed under the + * terms of the Artistic License 2.0. See the ./COPYING file in the source tree + * root for more information. + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ diff --git a/src/3rdParty/libkdtree/kdtree++/region.hpp b/src/3rdParty/libkdtree/kdtree++/region.hpp new file mode 100644 index 0000000000..98acab1d99 --- /dev/null +++ b/src/3rdParty/libkdtree/kdtree++/region.hpp @@ -0,0 +1,131 @@ +/** \file + * Defines the interface of the _Region class. + * + * \author Martin F. Krafft + */ + +#ifndef INCLUDE_KDTREE_REGION_HPP +#define INCLUDE_KDTREE_REGION_HPP + +#include + +#include + +namespace KDTree +{ + + template + struct _Region + { + typedef _Val value_type; + typedef _SubVal subvalue_type; + + // special typedef for checking against a fuzzy point (for find_nearest) + // Note the region (first) component is not supposed to have an area, its + // bounds should all be set to a specific point. + typedef std::pair<_Region,_SubVal> _CenterPt; + + _Region(_Acc const& __acc=_Acc(), const _Cmp& __cmp=_Cmp()) + : _M_cmp(__acc), _M_acc(__cmp) {} + + template + _Region(Val const& __V, + _Acc const& __acc=_Acc(), const _Cmp& __cmp=_Cmp()) + : _M_acc(__acc), _M_cmp(__cmp) + { + for (size_t __i = 0; __i != __K; ++__i) + { + _M_low_bounds[__i] = _M_high_bounds[__i] = _M_acc(__V,__i); + } + } + + template + _Region(Val const& __V, subvalue_type const& __R, + _Acc const& __acc=_Acc(), const _Cmp& __cmp=_Cmp()) + : _M_acc(__acc), _M_cmp(__cmp) + { + for (size_t __i = 0; __i != __K; ++__i) + { + _M_low_bounds[__i] = _M_acc(__V,__i) - __R; + _M_high_bounds[__i] = _M_acc(__V,__i) + __R; + } + } + + bool + intersects_with(_CenterPt const& __THAT) const + { + for (size_t __i = 0; __i != __K; ++__i) + { + // does it fall outside the bounds? + // ! low-tolerance <= x <= high+tolerance + // ! (low-tol <= x and x <= high+tol) + // !low-tol<=x or !x<=high+tol + // low-tol>x or x>high+tol + // x + * and Sylvain Bougerel distributed under the + * terms of the Artistic License 2.0. See the ./COPYING file in the source tree + * root for more information. + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ diff --git a/src/3rdParty/libkdtree/python-bindings/CMakeLists.txt b/src/3rdParty/libkdtree/python-bindings/CMakeLists.txt new file mode 100644 index 0000000000..4200cf4110 --- /dev/null +++ b/src/3rdParty/libkdtree/python-bindings/CMakeLists.txt @@ -0,0 +1,19 @@ +find_package (SWIG REQUIRED) +include (${SWIG_USE_FILE}) + +find_package (PythonLibs) +include_directories (${PYTHON_INCLUDE_PATH}) + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) +include_directories (${CMAKE_CURRENT_SOURCE_DIR}/..) + +# Build the _kdtree python module +set_source_files_properties (py-kdtree.i PROPERTIES CPLUSPLUS ON) +swig_add_module (kdtree python py-kdtree.i) +swig_link_libraries (kdtree ${PYTHON_LIBRARIES}) + +# Copy the test file into the build dir +install (FILES py-kdtree_test.py DESTINATION ${CMAKE_INSTALL_PREFIX}/python) +install (FILES ${CMAKE_BINARY_DIR}/python-bindings/kdtree.py DESTINATION ${CMAKE_INSTALL_PREFIX}/python) +install (FILES ${CMAKE_BINARY_DIR}/python-bindings/_kdtree.so DESTINATION ${CMAKE_INSTALL_PREFIX}/python) + diff --git a/src/3rdParty/libkdtree/python-bindings/gen-swig-hpp.py b/src/3rdParty/libkdtree/python-bindings/gen-swig-hpp.py new file mode 100644 index 0000000000..c675648425 --- /dev/null +++ b/src/3rdParty/libkdtree/python-bindings/gen-swig-hpp.py @@ -0,0 +1,264 @@ +#!/usr/bin/python + + +TREE_TYPES = [(dim, "int", "unsigned long long", "i", "L") for dim in range(2,7)] + \ + [(dim, "float", "unsigned long long", "f", "L") for dim in range(2,7)] + + +def write_swig_file(tmpl_fn_name, swig_fn_name): + TMPL_SEPARATOR_DEF="""\ +//////////////////////////////////////////////////////////////////////////////// +// TYPE (%s) -> %s +//////////////////////////////////////////////////////////////////////////////// +""" + TMPL_SEPARATOR=[] + + TMPL_RECORD_DEF="""\ +#define RECORD_%i%s%s record_t<%i, %s, %s> // cf. py-kdtree.hpp +""" + TMPL_RECORD=[] + + TMPL_IN_CONV_RECORD_DEF="""\ +%%typemap(in) RECORD_%i%s%s (RECORD_%i%s%s temp) { + if (PyTuple_Check($input)) { + if (PyArg_ParseTuple($input,"(%s)%s", %s, &temp.data)!=0) + { + $1 = temp; + } else { + PyErr_SetString(PyExc_TypeError,"tuple must have %i elements: (%i dim %s vector, %s value)"); + return NULL; + } + + } else { + PyErr_SetString(PyExc_TypeError,"expected a tuple."); + return NULL; + } + } +""" + TMPL_IN_CONV_RECORD=[] + + TMPL_IN_CONV_POINT_DEF="""\ +%%typemap(in) RECORD_%i%s%s::point_t (RECORD_%i%s%s::point_t point) { + if (PyTuple_Check($input)) { + if (PyArg_ParseTuple($input,"%s", %s)!=0) + { + $1 = point; + } else { + PyErr_SetString(PyExc_TypeError,"tuple must contain %i ints"); + return NULL; + } + + } else { + PyErr_SetString(PyExc_TypeError,"expected a tuple."); + return NULL; + } + } + """ + TMPL_IN_CONV_POINT=[] + + + TMPL_OUT_CONV_POINT_DEF="""\ + %%typemap(out) RECORD_%i%s%s * { + RECORD_%i%s%s * r = $1; + PyObject* py_result; + + if (r != NULL) { + + py_result = PyTuple_New(2); + if (py_result==NULL) { + PyErr_SetString(PyErr_Occurred(),"unable to create a tuple."); + return NULL; + } + + if (PyTuple_SetItem(py_result, 0, Py_BuildValue("(%s)", %s))==-1) { + PyErr_SetString(PyErr_Occurred(),"(a) when setting element"); + + Py_DECREF(py_result); + return NULL; + } + + if (PyTuple_SetItem(py_result, 1, Py_BuildValue("%s", r->data))==-1) { + PyErr_SetString(PyErr_Occurred(),"(b) when setting element"); + + Py_DECREF(py_result); + return NULL; + } + } else { + py_result = Py_BuildValue(""); + } + + $result = py_result; + } + """ + TMPL_OUT_CONV_POINT=[] + + TMPL_OUT_CONV_RECORD_DEF="""\ +%%typemap(out) std::vector* { + std::vector* v = $1; + + PyObject* py_result = PyList_New(v->size()); + if (py_result==NULL) { + PyErr_SetString(PyErr_Occurred(),"unable to create a list."); + return NULL; + } + std::vector::const_iterator iter = v->begin(); + + for (size_t i=0; isize(); i++, iter++) { + if (PyList_SetItem(py_result, i, Py_BuildValue("(%s)%s", %s, (*iter).data))==-1) { + PyErr_SetString(PyErr_Occurred(),"(c) when setting element"); + + Py_DECREF(py_result); + return NULL; + } else { + //std::cout << "successfully set element " << *iter << std::endl; + } + } + + $result = py_result; + } +""" + TMPL_OUT_CONV_RECORD=[] + + TMPL_PY_CLASS_DEF="""\ +%%template () RECORD_%i%s%s; +%%template (KDTree_%i%s) PyKDTree<%i, %s, %s>; +""" + TMPL_PY_CLASS=[] + + + TYPE_DEFS = [] + + for t in TREE_TYPES: + dim, coord_t, data_t, py_coord_t, py_data_t = t + coord_t_short = "".join([_[0] for _ in coord_t.split(" ")]) + data_t_short = "".join([_[0] for _ in data_t.split(" ")]) + + TMPL_SEPARATOR.append(TMPL_SEPARATOR_DEF%(",".join([coord_t for _ in range(dim)]), data_t)) + + TMPL_RECORD.append(TMPL_RECORD_DEF%(dim, coord_t_short, data_t_short, dim, coord_t, data_t)) + + TMPL_IN_CONV_RECORD.append(TMPL_IN_CONV_RECORD_DEF%\ + (dim, coord_t_short, data_t_short, + dim, coord_t_short, data_t_short, + py_coord_t*dim, py_data_t, ",".join(["&temp.point[%i]"%i for i in range(dim)]), + dim, dim, coord_t, data_t) + ) + + TMPL_IN_CONV_POINT.append(TMPL_IN_CONV_POINT_DEF%\ + (dim, coord_t_short, data_t_short, + dim, coord_t_short, data_t_short, + py_coord_t*dim, ",".join(["&point[%i]"%i for i in range(dim)]), + dim) + ) + + TMPL_OUT_CONV_RECORD.append(TMPL_OUT_CONV_RECORD_DEF%\ + (dim, coord_t_short, data_t_short, + dim, coord_t_short, data_t_short, + dim, coord_t_short, data_t_short, + py_coord_t*dim, py_data_t, ",".join(["(*iter).point[%i]"%i for i in range(dim)]), + ) + ) + TMPL_OUT_CONV_POINT.append(TMPL_OUT_CONV_POINT_DEF%\ + (dim, coord_t_short, data_t_short, + dim, coord_t_short, data_t_short, + py_coord_t*dim, ",".join(["r->point[%i]"%i for i in range(dim)]), + py_data_t) + ) + + TMPL_PY_CLASS.append(TMPL_PY_CLASS_DEF%\ + (dim, coord_t_short, data_t_short, + dim, coord_t.capitalize(), dim, coord_t, data_t) + ) + + + TMPL_BODY_LIST = [] + for i in range(len(TREE_TYPES)): + TMPL_BODY_LIST.append(TMPL_SEPARATOR[i] + "\n" + \ + TMPL_RECORD[i] + "\n" + \ + TMPL_IN_CONV_POINT[i] + "\n" + \ + TMPL_IN_CONV_RECORD[i] + "\n" + \ + TMPL_OUT_CONV_POINT[i] + "\n" + \ + TMPL_OUT_CONV_RECORD[i]) + + TMPL_BODY = "\n\n".join(TMPL_BODY_LIST) + + # write swig file + i_content = open(tmpl_fn_name, "r").read() + i_content = i_content.replace("%%TMPL_BODY%%", TMPL_BODY).replace("%%TMPL_PY_CLASS_DEF%%", "\n".join(TMPL_PY_CLASS)) + f=open(swig_fn_name, "w") + f.write(i_content) + f.close() + + +def write_hpp_file(tmpl_fn_name, hpp_fn_name): + TMPL_SEPARATOR_DEF="""\ +//////////////////////////////////////////////////////////////////////////////// +// Definition of (%s) with data type %s +//////////////////////////////////////////////////////////////////////////////// +""" + TMPL_SEPARATOR=[] + + TMPL_RECORD_DEF = """\ +#define RECORD_%i%s%s record_t<%i, %s, %s> +#define KDTREE_TYPE_%i%s%s KDTree::KDTree<%i, RECORD_%i%s%s, std::pointer_to_binary_function > +""" + TMPL_RECORD=[] + + TMPL_OP_EQ_DEF = """\ +inline bool operator==(RECORD_%i%s%s const& A, RECORD_%i%s%s const& B) { + return %s && A.data == B.data; +} +""" + TMPL_OP_EQ = [] + + TMPL_OP_OUT_DEF="""\ +std::ostream& operator<<(std::ostream& out, RECORD_%i%s%s const& T) +{ + return out << '(' << %s << '|' << T.data << ')'; +} +""" + TMPL_OP_OUT = [] + + TYPE_DEFS = [] + + for t in TREE_TYPES: + dim, coord_t, data_t, py_coord_t, py_data_t = t + coord_t_short = "".join([_[0] for _ in coord_t.split(" ")]) + data_t_short = "".join([_[0] for _ in data_t.split(" ")]) + + TMPL_SEPARATOR.append(TMPL_SEPARATOR_DEF%(",".join([coord_t for _ in range(dim)]), data_t)) + + TMPL_RECORD.append(TMPL_RECORD_DEF%(dim, coord_t_short, data_t_short, + dim, coord_t, data_t, + dim, coord_t_short, data_t_short, + dim, + dim, coord_t_short, data_t_short, + dim, coord_t_short, data_t_short) + ) + + TMPL_OP_EQ.append(TMPL_OP_EQ_DEF%(dim, coord_t_short, data_t_short, + dim, coord_t_short, data_t_short, + " && ".join(["A.point[%i] == B.point[%i]"%(i,i) for i in range(dim)]))) + + TMPL_OP_OUT.append(TMPL_OP_OUT_DEF%(dim, coord_t_short, data_t_short, + " << ',' << ".join(["T.point[%i]"%i for i in range(dim)]))) + + + TMPL_BODY_LIST = [] + for i in range(len(TREE_TYPES)): + TMPL_BODY_LIST.append(TMPL_SEPARATOR[i] + "\n" + TMPL_RECORD[i] + "\n" + TMPL_OP_EQ[i] + "\n" + TMPL_OP_OUT[i]) + + TMPL_BODY = "\n\n".join(TMPL_BODY_LIST) + + # write hpp file + hpp_content = open(tmpl_fn_name, "r").read() + hpp_content = hpp_content.replace("%%TMPL_HPP_DEFS%%", TMPL_BODY) + f=open(hpp_fn_name, "w") + f.write(hpp_content) + f.close() + + +if __name__=="__main__": + write_swig_file("py-kdtree.i.tmpl", "py-kdtree.i") + write_hpp_file("py-kdtree.hpp.tmpl", "py-kdtree.hpp") + diff --git a/src/3rdParty/libkdtree/python-bindings/py-kdtree.hpp.tmpl b/src/3rdParty/libkdtree/python-bindings/py-kdtree.hpp.tmpl new file mode 100644 index 0000000000..786eee4fe1 --- /dev/null +++ b/src/3rdParty/libkdtree/python-bindings/py-kdtree.hpp.tmpl @@ -0,0 +1,145 @@ +/** \file + * Provides a Python interface for the libkdtree++. + * + * \author Willi Richert + * + * + * This defines a proxy to a (int, int) -> long long KD-Tree. The long + * long is needed to save a reference to Python's object id(). Thereby, + * you can associate Python objects with 2D integer points. + * + * If you want to customize it you can adapt the following: + * + * * Dimension of the KD-Tree point vector. + * * DIM: number of dimensions. + * * operator==() and operator<<(): adapt to the number of comparisons + * * py-kdtree.i: Add or adapt all usages of PyArg_ParseTuple() to reflect the + * number of dimensions. + * * adapt query_records in find_nearest() and count_within_range() + * * Type of points. + * * coord_t: If you want to have e.g. floats you have + * to adapt all usages of PyArg_ParseTuple(): Change "i" to "f" e.g. + * * Type of associated data. + * * data_t: currently unsigned long long, which is "L" in py-kdtree.i + * * PyArg_ParseTuple() has to be changed to reflect changes in data_t + * + */ + + +#ifndef _PY_KDTREE_H_ +#define _PY_KDTREE_H_ + +#include + +#include +#include +#include + +template +struct record_t { + static const size_t dim = DIM; + typedef COORD_T coord_t; + typedef DATA_T data_t; + + typedef coord_t point_t[dim]; + + inline coord_t operator[](size_t const N) const { return point[N]; } + + point_t point; + data_t data; +}; + +typedef double RANGE_T; + +%%TMPL_HPP_DEFS%% + +//////////////////////////////////////////////////////////////////////////////// +// END OF TYPE SPECIFIC DEFINITIONS +//////////////////////////////////////////////////////////////////////////////// + + +template +inline double tac(RECORD_T r, int k) { return r[k]; } + +template +class PyKDTree { +public: + + typedef record_t RECORD_T; + typedef KDTree::KDTree > TREE_T; + TREE_T tree; + + PyKDTree() : tree(std::ptr_fun(tac)) { }; + + void add(RECORD_T T) { tree.insert(T); }; + + /** + Exact erase. + */ + bool remove(RECORD_T T) { + bool removed = false; + + typename TREE_T::const_iterator it = tree.find_exact(T); + if (it!=tree.end()) { + tree.erase_exact(T); + removed = true; + } + return removed; + }; + + int size(void) { return tree.size(); } + + void optimize(void) { tree.optimise(); } + + RECORD_T* find_exact(RECORD_T T) { + RECORD_T* found = NULL; + typename TREE_T::const_iterator it = tree.find_exact(T); + if (it!=tree.end()) + found = new RECORD_T(*it); + + return found; + } + + size_t count_within_range(typename RECORD_T::point_t T, RANGE_T range) { + RECORD_T query_record; + memcpy(query_record.point, T, sizeof(COORD_T)*DIM); + + return tree.count_within_range(query_record, range); + } + + std::vector* find_within_range(typename RECORD_T::point_t T, RANGE_T range) { + RECORD_T query_record; + memcpy(query_record.point, T, sizeof(COORD_T)*DIM); + + std::vector *v = new std::vector; + tree.find_within_range(query_record, range, std::back_inserter(*v)); + return v; + } + + RECORD_T* find_nearest (typename RECORD_T::point_t T) { + RECORD_T* found = NULL; + RECORD_T query_record; + memcpy(query_record.point, T, sizeof(COORD_T)*DIM); + + std::pair best = + tree.find_nearest(query_record, std::numeric_limits::max()); + + if (best.first!=tree.end()) { + found = new RECORD_T(*best.first); + } + return found; + } + + std::vector* get_all() { + std::vector* v = new std::vector; + + for (typename TREE_T::const_iterator iter=tree.begin(); iter!=tree.end(); ++iter) { + v->push_back(*iter); + } + + return v; + } + + size_t __len__() { return tree.size(); } +}; +#endif //_PY_KDTREE_H_ diff --git a/src/3rdParty/libkdtree/python-bindings/py-kdtree.i.tmpl b/src/3rdParty/libkdtree/python-bindings/py-kdtree.i.tmpl new file mode 100644 index 0000000000..e150940d64 --- /dev/null +++ b/src/3rdParty/libkdtree/python-bindings/py-kdtree.i.tmpl @@ -0,0 +1,27 @@ +/** \file + * + * Provides a Python interface for the libkdtree++. + * + * \author Willi Richert + * + */ + +%module kdtree + +%{ +#define SWIG_FILE_WITH_INIT +#include "py-kdtree.hpp" +%} + + +%ignore record_t::operator[]; +%ignore operator==; +%ignore operator<<; +%ignore KDTree::KDTree::operator=; +%ignore tac; + +%%TMPL_BODY%% + +%include "py-kdtree.hpp" + +%%TMPL_PY_CLASS_DEF%% diff --git a/src/3rdParty/libkdtree/python-bindings/py-kdtree_test.cpp b/src/3rdParty/libkdtree/python-bindings/py-kdtree_test.cpp new file mode 100644 index 0000000000..df4df0cd85 --- /dev/null +++ b/src/3rdParty/libkdtree/python-bindings/py-kdtree_test.cpp @@ -0,0 +1,96 @@ +#define KDTREE_DEFINE_OSTREAM_OPERATORS + +#include + +#include +#include + +#include "py-kdtree.hpp" + +int main() +{ + + + KDTree_2Int t; + + RECORD_2il c0 = { {5, 4} }; t.add(c0); + RECORD_2il c1 = { {4, 2} }; t.add(c1); + RECORD_2il c2 = { {7, 6} }; t.add(c2); + RECORD_2il c3 = { {2, 2} }; t.add(c3); + RECORD_2il c4 = { {8, 0} }; t.add(c4); + RECORD_2il c5 = { {5, 7} }; t.add(c5); + RECORD_2il c6 = { {3, 3} }; t.add(c6); + RECORD_2il c7 = { {9, 7} }; t.add(c7); + RECORD_2il c8 = { {2, 2} }; t.add(c8); + RECORD_2il c9 = { {2, 0} }; t.add(c9); + + std::cout << t.tree << std::endl; + + t.remove(c0); + t.remove(c1); + t.remove(c3); + t.remove(c5); + + t.optimize(); + + std::cout << std::endl << t.tree << std::endl; + + int i=0; + for (KDTREE_TYPE_2il::const_iterator iter=t.tree.begin(); iter!=t.tree.end(); ++iter, ++i); + std::cout << "iterator walked through " << i << " nodes in total" << std::endl; + if (i!=6) + { + std::cerr << "Error: does not tally with the expected number of nodes (6)" << std::endl; + return 1; + } + i=0; + for (KDTREE_TYPE_2il::const_reverse_iterator iter=t.tree.rbegin(); iter!=t.tree.rend(); ++iter, ++i); + std::cout << "reverse_iterator walked through " << i << " nodes in total" << std::endl; + if (i!=6) + { + std::cerr << "Error: does not tally with the expected number of nodes (6)" << std::endl; + return 1; + } + + RECORD_2il::point_t s = {5, 4}; + std::vector v; + unsigned int const RANGE = 3; + + size_t count = t.count_within_range(s, RANGE); + std::cout << "counted " << count + << " nodes within range " << RANGE << " of " << s << ".\n"; + v = t.find_within_range(s, RANGE); + + std::cout << "found " << v.size() << " nodes within range " << RANGE + << " of " << s << ":\n"; + std::vector::const_iterator ci = v.begin(); + for (; ci != v.end(); ++ci) + std::cout << *ci << " "; + std::cout << "\n" << std::endl; + + std::cout << "Nearest to " << s << ": " << + t.find_nearest(s) << std::endl; + + RECORD_2il::point_t s2 = { 10, 10}; + std::cout << "Nearest to " << s2 << ": " << + t.find_nearest(s2) << std::endl; + + std::cout << std::endl; + + std::cout << t.tree << std::endl; + + return 0; +} + +/* COPYRIGHT -- + * + * This file is part of libkdtree++, a C++ template KD-Tree sorting container. + * libkdtree++ is (c) 2004-2007 Martin F. Krafft + * and Sylvain Bougerel distributed under the + * terms of the Artistic License 2.0. See the ./COPYING file in the source tree + * root for more information. + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ diff --git a/src/3rdParty/libkdtree/python-bindings/py-kdtree_test.py b/src/3rdParty/libkdtree/python-bindings/py-kdtree_test.py new file mode 100644 index 0000000000..4227b71969 --- /dev/null +++ b/src/3rdParty/libkdtree/python-bindings/py-kdtree_test.py @@ -0,0 +1,400 @@ +# +# $Id: py-kdtree_test.py 2268 2008-08-20 10:08:58Z richert $ +# + +import unittest + +from kdtree import KDTree_2Int, KDTree_4Int, KDTree_3Float, KDTree_4Float, KDTree_6Float + + +class KDTree_2IntTestCase(unittest.TestCase): + def test_empty(self): + nn = KDTree_2Int() + self.assertEqual(0, nn.size()) + + actual = nn.find_nearest((2,3)) + self.assertTrue(None==actual, "%s != %s"%(str(None), str(actual))) + + def test_get_all(self): + nn = KDTree_2Int() + o1 = object() + nn.add(((1,1), id(o1))) + o2 = object() + nn.add(((10,10), id(o2))) + o3 = object() + nn.add(((11,11), id(o3))) + + self.assertEqual([((1,1), id(o1)), ((10,10), id(o2)), ((11,11), id(o3))], nn.get_all()) + self.assertEqual(3, len(nn)) + + nn.remove(((10,10), id(o2))) + self.assertEqual(2, len(nn)) + self.assertEqual([((1,1), id(o1)), ((11,11), id(o3))], nn.get_all()) + + def test_nearest(self): + nn = KDTree_2Int() + + nn_id = {} + + o1 = object() + nn.add(((1,1), id(o1))) + nn_id[id(o1)] = o1 + o2 = object() + nn.add(((10,10), id(o2))) + nn_id[id(o2)] = o2 + + expected = o1 + actual = nn.find_nearest((2,2))[1] + self.assertTrue(expected==nn_id[actual], "%s != %s"%(str(expected), str(nn_id[actual]))) + + expected = o2 + actual = nn.find_nearest((6, 6))[1] + self.assertTrue(expected==nn_id[actual], "%s != %s"%(str(expected), str(nn_id[actual]))) + + def test_find_within_range(self): + nn = KDTree_6Float() + + nn_id = {} + + o1 = object() + nn.add(((1,1,0,0,0,0), id(o1))) + nn_id[id(o1)] = o1 + o2 = object() + nn.add(((10,10,0,0,0,0), id(o2))) + nn_id[id(o2)] = o2 + o3 = object() + nn.add(((4.1, 4.1,0,0,0,0), id(o3))) + nn_id[id(o3)] = o3 + + expected = set([long(id(o1)), long(id(o3))]) + actual = set([ident + for _coord, ident + in nn.find_within_range((2.1,2.1,0,0,0,0), 3.9)]) + self.assertTrue(expected==actual, "%s != %s"%(str(expected), str(actual))) + + + def test_remove(self): + class C: + def __init__(self, i): + self.i = i + self.next = None + + nn = KDTree_2Int() + + k1, o1 = (1,1), C(7) + self.assertFalse(nn.remove((k1, id(o1))), "This cannot be removed!") + nn.add((k1, id(o1))) + + k2, o2 = (1,1), C(7) + nn.add((k2, id(o2))) + + self.assertEqual(2, nn.size()) + self.assertTrue(nn.remove((k2, id(o2)))) + self.assertEqual(1, nn.size()) + self.assertFalse(nn.remove((k2, id(o2)))) + self.assertEqual(1, nn.size()) + + nearest = nn.find_nearest(k1) + self.assertTrue(nearest[1] == id(o1), "%s != %s"%(nearest[1], o1)) + #self.assertTrue(nearest[1] is o1, "%s,%s is not %s"%(str(nearest[0]), str(nearest[1]), str((k1,id(o1))))) + + def test_count_within_range(self): + nn = KDTree_2Int() + + for p in [(0,0), (1,0), (0,1), (1,1)]: + nn.add((p, id(p))) + + res = nn.count_within_range((0,0), 1.0) + self.assertEqual(3, res, "Counted %i points instead of %i"%(res, 3)) + + res = nn.count_within_range((0,0), 1.9) + self.assertEqual(4, res, "Counted %i points instead of %i"%(res, 4)) + +class KDTree_4IntTestCase(unittest.TestCase): + def test_empty(self): + nn = KDTree_4Int() + self.assertEqual(0, nn.size()) + + actual = nn.find_nearest((0,0,2,3)) + self.assertTrue(None==actual, "%s != %s"%(str(None), str(actual))) + + def test_get_all(self): + nn = KDTree_4Int() + o1 = object() + nn.add(((0,0,1,1), id(o1))) + o2 = object() + nn.add(((0,0,10,10), id(o2))) + o3 = object() + nn.add(((0,0,11,11), id(o3))) + + self.assertEqual([((0,0,1,1), id(o1)), ((0,0,10,10), id(o2)), ((0,0,11,11), id(o3))], nn.get_all()) + self.assertEqual(3, len(nn)) + + nn.remove(((0,0,10,10), id(o2))) + self.assertEqual(2, len(nn)) + self.assertEqual([((0,0,1,1), id(o1)), ((0,0,11,11), id(o3))], nn.get_all()) + + def test_nearest(self): + nn = KDTree_4Int() + + nn_id = {} + + o1 = object() + nn.add(((0,0,1,1), id(o1))) + nn_id[id(o1)] = o1 + o2 = object() + nn.add(((0,0,10,10), id(o2))) + nn_id[id(o2)] = o2 + + expected = o1 + actual = nn.find_nearest((0,0,2,2))[1] + self.assertTrue(expected==nn_id[actual], "%s != %s"%(str(expected), str(nn_id[actual]))) + + expected = o2 + actual = nn.find_nearest((0,0,6,6))[1] + self.assertTrue(expected==nn_id[actual], "%s != %s"%(str(expected), str(nn_id[actual]))) + + def test_remove(self): + class C: + def __init__(self, i): + self.i = i + self.next = None + + nn = KDTree_4Int() + + k1, o1 = (0,0,1,1), C(7) + self.assertFalse(nn.remove((k1, id(o1))), "This cannot be removed!") + nn.add((k1, id(o1))) + + k2, o2 = (0,0,1,1), C(7) + nn.add((k2, id(o2))) + + self.assertEqual(2, nn.size()) + self.assertTrue(nn.remove((k2, id(o2)))) + self.assertEqual(1, nn.size()) + self.assertFalse(nn.remove((k2, id(o2)))) + self.assertEqual(1, nn.size()) + + nearest = nn.find_nearest(k1) + self.assertTrue(nearest[1] == id(o1), "%s != %s"%(nearest[1], o1)) + #self.assertTrue(nearest[1] is o1, "%s,%s is not %s"%(str(nearest[0]), str(nearest[1]), str((k1,id(o1))))) + +class KDTree_4FloatTestCase(unittest.TestCase): + def test_empty(self): + nn = KDTree_4Float() + self.assertEqual(0, nn.size()) + + actual = nn.find_nearest((0,0,2,3)) + self.assertTrue(None==actual, "%s != %s"%(str(None), str(actual))) + + def test_get_all(self): + nn = KDTree_4Int() + o1 = object() + nn.add(((0,0,1,1), id(o1))) + o2 = object() + nn.add(((0,0,10,10), id(o2))) + o3 = object() + nn.add(((0,0,11,11), id(o3))) + + self.assertEqual([((0,0,1,1), id(o1)), ((0,0,10,10), id(o2)), ((0,0,11,11), id(o3))], nn.get_all()) + self.assertEqual(3, len(nn)) + + nn.remove(((0,0,10,10), id(o2))) + self.assertEqual(2, len(nn)) + self.assertEqual([((0,0,1,1), id(o1)), ((0,0,11,11), id(o3))], nn.get_all()) + + def test_nearest(self): + nn = KDTree_4Int() + + nn_id = {} + + o1 = object() + nn.add(((0,0,1,1), id(o1))) + nn_id[id(o1)] = o1 + o2 = object() + nn.add(((0,0,10,10), id(o2))) + nn_id[id(o2)] = o2 + + expected = o1 + actual = nn.find_nearest((0,0,2,2))[1] + self.assertTrue(expected==nn_id[actual], "%s != %s"%(str(expected), str(nn_id[actual]))) + + expected = o2 + actual = nn.find_nearest((0,0,6,6))[1] + self.assertTrue(expected==nn_id[actual], "%s != %s"%(str(expected), str(nn_id[actual]))) + + def test_remove(self): + class C: + def __init__(self, i): + self.i = i + self.next = None + + nn = KDTree_4Int() + + k1, o1 = (0,0,1,1), C(7) + self.assertFalse(nn.remove((k1, id(o1))), "This cannot be removed!") + nn.add((k1, id(o1))) + + k2, o2 = (0,0,1,1), C(7) + nn.add((k2, id(o2))) + + self.assertEqual(2, nn.size()) + self.assertTrue(nn.remove((k2, id(o2)))) + self.assertEqual(1, nn.size()) + self.assertFalse(nn.remove((k2, id(o2)))) + self.assertEqual(1, nn.size()) + + nearest = nn.find_nearest(k1) + self.assertTrue(nearest[1] == id(o1), "%s != %s"%(nearest[1], o1)) + #self.assertTrue(nearest[1] is o1, "%s,%s is not %s"%(str(nearest[0]), str(nearest[1]), str((k1,id(o1))))) + +class KDTree_3FloatTestCase(unittest.TestCase): + def test_empty(self): + nn = KDTree_3Float() + self.assertEqual(0, nn.size()) + + actual = nn.find_nearest((2,3,0)) + self.assertTrue(None==actual, "%s != %s"%(str(None), str(actual))) + + def test_get_all(self): + nn = KDTree_3Float() + o1 = object() + nn.add(((1,1,0), id(o1))) + o2 = object() + nn.add(((10,10,0), id(o2))) + o3 = object() + nn.add(((11,11,0), id(o3))) + + self.assertEqual([((1,1,0), id(o1)), ((10,10,0), id(o2)), ((11,11,0), id(o3))], nn.get_all()) + self.assertEqual(3, len(nn)) + + nn.remove(((10,10,0), id(o2))) + self.assertEqual(2, len(nn)) + self.assertEqual([((1,1,0), id(o1)), ((11,11,0), id(o3))], nn.get_all()) + + def test_nearest(self): + nn = KDTree_3Float() + + nn_id = {} + + o1 = object() + nn.add(((1,1,0), id(o1))) + nn_id[id(o1)] = o1 + o2 = object() + nn.add(((10,10,0), id(o2))) + nn_id[id(o2)] = o2 + o3 = object() + nn.add(((4.1, 4.1,0), id(o3))) + nn_id[id(o3)] = o3 + + expected = o3 + actual = nn.find_nearest((2.9,2.9,0))[1] + self.assertTrue(expected==nn_id[actual], "%s != %s"%(str(expected), str(nn_id[actual]))) + + expected = o3 + actual = nn.find_nearest((6, 6,0))[1] + self.assertTrue(expected==nn_id[actual], "%s != %s"%(str(expected), str(nn_id[actual]))) + + def test_remove(self): + class C: + def __init__(self, i): + self.i = i + self.next = None + + nn = KDTree_3Float() + + k1, o1 = (1.1,1.1,0), C(7) + self.assertFalse(nn.remove((k1, id(o1))), "This cannot be removed!") + nn.add((k1, id(o1))) + + k2, o2 = (1.1,1.1,0), C(7) + nn.add((k2, id(o2))) + + self.assertEqual(2, nn.size()) + self.assertTrue(nn.remove((k2, id(o2)))) + self.assertEqual(1, nn.size()) + self.assertFalse(nn.remove((k2, id(o2)))) + self.assertEqual(1, nn.size()) + + nearest = nn.find_nearest(k1) + self.assertTrue(nearest[1] == id(o1), "%s != %s"%(nearest[1], o1)) + #self.assertTrue(nearest[1] is o1, "%s,%s is not %s"%(str(nearest[0]), str(nearest[1]), str((k1,id(o1))))) + +class KDTree_6FloatTestCase(unittest.TestCase): + def test_empty(self): + nn = KDTree_6Float() + self.assertEqual(0, nn.size()) + + actual = nn.find_nearest((2,3,0,0,0,0)) + self.assertTrue(None==actual, "%s != %s"%(str(None), str(actual))) + + def test_get_all(self): + nn = KDTree_6Float() + o1 = object() + nn.add(((1,1,0,0,0,0), id(o1))) + o2 = object() + nn.add(((10,10,0,0,0,0), id(o2))) + o3 = object() + nn.add(((11,11,0,0,0,0), id(o3))) + + self.assertEqual([((1,1,0,0,0,0), id(o1)), ((10,10,0,0,0,0), id(o2)), ((11,11,0,0,0,0 ), id(o3))], nn.get_all()) + self.assertEqual(3, len(nn)) + + nn.remove(((10,10,0,0,0,0), id(o2))) + self.assertEqual(2, len(nn)) + self.assertEqual([((1,1,0,0,0,0), id(o1)), ((11,11,0,0,0,0), id(o3))], nn.get_all()) + + def test_nearest(self): + nn = KDTree_6Float() + + nn_id = {} + + o1 = object() + nn.add(((1,1,0,0,0,0), id(o1))) + nn_id[id(o1)] = o1 + o2 = object() + nn.add(((10,10,0,0,0,0), id(o2))) + nn_id[id(o2)] = o2 + o3 = object() + nn.add(((4.1, 4.1,0,0,0,0), id(o3))) + nn_id[id(o3)] = o3 + + expected = o3 + actual = nn.find_nearest((2.9,2.9,0,0,0,0))[1] + self.assertTrue(expected==nn_id[actual], "%s != %s"%(str(expected), str(nn_id[actual]))) + + expected = o3 + actual = nn.find_nearest((6, 6,0,0,0,0))[1] + self.assertTrue(expected==nn_id[actual], "%s != %s"%(str(expected), str(nn_id[actual]))) + + def test_remove(self): + class C: + def __init__(self, i): + self.i = i + self.next = None + + nn = KDTree_6Float() + + k1, o1 = (1.1,1.1,0,0,0,0), C(7) + self.assertFalse(nn.remove((k1, id(o1))), "This cannot be removed!") + nn.add((k1, id(o1))) + + k2, o2 = (1.1,1.1,0,0,0,0), C(7) + nn.add((k2, id(o2))) + + self.assertEqual(2, nn.size()) + self.assertTrue(nn.remove((k2, id(o2)))) + self.assertEqual(1, nn.size()) + self.assertFalse(nn.remove((k2, id(o2)))) + self.assertEqual(1, nn.size()) + + nearest = nn.find_nearest(k1) + self.assertTrue(nearest[1] == id(o1), "%s != %s"%(nearest[1], o1)) + #self.assertTrue(nearest[1] is o1, "%s,%s is not %s"%(str(nearest[0]), str(nearest[1]), str((k1,id(o1))))) + + +def suite(): + return unittest.defaultTestLoader.loadTestsFromModule(sys.modules.get(__name__)) + +if __name__ == '__main__': + unittest.main() diff --git a/src/Mod/Mesh/App/CMakeLists.txt b/src/Mod/Mesh/App/CMakeLists.txt index b1468c751a..2d3e1eb85c 100644 --- a/src/Mod/Mesh/App/CMakeLists.txt +++ b/src/Mod/Mesh/App/CMakeLists.txt @@ -5,7 +5,7 @@ endif(WIN32) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_SOURCE_DIR}/src/3rdParty + ${CMAKE_SOURCE_DIR}/src/3rdParty/libkdtree ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS} ${XercesC_INCLUDE_DIRS} @@ -50,6 +50,8 @@ SET(Core_SRCS Core/Builder.h Core/Curvature.cpp Core/Curvature.h + Core/Decimation.cpp + Core/Decimation.h Core/Definitions.cpp Core/Definitions.h Core/Degeneration.cpp @@ -64,6 +66,8 @@ SET(Core_SRCS Core/Info.cpp Core/Info.h Core/Iterator.h + Core/KDTree.cpp + Core/KDTree.h Core/MeshIO.cpp Core/MeshIO.h Core/MeshKernel.cpp diff --git a/src/Mod/Mesh/App/Core/Decimation.cpp b/src/Mod/Mesh/App/Core/Decimation.cpp new file mode 100644 index 0000000000..6bb92b6915 --- /dev/null +++ b/src/Mod/Mesh/App/Core/Decimation.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (c) 2013 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +#endif + +#include "Decimation.h" +#include "MeshKernel.h" +#include "Algorithm.h" +#include "Iterator.h" +#include "TopoAlgorithm.h" +#include +#include "Simplify.h" + + +using namespace MeshCore; + +MeshSimplify::MeshSimplify(MeshKernel& mesh) + : myKernel(mesh) +{ +} + +MeshSimplify::~MeshSimplify() +{ +} + +void MeshSimplify::simplify(float tolerance, float reduction) +{ + Simplify alg; + + const MeshPointArray& points = myKernel.GetPoints(); + for (std::size_t i = 0; i < points.size(); i++) { + Simplify::Vertex v; + v.p = points[i]; + alg.vertices.push_back(v); + } + + const MeshFacetArray& facets = myKernel.GetFacets(); + for (std::size_t i = 0; i < facets.size(); i++) { + Simplify::Triangle t; + for (int j = 0; j < 3; j++) + t.v[j] = facets[i]._aulPoints[j]; + alg.triangles.push_back(t); + } + + int target_count = static_cast(static_cast(facets.size()) * (1.0f-reduction)); + + // Simplification starts + alg.simplify_mesh(target_count, tolerance); + + // Simplification done + MeshPointArray new_points; + new_points.reserve(alg.vertices.size()); + for (std::size_t i = 0; i < alg.vertices.size(); i++) { + new_points.push_back(alg.vertices[i].p); + } + + std::size_t numFacets = 0; + for (std::size_t i = 0; i < alg.triangles.size(); i++) { + if (!alg.triangles[i].deleted) + numFacets++; + } + MeshFacetArray new_facets; + new_facets.reserve(numFacets); + for (std::size_t i = 0; i < alg.triangles.size(); i++) { + if (!alg.triangles[i].deleted) { + MeshFacet face; + face._aulPoints[0] = alg.triangles[i].v[0]; + face._aulPoints[1] = alg.triangles[i].v[1]; + face._aulPoints[2] = alg.triangles[i].v[2]; + new_facets.push_back(face); + } + } + + myKernel.Adopt(new_points, new_facets, true); +} diff --git a/src/Mod/Mesh/App/Core/Decimation.h b/src/Mod/Mesh/App/Core/Decimation.h new file mode 100644 index 0000000000..49c3963f8f --- /dev/null +++ b/src/Mod/Mesh/App/Core/Decimation.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (c) 2013 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef MESH_DECIMATION_H +#define MESH_DECIMATION_H + + +namespace MeshCore +{ +class MeshKernel; + +class MeshExport MeshSimplify +{ +public: + MeshSimplify(MeshKernel&); + ~MeshSimplify(); + void simplify(float tolerance, float reduction); + +private: + MeshKernel& myKernel; +}; + +} // namespace MeshCore + + +#endif // MESH_DECIMATION_H diff --git a/src/Mod/Mesh/App/Core/KDTree.cpp b/src/Mod/Mesh/App/Core/KDTree.cpp new file mode 100644 index 0000000000..7192a3cd5f --- /dev/null +++ b/src/Mod/Mesh/App/Core/KDTree.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (c) 2011 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifdef _MSC_VER +# pragma warning(disable : 4396) +#endif +#ifndef _PreComp_ +#endif + +#include "KDTree.h" +#include + +using namespace MeshCore; + +struct Point3d +{ + typedef float value_type; + + Point3d(const Base::Vector3f& f, unsigned long i) : p(f), i(i) + { + } + + inline value_type operator[](const int N) const + { + return p[N]; + } + + inline bool operator==(const Point3d& other) const + { + return (this->p) == (other.p); + } + + inline bool operator!=(const Point3d& other) const + { + return (this->p) != (other.p); + } + + inline void operator=(const Point3d& other) + { + this->p = other.p; + this->i = other.i; + } + + Base::Vector3f p; + unsigned long i; +}; + +typedef KDTree::KDTree<3, Point3d> MyKDTree; + +class MeshKDTree::Private +{ +public: + MyKDTree kd_tree; +}; + +MeshKDTree::MeshKDTree(const std::vector& points) : d(new Private) +{ + unsigned long index=0; + for (std::vector::const_iterator it = points.begin(); it != points.end(); ++it) { + d->kd_tree.insert(Point3d(*it, index++)); + } +} + +MeshKDTree::MeshKDTree(const MeshPointArray& points) : d(new Private) +{ + unsigned long index=0; + for (MeshPointArray::_TConstIterator it = points.begin(); it != points.end(); ++it) { + d->kd_tree.insert(Point3d(*it, index++)); + } +} + +MeshKDTree::~MeshKDTree() +{ + delete d; +} + +bool MeshKDTree::IsEmpty() const +{ + return d->kd_tree.empty(); +} + +void MeshKDTree::Clear() +{ + d->kd_tree.clear(); +} + +void MeshKDTree::Optimize() +{ + d->kd_tree.optimize(); +} + +unsigned long MeshKDTree::FindNearest(const Base::Vector3f& p, Base::Vector3f& n, float& dist) const +{ + std::pair it = + d->kd_tree.find_nearest(Point3d(p,0)); + if (it.first == d->kd_tree.end()) + return ULONG_MAX; + unsigned long index = it.first->i; + n = it.first->p; + dist = it.second; + return index; +} + +unsigned long MeshKDTree::FindNearest(const Base::Vector3f& p, float max_dist, + Base::Vector3f& n, float& dist) const +{ + std::pair it = + d->kd_tree.find_nearest(Point3d(p,0), max_dist); + if (it.first == d->kd_tree.end()) + return ULONG_MAX; + unsigned long index = it.first->i; + n = it.first->p; + dist = it.second; + return index; +} + +unsigned long MeshKDTree::FindExact(const Base::Vector3f& p) const +{ + MyKDTree::const_iterator it = + d->kd_tree.find_exact(Point3d(p,0)); + if (it == d->kd_tree.end()) + return ULONG_MAX; + unsigned long index = it->i; + return index; +} + +void MeshKDTree::FindInRange(const Base::Vector3f& p, float range, std::vector& indices) const +{ + std::vector v; + d->kd_tree.find_within_range(Point3d(p,0), range, std::back_inserter(v)); + indices.reserve(v.size()); + for (std::vector::iterator it = v.begin(); it != v.end(); ++it) + indices.push_back(it->i); +} diff --git a/src/Mod/Mesh/App/Core/KDTree.h b/src/Mod/Mesh/App/Core/KDTree.h new file mode 100644 index 0000000000..63649e65bd --- /dev/null +++ b/src/Mod/Mesh/App/Core/KDTree.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (c) 2011 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef MESH_KDTREE_H +#define MESH_KDTREE_H + +#include "Elements.h" + +namespace MeshCore +{ + +class MeshExport MeshKDTree +{ +public: + MeshKDTree(const std::vector& points); + MeshKDTree(const MeshPointArray& points); + ~MeshKDTree(); + + bool IsEmpty() const; + void Clear(); + void Optimize(); + + unsigned long FindNearest(const Base::Vector3f& p, Base::Vector3f& n, float&) const; + unsigned long FindNearest(const Base::Vector3f& p, float max_dist, + Base::Vector3f& n, float&) const; + unsigned long FindExact(const Base::Vector3f& p) const; + void FindInRange(const Base::Vector3f&, float, std::vector&) const; + +private: + class Private; + Private* d; + + MeshKDTree(const MeshKDTree&); + void operator= (const MeshKDTree&); +}; + +} // namespace MeshCore + + +#endif // MESH_KDTREE_H diff --git a/src/Mod/Mesh/App/Core/Simplify.h b/src/Mod/Mesh/App/Core/Simplify.h new file mode 100644 index 0000000000..914a3f5f46 --- /dev/null +++ b/src/Mod/Mesh/App/Core/Simplify.h @@ -0,0 +1,518 @@ +// http://voxels.blogspot.de/2014/05/quadric-mesh-simplification-with-source.html +// https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification +// +// MIT License + +// Changes: +// * Use Base::Vector3f as vec3f class +// * Move global variables to a class to make the algorithm usable for multi-threading +// * Comment out printf statements +// * Fix compiler warnings +// * Remove macros loop,i,j,k + +#include +#include + +#define _USE_MATH_DEFINES + +typedef Base::Vector3f vec3f; + +class SymetricMatrix { + +public: + + // Constructor + + SymetricMatrix(double c=0) { for (std::size_t i=0;i<10;++i ) m[i] = c; } + + SymetricMatrix(double m11, double m12, double m13, double m14, + double m22, double m23, double m24, + double m33, double m34, + double m44) { + m[0] = m11; m[1] = m12; m[2] = m13; m[3] = m14; + m[4] = m22; m[5] = m23; m[6] = m24; + m[7] = m33; m[8] = m34; + m[9] = m44; + } + + // Make plane + + SymetricMatrix(double a,double b,double c,double d) + { + m[0] = a*a; m[1] = a*b; m[2] = a*c; m[3] = a*d; + m[4] = b*b; m[5] = b*c; m[6] = b*d; + m[7 ] =c*c; m[8 ] = c*d; + m[9 ] = d*d; + } + + double operator[](int c) const { return m[c]; } + + // Determinant + + double det(int a11, int a12, int a13, + int a21, int a22, int a23, + int a31, int a32, int a33) + { + double det = m[a11]*m[a22]*m[a33] + m[a13]*m[a21]*m[a32] + m[a12]*m[a23]*m[a31] + - m[a13]*m[a22]*m[a31] - m[a11]*m[a23]*m[a32]- m[a12]*m[a21]*m[a33]; + return det; + } + + const SymetricMatrix operator+(const SymetricMatrix& n) const + { + return SymetricMatrix( m[0]+n[0], m[1]+n[1], m[2]+n[2], m[3]+n[3], + m[4]+n[4], m[5]+n[5], m[6]+n[6], + m[ 7]+n[ 7], m[ 8]+n[8 ], + m[ 9]+n[9 ]); + } + + SymetricMatrix& operator+=(const SymetricMatrix& n) + { + m[0]+=n[0]; m[1]+=n[1]; m[2]+=n[2]; m[3]+=n[3]; + m[4]+=n[4]; m[5]+=n[5]; m[6]+=n[6]; m[7]+=n[7]; + m[8]+=n[8]; m[9]+=n[9]; + return *this; + } + + double m[10]; +}; +/////////////////////////////////////////// + +class Simplify +{ +public: + struct Triangle { int v[3];double err[4];int deleted,dirty;vec3f n; }; + struct Vertex { vec3f p;int tstart,tcount;SymetricMatrix q;int border;}; + struct Ref { int tid,tvertex; }; + std::vector triangles; + std::vector vertices; + std::vector refs; + + void simplify_mesh(int target_count, double tolerance_factor, double agressiveness=7); + +private: + // Helper functions + + double vertex_error(SymetricMatrix q, double x, double y, double z); + double calculate_error(int id_v1, int id_v2, vec3f &p_result); + bool flipped(vec3f p,int i0,int i1,Vertex &v0,Vertex &v1,std::vector &deleted); + void update_triangles(int i0,Vertex &v,std::vector &deleted,int &deleted_triangles); + void update_mesh(int iteration); + void compact_mesh(); +}; + +// +// Main simplification function +// +// target_count : target nr. of triangles +// agressiveness : sharpness to increase the threashold. +// 5..8 are good numbers +// more iterations yield higher quality +// +void Simplify::simplify_mesh(int target_count, double tolerance_factor, double agressiveness) +{ + (void)tolerance_factor; + // init + //printf("%s - start\n",__FUNCTION__); + //int timeStart=timeGetTime(); + + for (std::size_t i=0;i deleted0,deleted1; + int triangle_count=triangles.size(); + + for (int iteration=0;iteration<100;++iteration) + { + // target number of triangles reached ? Then break + //printf("iteration %d - triangles %d\n",iteration,triangle_count-deleted_triangles); + if (triangle_count-deleted_triangles<=target_count) + break; + + // update mesh once in a while + if (iteration%5==0) + { + update_mesh(iteration); + } + + // clear dirty flag + for (std::size_t i=0;ithreshold) + continue; + if (t.deleted) + continue; + if (t.dirty) + continue; + + for (std::size_t j=0;j<3;++j) + { + if (t.err[j] &deleted) +{ + (void)i0; (void)v1; + int bordercount=0; + for (int k=0;k0.999) + return true; + vec3f n; + n = d1.Cross(d2); + n.Normalize(); + deleted[k]=0; + if (n.Dot(t.n)<0.2) + return true; + } + return false; +} + +// Update triangle connections and edge error after a edge is collapsed + +void Simplify::update_triangles(int i0,Vertex &v,std::vector &deleted,int &deleted_triangles) +{ + vec3f p; + for (int k=0;k0) // compact triangles + { + int dst=0; + for (std::size_t i=0;i vcount,vids; + + for (std::size_t i=0;i try to find best result + vec3f p1=vertices[id_v1].p; + vec3f p2=vertices[id_v2].p; + vec3f p3=(p1+p2)/2; + double error1 = vertex_error(q, p1.x,p1.y,p1.z); + double error2 = vertex_error(q, p2.x,p2.y,p2.z); + double error3 = vertex_error(q, p3.x,p3.y,p3.z); + error = std::min(error1, std::min(error2, error3)); + if (error1 == error) + p_result=p1; + if (error2 == error) + p_result=p2; + if (error3 == error) + p_result=p3; + } + return error; +} + +/////////////////////////////////////////// diff --git a/src/Mod/Mesh/App/Mesh.cpp b/src/Mod/Mesh/App/Mesh.cpp index 101ede6ba6..f04f0ac44f 100644 --- a/src/Mod/Mesh/App/Mesh.cpp +++ b/src/Mod/Mesh/App/Mesh.cpp @@ -51,6 +51,7 @@ #include "Core/Triangulation.h" #include "Core/Trim.h" #include "Core/Visitor.h" +#include "Core/Decimation.h" #include "Mesh.h" #include "MeshPy.h" @@ -940,6 +941,12 @@ void MeshObject::smooth(int iterations, float d_max) _kernel.Smooth(iterations, d_max); } +void MeshObject::decimate(float fTolerance, float fReduction) +{ + MeshCore::MeshSimplify dm(this->_kernel); + dm.simplify(fTolerance, fReduction); +} + Base::Vector3d MeshObject::getPointNormal(unsigned long index) const { std::vector temp = _kernel.CalcVertexNormals(); diff --git a/src/Mod/Mesh/App/Mesh.h b/src/Mod/Mesh/App/Mesh.h index 1cfcb8549f..229af20efb 100644 --- a/src/Mod/Mesh/App/Mesh.h +++ b/src/Mod/Mesh/App/Mesh.h @@ -216,6 +216,7 @@ public: void movePoint(unsigned long, const Base::Vector3d& v); void setPoint(unsigned long, const Base::Vector3d& v); void smooth(int iterations, float d_max); + void decimate(float fTolerance, float fReduction); Base::Vector3d getPointNormal(unsigned long) const; std::vector getPointNormals() const; void crossSections(const std::vector&, std::vector §ions, diff --git a/src/Mod/Mesh/App/MeshPy.xml b/src/Mod/Mesh/App/MeshPy.xml index e0be9f6dfd..c71944df60 100644 --- a/src/Mod/Mesh/App/MeshPy.xml +++ b/src/Mod/Mesh/App/MeshPy.xml @@ -409,6 +409,19 @@ The argument int is the mode: 0=inner, 1=outer smooth([iteration=1,maxError=FLT_MAX]) + + + + Decimate the mesh + decimate(tolerance(Float), reduction(Float)) + tolerance: maximum error + reduction: reduction factor must be in the range [0.0,1.0] + Example: + mesh.decimate(0.5, 0.1) # reduction by up to 10 percent + mesh.decimate(0.5, 0.9) # reduction by up to 90 percent + + + Optimize the edges to get nicer facets diff --git a/src/Mod/Mesh/App/MeshPyImp.cpp b/src/Mod/Mesh/App/MeshPyImp.cpp index 769bbcd21b..ac5d6b9910 100644 --- a/src/Mod/Mesh/App/MeshPyImp.cpp +++ b/src/Mod/Mesh/App/MeshPyImp.cpp @@ -353,7 +353,7 @@ PyObject* MeshPy::offset(PyObject *args) getMeshObjectPtr()->offsetSpecial2(Float); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::offsetSpecial(PyObject *args) @@ -366,7 +366,7 @@ PyObject* MeshPy::offsetSpecial(PyObject *args) getMeshObjectPtr()->offsetSpecial(Float,zmax,zmin); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::crossSections(PyObject *args) @@ -1074,7 +1074,7 @@ PyObject* MeshPy::flipNormals(PyObject *args) getMeshObjectPtr()->flipNormals(); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::hasNonUniformOrientedFacets(PyObject *args) @@ -1119,7 +1119,7 @@ PyObject* MeshPy::harmonizeNormals(PyObject *args) getMeshObjectPtr()->harmonizeNormals(); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::countComponents(PyObject *args) @@ -1143,7 +1143,7 @@ PyObject* MeshPy::removeComponents(PyObject *args) } } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::fillupHoles(PyObject *args) @@ -1184,7 +1184,7 @@ PyObject* MeshPy::fixIndices(PyObject *args) getMeshObjectPtr()->validateIndices(); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::fixDeformations(PyObject *args) @@ -1198,7 +1198,7 @@ PyObject* MeshPy::fixDeformations(PyObject *args) getMeshObjectPtr()->validateDeformations(fMaxAngle, fEpsilon); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::fixDegenerations(PyObject *args) @@ -1211,7 +1211,7 @@ PyObject* MeshPy::fixDegenerations(PyObject *args) getMeshObjectPtr()->validateDegenerations(fEpsilon); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::removeDuplicatedPoints(PyObject *args) @@ -1223,7 +1223,7 @@ PyObject* MeshPy::removeDuplicatedPoints(PyObject *args) getMeshObjectPtr()->removeDuplicatedPoints(); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::removeDuplicatedFacets(PyObject *args) @@ -1235,7 +1235,7 @@ PyObject* MeshPy::removeDuplicatedFacets(PyObject *args) getMeshObjectPtr()->removeDuplicatedFacets(); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::refine(PyObject *args) @@ -1247,7 +1247,7 @@ PyObject* MeshPy::refine(PyObject *args) getMeshObjectPtr()->refine(); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::optimizeTopology(PyObject *args) @@ -1261,7 +1261,7 @@ PyObject* MeshPy::optimizeTopology(PyObject *args) getMeshObjectPtr()->optimizeTopology(fMaxAngle); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::optimizeEdges(PyObject *args) @@ -1274,7 +1274,7 @@ PyObject* MeshPy::optimizeEdges(PyObject *args) getMeshObjectPtr()->optimizeEdges(); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::splitEdges(PyObject *args) @@ -1286,7 +1286,7 @@ PyObject* MeshPy::splitEdges(PyObject *args) getMeshObjectPtr()->splitEdges(); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::splitEdge(PyObject *args) @@ -1321,7 +1321,7 @@ PyObject* MeshPy::splitEdge(PyObject *args) getMeshObjectPtr()->splitEdge(facet, neighbour, v); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::splitFacet(PyObject *args) @@ -1351,7 +1351,7 @@ PyObject* MeshPy::splitFacet(PyObject *args) getMeshObjectPtr()->splitFacet(facet, v1, v2); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::swapEdge(PyObject *args) @@ -1381,7 +1381,7 @@ PyObject* MeshPy::swapEdge(PyObject *args) getMeshObjectPtr()->swapEdge(facet, neighbour); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::collapseEdge(PyObject *args) @@ -1411,7 +1411,7 @@ PyObject* MeshPy::collapseEdge(PyObject *args) getMeshObjectPtr()->collapseEdge(facet, neighbour); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::collapseFacet(PyObject *args) @@ -1429,7 +1429,7 @@ PyObject* MeshPy::collapseFacet(PyObject *args) getMeshObjectPtr()->collapseFacet(facet); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::insertVertex(PyObject *args) @@ -1452,7 +1452,7 @@ PyObject* MeshPy::insertVertex(PyObject *args) getMeshObjectPtr()->insertVertex(facet, v); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::snapVertex(PyObject *args) @@ -1475,7 +1475,7 @@ PyObject* MeshPy::snapVertex(PyObject *args) getMeshObjectPtr()->snapVertex(facet, v); } PY_CATCH; - Py_Return; + Py_Return; } PyObject* MeshPy::printInfo(PyObject *args) @@ -1513,7 +1513,7 @@ PyObject* MeshPy::collapseFacets(PyObject *args) return 0; } - Py_Return; + Py_Return; } PyObject* MeshPy::foraminate(PyObject *args) @@ -1590,7 +1590,7 @@ PyObject* MeshPy::cut(PyObject *args) polygon2d.Add(Base::Vector2d(it->x, it->y)); getMeshObjectPtr()->cut(polygon2d, proj, MeshObject::CutType(mode)); - Py_Return; + Py_Return; } PyObject* MeshPy::trim(PyObject *args) @@ -1624,7 +1624,7 @@ PyObject* MeshPy::trim(PyObject *args) polygon2d.Add(Base::Vector2d(it->x, it->y)); getMeshObjectPtr()->trim(polygon2d, proj, MeshObject::CutType(mode)); - Py_Return; + Py_Return; } PyObject* MeshPy::smooth(PyObject *args) @@ -1639,7 +1639,20 @@ PyObject* MeshPy::smooth(PyObject *args) getMeshObjectPtr()->smooth(iter, d_max); } PY_CATCH; - Py_Return; + Py_Return; +} + +PyObject* MeshPy::decimate(PyObject *args) +{ + float fTol, fRed; + if (!PyArg_ParseTuple(args, "ff", &fTol,&fRed)) + return NULL; + + PY_TRY { + getMeshObjectPtr()->decimate(fTol, fRed); + } PY_CATCH; + + Py_Return; } PyObject* MeshPy::nearestFacetOnRay(PyObject *args) diff --git a/src/Mod/Mesh/Gui/MeshSelection.cpp b/src/Mod/Mesh/Gui/MeshSelection.cpp index 0d85c0ee81..60f1ef0a9d 100644 --- a/src/Mod/Mesh/Gui/MeshSelection.cpp +++ b/src/Mod/Mesh/Gui/MeshSelection.cpp @@ -200,7 +200,7 @@ void MeshSelection::prepareFreehandSelection(bool add,SoEventCallbackCB *cb) freehand->setColor(1.0f, 0.0f, 0.0f); freehand->setLineWidth(3.0f); viewer->navigationStyle()->startSelection(freehand); - + QBitmap cursor = QBitmap::fromData(QSize(CROSS_WIDTH, CROSS_HEIGHT), cross_bitmap); QBitmap mask = QBitmap::fromData(QSize(CROSS_WIDTH, CROSS_HEIGHT), cross_mask_bitmap); QCursor custom(cursor, mask, CROSS_HOT_X, CROSS_HOT_Y); diff --git a/src/Mod/Test/Gui/CMakeLists.txt b/src/Mod/Test/Gui/CMakeLists.txt index a5b4d64570..db1b9a9d91 100644 --- a/src/Mod/Test/Gui/CMakeLists.txt +++ b/src/Mod/Test/Gui/CMakeLists.txt @@ -1,6 +1,7 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src + ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS} ${XercesC_INCLUDE_DIRS} ) @@ -40,6 +41,7 @@ if(BUILD_QT5) else() qt4_add_resources(Resource_SRCS Resources/Test.qrc) endif() + SET(Resource_SRCS ${Resource_SRCS} Resources/Test.qrc