Skip to content

Commit 743634b

Browse files
Merge pull request #986 from Geode-solutions/feat/implement_dynamic_kdtree
Feat/implement_dynamic_kdtree
2 parents c2aab5d + 16d7a8b commit 743634b

File tree

7 files changed

+410
-12
lines changed

7 files changed

+410
-12
lines changed

bindings/python/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
# This file is autogenerated by pip-compile with Python 3.10
33
# by the following command:
44
#
5-
# pip-compile bindings/python/requirements.in
5+
# pip-compile --pre bindings/python/requirements.in
66
#
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) 2019 - 2024 Geode-solutions
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*
22+
*/
23+
24+
#pragma once
25+
26+
#include <geode/basic/pimpl.hpp>
27+
28+
#include <geode/geometry/common.hpp>
29+
#include <geode/geometry/point.hpp>
30+
31+
namespace geode
32+
{
33+
/*!
34+
* Given a list of points, this class returns neighboring points.
35+
*/
36+
template < index_t dimension >
37+
class DynamicNNSearch
38+
{
39+
OPENGEODE_DISABLE_COPY( DynamicNNSearch );
40+
41+
public:
42+
explicit DynamicNNSearch( std::vector< Point< dimension > > points );
43+
DynamicNNSearch( DynamicNNSearch&& other ) noexcept;
44+
~DynamicNNSearch();
45+
46+
index_t nb_points() const;
47+
48+
const Point< dimension >& point( index_t index ) const;
49+
void add_point( const Point< dimension >& point );
50+
51+
/*!
52+
* Get the neighbors closer than a given distance from the given point
53+
* or within a sphere
54+
* @param[in] point The center of the sphere
55+
* @param[in] threshold_distance The radius of the sphere
56+
* @return the list of points inside this distance
57+
*/
58+
std::vector< index_t > radius_neighbors(
59+
const Point< dimension >& point, double threshold_distance ) const;
60+
61+
private:
62+
IMPLEMENTATION_MEMBER( impl_ );
63+
};
64+
ALIAS_2D_AND_3D( DynamicNNSearch );
65+
} // namespace geode

