Skip to content

Commit c5f7198

Browse files
committed
Library version updated to v1.0.0.
The new leaf_ranges method of the kd_tree returns the indices per leaf. Renamed apply_one_space to apply_dim_space.
1 parent 6f73ef5 commit c5f7198

File tree

10 files changed

+100
-11
lines changed

10 files changed

+100
-11
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/utils.cmake)
66

77
project(pico_tree
88
LANGUAGES CXX
9-
VERSION 0.8.3
9+
VERSION 1.0.0
1010
DESCRIPTION "PicoTree is a C++ header only library for fast nearest neighbor searches and range searches using a KdTree."
1111
HOMEPAGE_URL "https://github.com/Jaybro/pico_tree")
1212

examples/kd_tree/kd_tree_custom_metric.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
// This example shows how to create a custom metric for the kd_tree. The kd_tree
66
// does not support metrics that "normalize" the distance between two points for
77
// performance reasons. This means that, for example, the L2 metric is not
8-
// supported, but there is support for the L1 or L2^2 metric.
8+
// supported, but that there is support for the L1 or L2^2 metric.
99
//
1010
// There are two different categories of metrics: Euclidean and topological. A
1111
// Euclidean metric supports any R^n space where all axes are orthogonal with
1212
// respect to each other. A topological space is the same, but it allows
13-
// wrapping of coordinate values along any of the axes. An implementation for
14-
// both is provided below.
13+
// wrapping of coordinate values along any of the axes. An implementation of a
14+
// custom metric is provided for both below.
1515

1616
// The LP^P metric is a generalization of the L2^2 metric.
1717
template <std::size_t P_>
@@ -72,7 +72,7 @@ struct metric_t2_squared {
7272
}
7373

