add kd tree class

add mesh decimation algorithm
This commit is contained in:
wmayer
2017-11-14 11:37:30 +01:00
parent 0b33f977f7
commit 04ea295280
35 changed files with 5546 additions and 27 deletions

3
src/3rdParty/libkdtree/AUTHORS vendored Normal file
View File

@@ -0,0 +1,3 @@
Martin F. Krafft <libkdtree@pobox.madduck.net>
Paul Harris <paulharris@computer.org>
Sylvain Bougerel <sylvain.bougerel.devel@gmail.com>

37
src/3rdParty/libkdtree/CMakeLists.txt vendored Normal file
View File

@@ -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)

176
src/3rdParty/libkdtree/COPYING vendored Normal file
View File

@@ -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.

236
src/3rdParty/libkdtree/ChangeLog vendored Normal file
View File

@@ -0,0 +1,236 @@
libkdtree++ ChangeLog
=====================
2008-11-17 Sylvain Bougerel <sylvain.bougerel@asia.thalesgroup.com>
- Added #include<cstdio> 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 <paulharris@computer.org>
- Bug fix: was incorrectly casting a pointer when the search key type
was different to the stored type.
2008-12-30 Paul Harris <paulharris@computer.org>
- 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 <sylvain.bougerel@asia.thalesgroup.com>
- 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 <sylvain.bougerel@asia.thalesgroup.com>
- 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 <sylvain.bougerel@asia.thalesgroup.com>
- 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 <Visitor> 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 <libkdtree@pobox.madduck.net> and
Sylvain Bougerel <sylvain.bougerel.devel@gmail.com> 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.

234
src/3rdParty/libkdtree/INSTALL vendored Normal file
View File

@@ -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.

121
src/3rdParty/libkdtree/README vendored Normal file
View File

@@ -0,0 +1,121 @@
libkdtree++ README
==================
libkdtree++ is (c) 2004-2007 Martin F. Krafft <libkdtree@pobox.madduck.net>
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 <libkdtree-devel@lists.alioth.debian.org>.
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 <kdtree++/kdtree.hpp>
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.

12
src/3rdParty/libkdtree/README.Python vendored Normal file
View File

@@ -0,0 +1,12 @@
Stand-alone Python bindings, contributed by Willi Richert <w.richert@gmx.net>
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

View File

@@ -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)

View File

@@ -0,0 +1,108 @@
// Thanks to James Remillard
//
#include <cstdio>
#include <kdtree++/kdtree.hpp>
#include <vector>
#include <map>
#include <set>
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<kdtreeNode> 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<size_t> 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<kdtreeNode> howClose;
tree.find_within_range(refNode,limit,back_insert_iterator<vector<kdtreeNode> >(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<size_t>::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");
}
}
}

View File

@@ -0,0 +1,116 @@
#define KDTREE_SIZE_T unsigned int
#include <kdtree++/kdtree.hpp>
#include <vector>
#include <limits>
#include <iostream>
#include <functional>
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,int,double> > 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<duplet> 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<double>::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;
}
}
}
}

View File