src/geode/geometry/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ add_geode_library(
3838
"common.cpp"
3939
"coordinate_system.cpp"
4040
"distance.cpp"
41+
"dynamic_nn_search.cpp"
4142
"frame.cpp"
4243
"information.cpp"
4344
"intersection.cpp"
@@ -69,6 +70,7 @@ add_geode_library(
6970
"common.hpp"
7071
"coordinate_system.hpp"
7172
"distance.hpp"
73+
"dynamic_nn_search.hpp"
7274
"frame.hpp"
7375
"information.hpp"
7476
"intersection.hpp"
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* Copyright (c) 2019 - 2024 Geode-solutions
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*
22+
*/
23+
24+
#include <geode/geometry/dynamic_nn_search.hpp>
25+
26+
#include <nanoflann.hpp>
27+
28+
#include <absl/algorithm/container.h>
29+
30+
#include <geode/basic/logger.hpp>
31+
#include <geode/basic/pimpl_impl.hpp>
32+
33+
namespace geode
34+
{
35+
template < index_t dimension >
36+
class DynamicNNSearch< dimension >::Impl
37+
{
38+
public:
39+
explicit Impl( std::vector< Point< dimension > > points )
40+
: cloud_{ std::move( points ) },
41+
dynamic_nn_tree_{ dimension, cloud_ }
42+
{
43+
}
44+
45+
const Point< dimension >& point( const index_t index ) const
46+
{
47+
return cloud_.points.at( index );
48+
}
49+
50+
index_t nb_points() const
51+
{
52+
return cloud_.kdtree_get_point_count();
53+
}
54+
55+
std::vector< index_t > radius_neighbors(
56+
const Point< dimension >& point,
57+
const double threshold_distance ) const
58+
{
59+
std::vector< nanoflann::ResultItem< index_t, double > > results;
60+
nanoflann::RadiusResultSet< double, index_t > resultSet(
61+
threshold_distance * threshold_distance, results );
62+
dynamic_nn_tree_.findNeighbors( resultSet, &copy( point )[0] );
63+
absl::c_sort( results, nanoflann::IndexDist_Sorter() );
64+
std::vector< index_t > indices;
65+
indices.reserve( results.size() );
66+
for( const auto& result : results )
67+
{
68+
indices.emplace_back( result.first );
69+
}
70+
return indices;
71+
}
72+
73+
void add_point( const Point< dimension >& point )
74+
{
75+
const auto nb_points = cloud_.kdtree_get_point_count();
76+
cloud_.points.emplace_back( point );
77+
dynamic_nn_tree_.addPoints( nb_points, nb_points );
78+
}
79+
80+
private:
81+
std::array< double, dimension > copy(
82+
const Point< dimension >& point ) const
83+
{
84+
std::array< double, dimension > result;
85+
for( const auto i : LRange{ dimension } )
86+
{
87+
result[i] = point.value( i );
88+
}
89+
return result;
90+
}
91+
92+
struct PointCloud
93+
{
94+
std::vector< Point< dimension > > points;
95+
96+
size_t kdtree_get_point_count() const
97+
{
98+
return points.size();
99+
}
100+
101+
double kdtree_get_pt( size_t idx, size_t dim ) const
102+
{
103+
return points[idx].value( dim );
104+
}
105+
106+
// Optional bounding-box computation: return false to default to a
107+
// standard bbox computation loop.
108+
// Return true if the BBOX was already computed by the class and
109+
// returned in "bb" so it can be avoided to redo it again. Look at
110+
// bb.size() to find out the expected dimensionality (e.g. 2 or 3
111+
// for point clouds)
112+
template < class BBOX >
113+
bool kdtree_get_bbox( BBOX& /* bb */ ) const
114+
{
115+
return false;
116+
}
117+
};
118+
119+
private:
120+
PointCloud cloud_;
121+
nanoflann::KDTreeSingleIndexDynamicAdaptor<
122+
nanoflann::L2_Simple_Adaptor< double, PointCloud >,
123+
PointCloud,
124+
dimension,
125+
index_t >
126+
dynamic_nn_tree_;
127+
};
128+
129+
template < index_t dimension >
130+
DynamicNNSearch< dimension >::DynamicNNSearch(
131+
std::vector< Point< dimension > > points )
132+
: impl_{ std::move( points ) }
133+
{
134+
}
135+
136+
template < index_t dimension >
137+
DynamicNNSearch< dimension >::DynamicNNSearch(
138+
DynamicNNSearch&& ) noexcept = default;
139+
140+
template < index_t dimension >
141+
DynamicNNSearch< dimension >::~DynamicNNSearch() = default;
142+
143+
template < index_t dimension >
144+
const Point< dimension >& DynamicNNSearch< dimension >::point(
145+
index_t index ) const
146+
{
147+
return impl_->point( index );
148+
}
149+
150+
template < index_t dimension >
151+
index_t DynamicNNSearch< dimension >::nb_points() const
152+
{
153+
return impl_->nb_points();
154+
}
155+
156+
template < index_t dimension >
157+
std::vector< index_t > DynamicNNSearch< dimension >::radius_neighbors(
158+
const Point< dimension >& point, double threshold_distance ) const
159+
{
160+
return impl_->radius_neighbors( point, threshold_distance );
161+
}
162+
163+
template < index_t dimension >
164+
void DynamicNNSearch< dimension >::add_point(
165+
const Point< dimension >& point )
166+
{
167+
return impl_->add_point( point );
168+
}
169+
170+
template class opengeode_geometry_api DynamicNNSearch< 2 >;
171+
template class opengeode_geometry_api DynamicNNSearch< 3 >;
172+
} // namespace geode

src/geode/geometry/points_sort.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ namespace
7070
* the two halves
7171
*/
7272
template < typename Compare >
73-
itr split( const itr& begin, const itr& end, const Compare& cmp )
73+
itr split_container( const itr& begin, const itr& end, const Compare& cmp )
7474
{
7575
if( begin >= end )
7676
{
@@ -108,13 +108,13 @@ namespace
108108

109109
const auto m0 = begin;
110110
const auto m8 = end;
111-
const auto m4 = split( m0, m8, compX );
112-
const auto m2 = split( m0, m4, compY );
113-
const auto m1 = split( m0, m2, compZ );
114-
const auto m3 = split( m2, m4, compZ );
115-
const auto m6 = split( m4, m8, compY );
116-
const auto m5 = split( m4, m6, compZ );
117-
const auto m7 = split( m6, m8, compZ );
111+
const auto m4 = split_container( m0, m8, compX );
112+
const auto m2 = split_container( m0, m4, compY );
113+
const auto m1 = split_container( m0, m2, compZ );
114+
const auto m3 = split_container( m2, m4, compZ );
115+
const auto m6 = split_container( m4, m8, compY );
116+
const auto m5 = split_container( m4, m6, compZ );
117+
const auto m7 = split_container( m6, m8, compZ );
118118
morton_mapping< COORDZ >( points, m0, m1 );
119119
morton_mapping< COORDY >( points, m1, m2 );
120120
morton_mapping< COORDY >( points, m2, m3 );
@@ -142,9 +142,9 @@ namespace
142142

143143
const auto m0 = begin;
144144
const auto m4 = end;
145-
const auto m2 = split( m0, m4, compX );
146-
const auto m1 = split( m0, m2, compY );
147-
const auto m3 = split( m2, m4, compY );
145+
const auto m2 = split_container( m0, m4, compX );
146+
const auto m1 = split_container( m0, m2, compY );
147+
const auto m3 = split_container( m2, m4, compY );
148148
morton_mapping< COORDY >( points, m0, m1 );
149149
morton_mapping< COORDX >( points, m1, m2 );
150150
morton_mapping< COORDX >( points, m2, m3 );

tests/geometry/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ add_geode_test(
6060
${PROJECT_NAME}::basic
6161
${PROJECT_NAME}::geometry
6262
)
63+
add_geode_test(
64+
SOURCE "test-dynamic-nnsearch.cpp"
65+
DEPENDENCIES
66+
${PROJECT_NAME}::basic
67+
${PROJECT_NAME}::geometry
68+
)
6369
add_geode_test(
6470
SOURCE "test-intersection.cpp"
6571
DEPENDENCIES

0 commit comments

Comments
 (0)