7474
template <typename UnaryPredicate_>
75-
void apply_one_space([[maybe_unused]] int dim, UnaryPredicate_ p) const {
75+
void apply_dim_space([[maybe_unused]] int dim, UnaryPredicate_ p) const {
7676
p(pico_tree::one_space_s1{});
7777
}
7878
};

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
setup(
66
name='pico_tree',
77
# The same as the CMake project version.
8-
version='0.8.3',
8+
version='1.0.0',
99
description='PicoTree Python Bindings',
1010
author='Jonathan Broere',
1111
url='https://github.com/Jaybro/pico_tree',

src/pico_tree/pico_tree/internal/box.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ class metric_box_map {
357357
auto bd = [&](auto one_space) {
358358
contains = make_segment(min, max, one_space).contains(v);
359359
};
360-
metric_.apply_one_space(dim, bd);
360+
metric_.apply_dim_space(dim, bd);
361361
return contains;
362362
}
363363

src/pico_tree/pico_tree/internal/kd_tree_data.hpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#pragma once
22

3+
#include <iterator>
4+
#include <vector>
5+
36
#include "pico_tree/core.hpp"
47
#include "pico_tree/internal/memory.hpp"
58
#include "pico_tree/internal/stream_wrapper.hpp"
@@ -9,13 +12,33 @@ namespace pico_tree::internal {
912
//! \brief The data structure that represents a kd_tree.
1013
template <typename Node_, size_t Dim_>
1114
class kd_tree_data {
15+
template <typename Iterator_>
16+
class iterator_range {
17+
public:
18+
using iterator_type = Iterator_;
19+
using difference_type =
20+
typename std::iterator_traits<Iterator_>::difference_type;
21+
22+
constexpr iterator_range(Iterator_ begin, Iterator_ end)
23+
: begin_(begin), end_(end) {}
24+
25+
constexpr Iterator_ begin() const { return begin_; }
26+
constexpr Iterator_ end() const { return end_; }
27+
28+
private:
29+
Iterator_ begin_;
30+
Iterator_ end_;
31+
};
32+
1233
public:
1334
using index_type = typename Node_::index_type;
1435
using scalar_type = typename Node_::scalar_type;
1536
static size_t constexpr dim = Dim_;
1637
using box_type = internal::box<scalar_type, dim>;
1738
using node_type = Node_;
1839
using node_allocator_type = chunk_allocator<node_type, 256>;
40+
using leaf_range_type =
41+
iterator_range<typename std::vector<index_type>::const_iterator>;
1942

2043
static kd_tree_data load(stream_wrapper& stream) {
2144
typename box_type::size_type sdim;
@@ -34,6 +57,12 @@ class kd_tree_data {
3457
data.write(stream);
3558
}
3659

60+
inline std::vector<leaf_range_type> leaf_ranges() const {
61+
std::vector<leaf_range_type> ranges;
62+
insert_leaf_range(root_node, ranges);
63+
return ranges;
64+
}
65+
3766
//! \brief Sorted indices that refer to points inside points_.
3867
std::vector<index_type> indices;
3968
//! \brief Bounding box of the root node.
@@ -44,6 +73,19 @@ class kd_tree_data {
4473
node_type* root_node;
4574

4675
private:
76+
inline void insert_leaf_range(
77+
node_type const* const node, std::vector<leaf_range_type>& ranges) const {
78+
if (node->is_leaf() && !node->data.leaf.empty()) {
79+
using difference_type = typename leaf_range_type::difference_type;
80+
ranges.push_back(leaf_range_type(
81+
indices.begin() + difference_type(node->data.leaf.begin_idx),
82+
indices.begin() + difference_type(node->data.leaf.end_idx)));
83+
} else {
84+
insert_leaf_range(node->left, ranges);
85+
insert_leaf_range(node->right, ranges);
86+
}
87+
}
88+
4789
//! \brief Recursively reads the Node and its descendants.
4890
inline node_type* read_node(stream_wrapper& stream) {
4991
node_type* node = allocator.allocate();

src/pico_tree/pico_tree/internal/kd_tree_node.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ struct kd_tree_node_base {
2929
//! \brief Tree leaf data.
3030
template <typename Index_>
3131
struct kd_tree_leaf {
32+
//! \brief Returns true if the leaf is empty.
33+
constexpr bool empty() const { return begin_idx == end_idx; }
34+
3235
//! \brief Begin of an index range.
3336
Index_ begin_idx;
3437
//! \brief End of an index range.

src/pico_tree/pico_tree/internal/kd_tree_search.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ class search_nearest_topological {
206206
scalar_type min, scalar_type max, scalar_type v, int dim) const {
207207
scalar_type d;
208208
auto bd = [&](auto one_space) { d = box_distance(min, max, v, one_space); };
209-
metric_.apply_one_space(dim, bd);
209+
metric_.apply_dim_space(dim, bd);
210210
return metric_(d);
211211
}
212212

src/pico_tree/pico_tree/kd_tree.hpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class kd_tree {
5050
using metric_type = Metric_;
5151
//! \brief Neighbor type of various search results.
5252
using neighbor_type = neighbor<index_type, scalar_type>;
53+
//! \brief Leaf index range type.
54+
using leaf_range_type = typename kd_tree_data_type::leaf_range_type;
5355

5456
//! \brief Creates a kd_tree given \p space and \p stop_condition.
5557
//! \details The kd_tree takes \p space by value. This allows it to take
@@ -269,7 +271,8 @@ class kd_tree {
269271
//! within radius \p radius and stores the results in output vector \p n.
270272
//! \see template <typename P_, typename RandomAccessIterator_> void
271273
//! search_radius(P_ const&, scalar_type, std::vector<neighbor_type>&, bool)
272-
//! const \see template <typename P_, typename RandomAccessIterator_> void
274+
//! const
275+
//! \see template <typename P_, typename RandomAccessIterator_> void
273276
//! search_nn(P_ const&, scalar_type, neighbor_type&) const
274277
template <typename P_>
275278
inline void search_radius(
@@ -314,6 +317,15 @@ class kd_tree {
314317
idxs)(data_.root_node);
315318
}
316319

320+
//! \brief Returns the index range for all non-empty leaves in the kd_tree.
321+
//! \details The leaf order follows from a depth-first traversal of the
322+
//! kd_tree. If the input bounds is a hyper cube, then the leaf order is
323+
//! identical to that of the N-order curve (a rotated version of the Z-order
324+
//! curve that splits nodes in reversed order).
325+
inline std::vector<leaf_range_type> leaf_ranges() const {
326+
return data_.leaf_ranges();
327+
}
328+
317329
//! \brief Point set used by the tree.
318330
inline space_type const& space() const { return space_; }
319331

src/pico_tree/pico_tree/metric.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ struct metric_so2 {
214214
}
215215

216216
template <typename UnaryPredicate_>
217-
void apply_one_space([[maybe_unused]] int dim, UnaryPredicate_ p) const {
217+
void apply_dim_space([[maybe_unused]] int dim, UnaryPredicate_ p) const {
218218
p(one_space_s1{});
219219
}
220220
};
@@ -247,7 +247,7 @@ struct metric_se2_squared {
247247
}
248248

249249
template <typename UnaryPredicate_>
250-
void apply_one_space(int dim, UnaryPredicate_ p) const {
250+
void apply_dim_space(int dim, UnaryPredicate_ p) const {
251251
if (dim < 2) {
252252
p(one_space_r1{});
253253
} else {

test/pico_tree/kd_tree_test.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,35 @@ TEST(KdTreeTest, WriteRead) {
144144

145145
EXPECT_TRUE(std::filesystem::remove(filename));
146146
}
147+
148+
TEST(KdTreeTest, LeafRanges) {
149+
using point_type = pico_tree::point_2f;
150+
using scalar_type = typename point_type::scalar_type;
151+
using index_type = int;
152+
std::size_t point_count = 100;
153+
scalar_type area_size = 2;
154+
std::vector<point_type> random =
155+
pico_tree::generate_random_n<point_type>(point_count, area_size);
156+
157+
kd_tree<point_type> tree(random, pico_tree::max_leaf_depth_t(2));
158+
159+
auto leaf_ranges = tree.leaf_ranges();
160+
EXPECT_EQ(leaf_ranges.size(), 4);
161+
if (!leaf_ranges.empty()) {
162+
std::ptrdiff_t sum_range_point_count = 0;
163+
index_type index_sum = 0;
164+
for (auto const& r : leaf_ranges) {
165+
sum_range_point_count += std::distance(r.begin(), r.end());
166+
for (int i : r) {
167+
index_sum += i;
168+
}
169+
}
170+
EXPECT_EQ(static_cast<std::size_t>(sum_range_point_count), point_count);
171+
EXPECT_EQ(
172+
static_cast<std::size_t>(index_sum),
173+
(point_count - 1) * point_count / 2);
174+
EXPECT_EQ(
175+
std::distance(leaf_ranges.front().begin(), leaf_ranges.back().end()),
176+
point_count);
177+
}
178+
}

0 commit comments

Comments
 (0)