@@ -0,0 +1,423 @@
#define KDTREE_DEFINE_OSTREAM_OPERATORS
// Make SURE all our asserts() are checked
#undef NDEBUG
#include <kdtree++/kdtree.hpp>
#include <deque>
#include <iostream>
#include <vector>
#include <limits>
#include <functional>
#include <set>
// used to ensure all triplets that are accessed via the operator<< are initialised.
std::set<const void*> 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<triplet,size_t,double> > 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<tree_type::const_iterator,double> 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<alt_tree::const_iterator,double> 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<tree_type::const_iterator,double> 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<tree_type::const_iterator,double> 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<triplet> 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<triplet> 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<triplet>::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<tree_type::const_iterator,double> 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<tree_type::const_iterator,double> 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<tree_type::const_iterator,double> nif = t.find_nearest_if(s,std::numeric_limits<double>::max(),Predicate());
std::cout << "Test find_nearest_if(), nearest to " << s << " @ " << nif.second << ": " << *nif.first << std::endl;
std::pair<tree_type::const_iterator,double> cantfind = t.find_nearest_if(s,std::numeric_limits<double>::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<tree_type::const_iterator,double> found = t.find_nearest(s,std::numeric_limits<double>::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<double>::epsilon() );
}
{
triplet s2(10, 10, 2);
std::pair<tree_type::const_iterator,double> found = t.find_nearest(s2,std::numeric_limits<double>::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<double>::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<triplet> 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<triplet> 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<triplet>::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 <libkdtree@pobox.madduck.net>
* and Sylvain Bougerel <sylvain.bougerel.devel@gmail.com> 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.
*/

View File

@@ -0,0 +1,96 @@
/** \file
* Defines the allocator interface as used by the KDTree class.
*
* \author Martin F. Krafft <libkdtree@pobox.madduck.net>
*/
#ifndef INCLUDE_KDTREE_ALLOCATOR_HPP
#define INCLUDE_KDTREE_ALLOCATOR_HPP
#include <cstddef>
#include "node.hpp"
namespace KDTree
{
template <typename _Tp, typename _Alloc>
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 <libkdtree@pobox.madduck.net>
* and Sylvain Bougerel <sylvain.bougerel.devel@gmail.com> 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.
*/

View File

@@ -0,0 +1,89 @@
/** \file
* Defines the various functors and interfaces used for KDTree.
*
* \author Martin F. Krafft <libkdtree@pobox.madduck.net>
* \author Sylvain Bougerel <sylvain.bougerel.devel@gmail.com>
*/
#ifndef INCLUDE_KDTREE_ACCESSOR_HPP
#define INCLUDE_KDTREE_ACCESSOR_HPP
#include <cstddef>
namespace KDTree
{
template <typename _Val>
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 <typename _Tp>
struct always_true
{
bool operator() (const _Tp& ) const { return true; }
};
template <typename _Tp, typename _Dist>
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 <typename _Tp, typename _Dist>
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 <libkdtree@pobox.madduck.net>
* and Sylvain Bougerel <sylvain.bougerel.devel@gmail.com> 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.
*/

View File

@@ -0,0 +1,258 @@
/** \file
* Defines interfaces for iterators as used by the KDTree class.
*
* \author Martin F. Krafft <libkdtree@pobox.madduck.net>
*/
#ifndef INCLUDE_KDTREE_ITERATOR_HPP
#define INCLUDE_KDTREE_ITERATOR_HPP
#include <iterator>
#include <kdtree++/node.hpp>
namespace KDTree
{
template <typename _Val, typename _Ref, typename _Ptr>
class _Iterator;
template<typename _Val, typename _Ref, typename _Ptr>
inline bool
operator==(_Iterator<_Val, _Ref, _Ptr> const&,
_Iterator<_Val, _Ref, _Ptr> const&);
template<typename _Val>
inline bool
operator==(_Iterator<_Val, const _Val&, const _Val*> const&,
_Iterator<_Val, _Val&, _Val*> const&);
template<typename _Val>
inline bool
operator==(_Iterator<_Val, _Val&, _Val*> const&,
_Iterator<_Val, const _Val&, const _Val*> const&);
template<typename _Val, typename _Ref, typename _Ptr>
inline bool
operator!=(_Iterator<_Val, _Ref, _Ptr> const&,
_Iterator<_Val, _Ref, _Ptr> const&);
template<typename _Val>
inline bool
operator!=(_Iterator<_Val, const _Val&, const _Val*> const&,
_Iterator<_Val, _Val&, _Val*> const&);
template<typename _Val>
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 <size_t const __K, typename _Val, typename _Acc,
typename _Dist, typename _Cmp, typename _Alloc>
friend class KDTree;
};
template <typename _Val, typename _Ref, typename _Ptr>
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<typename _Val, typename _Ref, typename _Ptr>
inline bool
operator==(_Iterator<_Val, _Ref, _Ptr> const& __X,
_Iterator<_Val, _Ref, _Ptr> const& __Y)
{ return __X._M_node == __Y._M_node; }
template<typename _Val>
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<typename _Val>
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<typename _Val, typename _Ref, typename _Ptr>
inline bool
operator!=(_Iterator<_Val, _Ref, _Ptr> const& __X,
_Iterator<_Val, _Ref, _Ptr> const& __Y)
{ return __X._M_node != __Y._M_node; }
template<typename _Val>
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<typename _Val>
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 <libkdtree@pobox.madduck.net>
* and Sylvain Bougerel <sylvain.bougerel.devel@gmail.com> 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.
*/

1247
src/3rdParty/libkdtree/kdtree++/kdtree.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

362
src/3rdParty/libkdtree/kdtree++/node.hpp vendored Normal file
View File

@@ -0,0 +1,362 @@
/** \file
* Defines interfaces for nodes as used by the KDTree class.
*
* \author Martin F. Krafft <libkdtree@pobox.madduck.net>
*/
#ifndef INCLUDE_KDTREE_NODE_HPP
#define INCLUDE_KDTREE_NODE_HPP
#ifdef KDTREE_DEFINE_OSTREAM_OPERATORS
# include <ostream>
#endif
#include <cstddef>
#include <cmath>
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 <typename _Val>
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 <typename Char, typename Traits>
friend
std::basic_ostream<Char, Traits>&
operator<<(typename std::basic_ostream<Char, Traits>& 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 <typename Char, typename Traits>
friend
std::basic_ostream<Char, Traits>&
operator<<(typename std::basic_ostream<Char, Traits>& 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 <typename _Val, typename _Acc, typename _Cmp>
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 <typename _ValA, typename _ValB, typename _Cmp,
typename _Acc>
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 <typename _ValA, typename _ValB, typename _Dist,
typename _Acc>
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 <typename _ValA, typename _ValB, typename _Dist,
typename _Acc>
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 <typename _Val, typename _Cmp, typename _Acc>
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<const _Node<_Val>* >(__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 <class SearchVal,
typename _Val, typename _Cmp,
typename _Acc, typename _Dist,
typename _Predicate>
inline
std::pair<const _Node<_Val>*,
std::pair<size_t, typename _Dist::distance_type> >
_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<const _Node<_Val>* >(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<const _Node<_Val>* >(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<const _Node<_Val>* >(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<const _Node<_Val>* >(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<const _Node<_Val>* >(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<const _Node<_Val>* >(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<const _Node<_Val>* >(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<const _Node<_Val>* >(probe)->_M_value);
d = sqrt(d);
if (d <= __max) // CHANGED, see the above notes ("bad candidate notes")
{
__best = static_cast<const _Node<_Val>* >(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<const _Node<_Val>* >(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<const _Node<_Val>* >(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<const _Node<_Val>* >(cur)->_M_value)) <= __max))
{
probe = near_node;
++probe_dim;
}
}
}
return std::pair<const _Node<_Val>*,
std::pair<size_t, typename _Dist::distance_type> >
(__best, std::pair<size_t, typename _Dist::distance_type>
(__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 <libkdtree@pobox.madduck.net>
* and Sylvain Bougerel <sylvain.bougerel.devel@gmail.com> 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.
*/

View File

@@ -0,0 +1,131 @@
/** \file
* Defines the interface of the _Region class.
*
* \author Martin F. Krafft <libkdtree@pobox.madduck.net>
*/
#ifndef INCLUDE_KDTREE_REGION_HPP
#define INCLUDE_KDTREE_REGION_HPP
#include <cstddef>
#include <kdtree++/node.hpp>
namespace KDTree
{
template <size_t const __K, typename _Val, typename _SubVal,
typename _Acc, typename _Cmp>
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 <typename Val>
_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 <typename Val>
_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<low-tol or high+tol<x
if (_M_cmp(__THAT.first._M_low_bounds[__i], _M_low_bounds[__i] - __THAT.second)
|| _M_cmp(_M_high_bounds[__i] + __THAT.second, __THAT.first._M_low_bounds[__i]))
return false;
}
return true;
}
bool
intersects_with(_Region const& __THAT) const
{
for (size_t __i = 0; __i != __K; ++__i)
{
if (_M_cmp(__THAT._M_high_bounds[__i], _M_low_bounds[__i])
|| _M_cmp(_M_high_bounds[__i], __THAT._M_low_bounds[__i]))
return false;
}
return true;
}
bool
encloses(value_type const& __V) const
{
for (size_t __i = 0; __i != __K; ++__i)
{
if (_M_cmp(_M_acc(__V, __i), _M_low_bounds[__i])
|| _M_cmp(_M_high_bounds[__i], _M_acc(__V, __i)))
return false;
}
return true;
}
_Region&
set_high_bound(value_type const& __V, size_t const __L)
{
_M_high_bounds[__L % __K] = _M_acc(__V, __L % __K);
return *this;
}
_Region&
set_low_bound(value_type const& __V, size_t const __L)
{
_M_low_bounds[__L % __K] = _M_acc(__V, __L % __K);
return *this;
}
subvalue_type _M_low_bounds[__K], _M_high_bounds[__K];
_Acc _M_acc;
_Cmp _M_cmp;
};
} // 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 <libkdtree@pobox.madduck.net>
* and Sylvain Bougerel <sylvain.bougerel.devel@gmail.com> 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.
*/

View File

@@ -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)

View File

@@ -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<RECORD_%i%s%s >* {
std::vector<RECORD_%i%s%s >* 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<RECORD_%i%s%s >::const_iterator iter = v->begin();
for (size_t i=0; i<v->size(); 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<RECORD_%i%s%s,int,double> >
"""
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")

View File

@@ -0,0 +1,145 @@
/** \file
* Provides a Python interface for the libkdtree++.
*
* \author Willi Richert <w.richert@gmx.net>
*
*
* 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 <kdtree++/kdtree.hpp>
#include <iostream>
#include <vector>
#include <limits>
template <size_t DIM, typename COORD_T, typename DATA_T >
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 <class RECORD_T>
inline double tac(RECORD_T r, int k) { return r[k]; }
template <size_t DIM, typename COORD_T, typename DATA_T >
class PyKDTree {
public:
typedef record_t<DIM, COORD_T, DATA_T> RECORD_T;
typedef KDTree::KDTree<DIM, RECORD_T, std::pointer_to_binary_function<RECORD_T,int,double> > TREE_T;
TREE_T tree;
PyKDTree() : tree(std::ptr_fun(tac<RECORD_T>)) { };
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<RECORD_T >* 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<RECORD_T> *v = new std::vector<RECORD_T>;
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<typename TREE_T::const_iterator, typename TREE_T::distance_type> best =
tree.find_nearest(query_record, std::numeric_limits<typename TREE_T::distance_type>::max());
if (best.first!=tree.end()) {
found = new RECORD_T(*best.first);
}
return found;
}
std::vector<RECORD_T >* get_all() {
std::vector<RECORD_T>* v = new std::vector<RECORD_T>;
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_

View File

@@ -0,0 +1,27 @@
/** \file
*
* Provides a Python interface for the libkdtree++.
*
* \author Willi Richert <w.richert@gmx.net>
*
*/
%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%%

View File

@@ -0,0 +1,96 @@
#define KDTREE_DEFINE_OSTREAM_OPERATORS
#include <kdtree++/kdtree.hpp>
#include <iostream>
#include <vector>
#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<RECORD_2il> 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<RECORD_2il>::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 <libkdtree@pobox.madduck.net>
* and Sylvain Bougerel <sylvain.bougerel.devel@gmail.com> 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.
*/

View File

@@ -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()

View File

@@ -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

View File

@@ -0,0 +1,97 @@
/***************************************************************************
* Copyright (c) 2013 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 <Base/Tools.h>
#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<int>(static_cast<float>(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);
}

View File

@@ -0,0 +1,46 @@
/***************************************************************************
* Copyright (c) 2013 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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

View File

@@ -0,0 +1,155 @@
/***************************************************************************
* Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 <kdtree++/kdtree.hpp>
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<Base::Vector3f>& points) : d(new Private)
{
unsigned long index=0;
for (std::vector<Base::Vector3f>::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<MyKDTree::const_iterator, MyKDTree::distance_type> 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<MyKDTree::const_iterator, MyKDTree::distance_type> 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<unsigned long>& indices) const
{
std::vector<Point3d> v;
d->kd_tree.find_within_range(Point3d(p,0), range, std::back_inserter(v));
indices.reserve(v.size());
for (std::vector<Point3d>::iterator it = v.begin(); it != v.end(); ++it)
indices.push_back(it->i);
}

View File

@@ -0,0 +1,60 @@
/***************************************************************************
* Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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<Base::Vector3f>& 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<unsigned long>&) const;
private:
class Private;
Private* d;
MeshKDTree(const MeshKDTree&);
void operator= (const MeshKDTree&);
};
} // namespace MeshCore
#endif // MESH_KDTREE_H

View File

@@ -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 <vector>
#include <Base/Vector3D.h>
#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<Triangle> triangles;
std::vector<Vertex> vertices;
std::vector<Ref> 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<int> &deleted);
void update_triangles(int i0,Vertex &v,std::vector<int> &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<triangles.size();++i)
triangles[i].deleted=0;
// main iteration loop
int deleted_triangles=0;
std::vector<int> 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;i<triangles.size();++i)
triangles[i].dirty=0;
//
// All triangles with edges below the threshold will be removed
//
// The following numbers works well for most models.
// If it does not, try to adjust the 3 parameters
//
double threshold = 0.000000001*pow(double(iteration+3),agressiveness);
//if (tolerance_factor < 1.0)
// threshold *= tolerance_factor;
// remove vertices & mark deleted triangles
for (std::size_t i=0;i<triangles.size();++i)
{
Triangle &t=triangles[i];
if (t.err[3]>threshold)
continue;
if (t.deleted)
continue;
if (t.dirty)
continue;
for (std::size_t j=0;j<3;++j)
{
if (t.err[j]<threshold)
{
int i0=t.v[ j ]; Vertex &v0 = vertices[i0];
int i1=t.v[(j+1)%3]; Vertex &v1 = vertices[i1];
// Border check
if (v0.border != v1.border)
continue;
// Compute vertex to collapse to
vec3f p;
calculate_error(i0,i1,p);
deleted0.resize(v0.tcount); // normals temporarily
deleted1.resize(v1.tcount); // normals temporarily
// dont remove if flipped
if (flipped(p,i0,i1,v0,v1,deleted0))
continue;
if (flipped(p,i1,i0,v1,v0,deleted1))
continue;
// not flipped, so remove edge
v0.p=p;
v0.q=v1.q+v0.q;
int tstart=refs.size();
update_triangles(i0,v0,deleted0,deleted_triangles);
update_triangles(i0,v1,deleted1,deleted_triangles);
int tcount=refs.size()-tstart;
if (tcount<=v0.tcount)
{
// save ram
if (tcount)
memcpy(&refs[v0.tstart],&refs[tstart],tcount*sizeof(Ref));
}
else
{
// append
v0.tstart=tstart;
}
v0.tcount=tcount;
break;
}
}
// done?
if (triangle_count-deleted_triangles<=target_count)
break;
}
}
// clean up mesh
compact_mesh();
// ready
//int timeEnd=timeGetTime();
//printf("%s - %d/%d %d%% removed in %d ms\n",__FUNCTION__,
// triangle_count-deleted_triangles,
// triangle_count,deleted_triangles*100/triangle_count,
// timeEnd-timeStart);
}
// Check if a triangle flips when this edge is removed
bool Simplify::flipped(vec3f p, int i0, int i1,
Vertex &v0,
Vertex &v1,
std::vector<int> &deleted)
{
(void)i0; (void)v1;
int bordercount=0;
for (int k=0;k<v0.tcount;++k)
{
Triangle &t=triangles[refs[v0.tstart+k].tid];
if (t.deleted)
continue;
int s=refs[v0.tstart+k].tvertex;
int id1=t.v[(s+1)%3];
int id2=t.v[(s+2)%3];
if (id1==i1 || id2==i1) // delete ?
{
bordercount++;
deleted[k]=1;
continue;
}
vec3f d1 = vertices[id1].p-p; d1.Normalize();
vec3f d2 = vertices[id2].p-p; d2.Normalize();
if (fabs(d1.Dot(d2))>0.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<int> &deleted,int &deleted_triangles)
{
vec3f p;
for (int k=0;k<v.tcount;++k)
{
Ref &r=refs[v.tstart+k];
Triangle &t=triangles[r.tid];
if (t.deleted)
continue;
if (deleted[k])
{
t.deleted=1;
deleted_triangles++;
continue;
}
t.v[r.tvertex]=i0;
t.dirty=1;
t.err[0]=calculate_error(t.v[0],t.v[1],p);
t.err[1]=calculate_error(t.v[1],t.v[2],p);
t.err[2]=calculate_error(t.v[2],t.v[0],p);
t.err[3]=std::min(t.err[0],std::min(t.err[1],t.err[2]));
refs.push_back(r);
}
}
// compact triangles, compute edge error and build reference list
void Simplify::update_mesh(int iteration)
{
if(iteration>0) // compact triangles
{
int dst=0;
for (std::size_t i=0;i<triangles.size();++i)
{
if (!triangles[i].deleted)
{
triangles[dst++]=triangles[i];
}
}
triangles.resize(dst);
}
//
// Init Quadrics by Plane & Edge Errors
//
// required at the beginning ( iteration == 0 )
// recomputing during the simplification is not required,
// but mostly improves the result for closed meshes
//
if (iteration == 0)
{
for (std::size_t i=0;i<vertices.size();++i)
vertices[i].q=SymetricMatrix(0.0);
for (std::size_t i=0;i<triangles.size();++i)
{
Triangle &t=triangles[i];
vec3f n,p[3];
for (std::size_t j=0;j<3;++j)
p[j]=vertices[t.v[j]].p;
n = (p[1]-p[0]).Cross(p[2]-p[0]);
n.Normalize();
t.n=n;
for (std::size_t j=0;j<3;++j)
vertices[t.v[j]].q = vertices[t.v[j]].q+SymetricMatrix(n.x,n.y,n.z,-n.Dot(p[0]));
}
for (std::size_t i=0;i<triangles.size();++i)
{
// Calc Edge Error
Triangle &t=triangles[i];vec3f p;
for (std::size_t j=0;j<3;++j)
t.err[j] = calculate_error(t.v[j],t.v[(j+1)%3],p);
t.err[3]=std::min(t.err[0],std::min(t.err[1],t.err[2]));
}
}
// Init Reference ID list
for (std::size_t i=0;i<vertices.size();++i)
{
vertices[i].tstart=0;
vertices[i].tcount=0;
}
for (std::size_t i=0;i<triangles.size();++i)
{
Triangle &t=triangles[i];
for (std::size_t j=0;j<3;++j)
vertices[t.v[j]].tcount++;
}
int tstart=0;
for (std::size_t i=0;i<vertices.size();++i)
{
Vertex &v=vertices[i];
v.tstart=tstart;
tstart+=v.tcount;
v.tcount=0;
}
// Write References
refs.resize(triangles.size()*3);
for (std::size_t i=0;i<triangles.size();++i)
{
Triangle &t=triangles[i];
for (std::size_t j=0;j<3;++j)
{
Vertex &v=vertices[t.v[j]];
refs[v.tstart+v.tcount].tid=i;
refs[v.tstart+v.tcount].tvertex=j;
v.tcount++;
}
}
// Identify boundary : vertices[].border=0,1
if (iteration == 0)
{
std::vector<int> vcount,vids;
for (std::size_t i=0;i<vertices.size();++i)
vertices[i].border=0;
for (std::size_t i=0;i<vertices.size();++i)
{
Vertex &v=vertices[i];
vcount.clear();
vids.clear();
for (int j=0; j<v.tcount; ++j)
{
int k=refs[v.tstart+j].tid;
Triangle &t=triangles[k];
for (int k=0;k<3;++k)
{
std::size_t ofs=0; int id=t.v[k];
while(ofs<vcount.size())
{
if (vids[ofs]==id)
break;
ofs++;
}
if(ofs==vcount.size())
{
vcount.push_back(1);
vids.push_back(id);
}
else
{
vcount[ofs]++;
}
}
}
for (std::size_t j=0;j<vcount.size();++j) {
if (vcount[j]==1)
vertices[vids[j]].border=1;
}
}
}
}
// Finally compact mesh before exiting
void Simplify::compact_mesh()
{
int dst=0;
for (std::size_t i=0;i<vertices.size();++i)
{
vertices[i].tcount=0;
}
for (std::size_t i=0;i<triangles.size();++i)
{
if (!triangles[i].deleted)
{
Triangle &t=triangles[i];
triangles[dst++]=t;
for (std::size_t j=0;j<3;++j)
vertices[t.v[j]].tcount=1;
}
}
triangles.resize(dst);
dst=0;
for (std::size_t i=0;i<vertices.size();++i)
{
if (vertices[i].tcount)
{
vertices[i].tstart=dst;
vertices[dst].p=vertices[i].p;
dst++;
}
}
for (std::size_t i=0;i<triangles.size();++i)
{
Triangle &t=triangles[i];
for (std::size_t j=0;j<3;++j)
t.v[j]=vertices[t.v[j]].tstart;
}
vertices.resize(dst);
}
// Error between vertex and Quadric
double Simplify::vertex_error(SymetricMatrix q, double x, double y, double z)
{
return q[0]*x*x + 2*q[1]*x*y + 2*q[2]*x*z + 2*q[3]*x + q[4]*y*y
+ 2*q[5]*y*z + 2*q[6]*y + q[7]*z*z + 2*q[8]*z + q[9];
}
// Error for one edge
double Simplify::calculate_error(int id_v1, int id_v2, vec3f &p_result)
{
// compute interpolated vertex
SymetricMatrix q = vertices[id_v1].q + vertices[id_v2].q;
bool border = vertices[id_v1].border & vertices[id_v2].border;
double error=0;
double det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7);
if (det != 0 && !border)
{
// q_delta is invertible
p_result.x = -1/det*(q.det(1, 2, 3, 4, 5, 6, 5, 7 , 8)); // vx = A41/det(q_delta)
p_result.y = 1/det*(q.det(0, 2, 3, 1, 5, 6, 2, 7 , 8)); // vy = A42/det(q_delta)
p_result.z = -1/det*(q.det(0, 1, 3, 1, 4, 6, 2, 5, 8)); // vz = A43/det(q_delta)
error = vertex_error(q, p_result.x, p_result.y, p_result.z);
}
else
{
// det = 0 -> 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;
}
///////////////////////////////////////////

View File

@@ -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<Base::Vector3f> temp = _kernel.CalcVertexNormals();

View File

@@ -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<Base::Vector3d> getPointNormals() const;
void crossSections(const std::vector<TPlane>&, std::vector<TPolylines> &sections,

View File

@@ -409,6 +409,19 @@ The argument int is the mode: 0=inner, 1=outer
smooth([iteration=1,maxError=FLT_MAX])</UserDocu>
</Documentation>
</Methode>
<Methode Name="decimate">
<Documentation>
<UserDocu>
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
</UserDocu>
</Documentation>
</Methode>
<Methode Name="optimizeTopology" Const="true">
<Documentation>
<UserDocu>Optimize the edges to get nicer facets</UserDocu>

View File

@@ -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)

View File

@@ -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);

View File

@@ -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