nanoflann
C++ header-only ANN library
Loading...
Searching...
No Matches
nanoflann.hpp
1/***********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
5 * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
6 * Copyright 2011-2026 Jose Luis Blanco (joseluisblancoc@gmail.com).
7 * All rights reserved.
8 *
9 * THE BSD LICENSE
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *************************************************************************/
32
63
64#pragma once
65
66#include <algorithm>
67#include <array>
68#include <atomic>
69#include <cassert>
70#include <chrono> // std::chrono (async incremental index polling)
71#include <cmath> // for abs()
72#include <cstdint>
73#include <cstdio> // snprintf
74#include <cstdlib> // for abs()
75#include <functional> // std::reference_wrapper
76#include <future>
77#include <istream>
78#include <limits> // std::numeric_limits
79#include <memory> // std::unique_ptr (async incremental index)
80#include <new> // placement new (incremental index node pool)
81#include <ostream>
82#include <stack>
83#include <stdexcept>
84#include <type_traits> // std::is_trivially_destructible
85#include <unordered_map>
86#include <vector>
87
89#define NANOFLANN_VERSION_STRING "1.10.1"
91#define NANOFLANN_VERSION 0x010A01
92
93// Avoid conflicting declaration of min/max macros in Windows headers
94#if !defined(NOMINMAX) && (defined(_WIN32) || defined(_WIN32_) || defined(WIN32) || defined(_WIN64))
95#define NOMINMAX
96#ifdef max
97#undef max
98#undef min
99#endif
100#endif
101// Avoid conflicts with X11 headers
102#ifdef None
103#undef None
104#endif
105
106// Handle restricted pointers
107#if defined(__GNUC__) || defined(__clang__)
108#define NANOFLANN_RESTRICT __restrict__
109#elif defined(_MSC_VER)
110#define NANOFLANN_RESTRICT __restrict
111#else
112#define NANOFLANN_RESTRICT
113#endif
114
115// [[nodiscard]] support
116#if defined(__has_cpp_attribute) && __has_cpp_attribute(nodiscard)
117#define NANOFLANN_NODISCARD [[nodiscard]]
118#else
119#define NANOFLANN_NODISCARD
120#endif
121
122// [[fallthrough]] support for intentional switch fall-throughs
123#if defined(__has_cpp_attribute) && __has_cpp_attribute(fallthrough)
124#define NANOFLANN_FALLTHROUGH [[fallthrough]]
125#else
126#define NANOFLANN_FALLTHROUGH
127#endif
128
129// Memory alignment of KD-tree nodes:
130#ifndef NANOFLANN_NODE_ALIGNMENT
131#define NANOFLANN_NODE_ALIGNMENT 16
132#endif
133
134namespace nanoflann
135{
138
140template <typename T>
141constexpr T pi_const()
142{
143 return static_cast<T>(3.14159265358979323846);
144}
145
150template <typename T, typename = int>
151struct has_resize : std::false_type
152{
153};
154
155template <typename T>
156struct has_resize<T, decltype((void)std::declval<T>().resize(1), 0)> : std::true_type
157{
158};
159
160template <typename T, typename = int>
161struct has_assign : std::false_type
162{
163};
164
165template <typename T>
166struct has_assign<T, decltype((void)std::declval<T>().assign(1, 0), 0)> : std::true_type
167{
168};
169
173template <typename Container>
174inline typename std::enable_if<has_resize<Container>::value, void>::type resize(
175 Container& c, const size_t nElements)
176{
177 c.resize(nElements);
178}
179
184template <typename Container>
185inline typename std::enable_if<!has_resize<Container>::value, void>::type resize(
186 Container& c, const size_t nElements)
187{
188 if (nElements != c.size()) throw std::logic_error("Attempt to resize a fixed size container.");
189}
190
194template <typename Container, typename T>
195inline typename std::enable_if<has_assign<Container>::value, void>::type assign(
196 Container& c, const size_t nElements, const T& value)
197{
198 c.assign(nElements, value);
199}
200
204template <typename Container, typename T>
205inline typename std::enable_if<!has_assign<Container>::value, void>::type assign(
206 Container& c, const size_t nElements, const T& value)
207{
208 for (size_t i = 0; i < nElements; i++) c[i] = value;
209}
210
213{
215 template <typename PairType>
216 bool operator()(const PairType& p1, const PairType& p2) const
217 {
218 return p1.second < p2.second;
219 }
220};
221
230template <typename IndexType = size_t, typename DistanceType = double>
231struct ResultItem
232{
233 ResultItem() = default;
234 ResultItem(const IndexType index, const DistanceType distance) : first(index), second(distance)
235 {
236 }
237
238 IndexType first;
239 DistanceType second;
240};
241
242namespace detail
243{
248template <typename DistanceType, typename IndexType, typename CountType>
249bool addPointToSortedResultSet(
250 DistanceType* dists, IndexType* indices, CountType& count, CountType capacity,
251 DistanceType dist, IndexType index)
252{
253 CountType i;
254 for (i = count; i > 0; --i)
255 {
256#ifdef NANOFLANN_FIRST_MATCH
257 if ((dists[i - 1] > dist) || ((dist == dists[i - 1]) && (indices[i - 1] > index)))
258 {
259#else
260 if (dists[i - 1] > dist)
261 {
262#endif
263 if (i < capacity)
264 {
265 dists[i] = dists[i - 1];
266 indices[i] = indices[i - 1];
267 }
268 }
269 else
270 break;
271 }
272 if (i < capacity)
273 {
274 dists[i] = dist;
275 indices[i] = index;
276 }
277 if (count < capacity) count++;
278 return true;
279}
280} // namespace detail
281
284
286template <typename _DistanceType, typename _IndexType = size_t, typename _CountType = size_t>
287class KNNResultSet
288{
289 public:
290 using DistanceType = _DistanceType;
291 using IndexType = _IndexType;
292 using CountType = _CountType;
293
294 private:
295 IndexType* indices;
296 DistanceType* dists;
297 CountType capacity;
298 CountType count;
299
300 public:
301 explicit KNNResultSet(CountType capacity_)
302 : indices(nullptr), dists(nullptr), capacity(capacity_), count(0)
303 {
304 }
305
306 void init(IndexType* indices_, DistanceType* dists_)
307 {
308 indices = indices_;
309 dists = dists_;
310 count = 0;
311 }
312
313 NANOFLANN_NODISCARD CountType size() const noexcept { return count; }
314 NANOFLANN_NODISCARD bool empty() const noexcept { return count == 0; }
315 NANOFLANN_NODISCARD bool full() const noexcept { return count == capacity; }
316
322 bool addPoint(DistanceType dist, IndexType index)
323 {
324 return detail::addPointToSortedResultSet(dists, indices, count, capacity, dist, index);
325 }
326
329 NANOFLANN_NODISCARD DistanceType worstDist() const noexcept
330 {
331 return (count < capacity || !count) ? std::numeric_limits<DistanceType>::max()
332 : dists[count - 1];
333 }
334
335 void sort()
336 {
337 // already sorted
338 }
339};
340
342template <typename _DistanceType, typename _IndexType = size_t, typename _CountType = size_t>
343class RKNNResultSet
344{
345 public:
346 using DistanceType = _DistanceType;
347 using IndexType = _IndexType;
348 using CountType = _CountType;
349
350 private:
351 IndexType* indices;
352 DistanceType* dists;
353 CountType capacity;
354 CountType count;
355 DistanceType maximumSearchDistanceSquared;
356
357 public:
358 explicit RKNNResultSet(CountType capacity_, DistanceType maximumSearchDistanceSquared_)
359 : indices(nullptr),
360 dists(nullptr),
361 capacity(capacity_),
362 count(0),
363 maximumSearchDistanceSquared(maximumSearchDistanceSquared_)
364 {
365 }
366
367 void init(IndexType* indices_, DistanceType* dists_)
368 {
369 indices = indices_;
370 dists = dists_;
371 count = 0;
372 if (capacity) dists[capacity - 1] = maximumSearchDistanceSquared;
373 }
374
375 NANOFLANN_NODISCARD CountType size() const noexcept { return count; }
376 NANOFLANN_NODISCARD bool empty() const noexcept { return count == 0; }
377 NANOFLANN_NODISCARD bool full() const noexcept { return count == capacity; }
378
384 bool addPoint(DistanceType dist, IndexType index)
385 {
386 return detail::addPointToSortedResultSet(dists, indices, count, capacity, dist, index);
387 }
388
391 NANOFLANN_NODISCARD DistanceType worstDist() const noexcept
392 {
393 return (count < capacity || !count) ? maximumSearchDistanceSquared : dists[count - 1];
394 }
395
396 void sort()
397 {
398 // already sorted
399 }
400};
401
405template <typename _DistanceType, typename _IndexType = size_t>
406class RadiusResultSet
407{
408 public:
409 using DistanceType = _DistanceType;
410 using IndexType = _IndexType;
411
412 public:
413 const DistanceType radius;
414
415 std::vector<ResultItem<IndexType, DistanceType>>& m_indices_dists;
416
417 explicit RadiusResultSet(
418 DistanceType radius_, std::vector<ResultItem<IndexType, DistanceType>>& indices_dists)
419 : radius(radius_), m_indices_dists(indices_dists)
420 {
421 init();
422 }
423
424 void init() { clear(); }
425 void clear() { m_indices_dists.clear(); }
426
427 NANOFLANN_NODISCARD size_t size() const noexcept { return m_indices_dists.size(); }
428 NANOFLANN_NODISCARD bool empty() const noexcept { return m_indices_dists.empty(); }
429 NANOFLANN_NODISCARD bool full() const noexcept { return true; }
430
436 bool addPoint(DistanceType dist, IndexType index)
437 {
438 if (dist < radius) m_indices_dists.emplace_back(index, dist);
439 return true;
440 }
441
442 NANOFLANN_NODISCARD DistanceType worstDist() const noexcept { return radius; }
443
449 {
450 if (m_indices_dists.empty())
451 throw std::runtime_error(
452 "Cannot invoke RadiusResultSet::worst_item() on "
453 "an empty list of results.");
454 auto it =
455 std::max_element(m_indices_dists.begin(), m_indices_dists.end(), IndexDist_Sorter());
456 return *it;
457 }
458
459 void sort() { std::sort(m_indices_dists.begin(), m_indices_dists.end(), IndexDist_Sorter()); }
460};
461
467template <typename _IndexType = size_t>
468class BoxResultSet
469{
470 public:
471 using IndexType = _IndexType;
472
473 std::vector<IndexType>& m_indices;
474
475 explicit BoxResultSet(std::vector<IndexType>& indices) : m_indices(indices)
476 {
477 m_indices.clear();
478 }
479
480 NANOFLANN_NODISCARD size_t size() const noexcept { return m_indices.size(); }
481 NANOFLANN_NODISCARD bool empty() const noexcept { return m_indices.empty(); }
482 NANOFLANN_NODISCARD bool full() const noexcept { return true; }
483
486 template <typename DistanceType>
487 bool addPoint(DistanceType /*dist*/, IndexType index)
488 {
489 m_indices.push_back(index);
490 return true;
491 }
492
493 void sort() { std::sort(m_indices.begin(), m_indices.end()); }
494};
495
497
500template <typename T>
501void save_value(std::ostream& stream, const T& value)
502{
503 stream.write(reinterpret_cast<const char*>(&value), sizeof(T));
504}
505
506template <typename T>
507void save_value(std::ostream& stream, const std::vector<T>& value)
508{
509 size_t size = value.size();
510 stream.write(reinterpret_cast<const char*>(&size), sizeof(size_t));
511 stream.write(reinterpret_cast<const char*>(value.data()), sizeof(T) * size);
512}
513
514template <typename T>
515void load_value(std::istream& stream, T& value)
516{
517 stream.read(reinterpret_cast<char*>(&value), sizeof(T));
518}
519
520template <typename T>
521void load_value(std::istream& stream, std::vector<T>& value)
522{
523 size_t size;
524 stream.read(reinterpret_cast<char*>(&size), sizeof(size_t));
525 value.resize(size);
526 stream.read(reinterpret_cast<char*>(value.data()), sizeof(T) * size);
527}
529
532
533struct Metric
534{
535};
536
547template <class T, class DataSource, typename _DistanceType = T, typename IndexType = size_t>
548struct L1_Adaptor
549{
550 using ElementType = T;
551 using DistanceType = _DistanceType;
552
553 const DataSource& data_source;
554
555 L1_Adaptor(const DataSource& _data_source) : data_source(_data_source) {}
556
557 inline DistanceType evalMetric(
558 const T* NANOFLANN_RESTRICT a, const IndexType b_idx, size_t size) const
559 {
560 DistanceType result = DistanceType();
561 const size_t multof4 = (size >> 2) << 2; // largest multiple of 4
562 size_t d;
563
564 for (d = 0; d < multof4; d += 4)
565 {
566 const DistanceType diff0 = std::abs(a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0));
567 const DistanceType diff1 = std::abs(a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1));
568 const DistanceType diff2 = std::abs(a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2));
569 const DistanceType diff3 = std::abs(a[d + 3] - data_source.kdtree_get_pt(b_idx, d + 3));
570 /* Parentheses break dependency chain: */
571 result += (diff0 + diff1) + (diff2 + diff3);
572 }
573 /* Process last 0-3 components. Unrolled loop with fall-through switch.
574 */
575 switch (size - multof4)
576 {
577 case 3:
578 result += std::abs(a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2));
579 NANOFLANN_FALLTHROUGH;
580 case 2:
581 result += std::abs(a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1));
582 NANOFLANN_FALLTHROUGH;
583 case 1:
584 result += std::abs(a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0));
585 NANOFLANN_FALLTHROUGH;
586 case 0:
587 break;
588 }
589 return result;
590 }
591
592 template <typename U, typename V>
593 inline DistanceType accum_dist(const U a, const V b, const size_t) const
594 {
595 return std::abs(a - b);
596 }
597};
598
609template <class T, class DataSource, typename _DistanceType = T, typename IndexType = size_t>
610struct L2_Adaptor
611{
612 using ElementType = T;
613 using DistanceType = _DistanceType;
614
615 const DataSource& data_source;
616
617 L2_Adaptor(const DataSource& _data_source) : data_source(_data_source) {}
618
619 inline DistanceType evalMetric(
620 const T* NANOFLANN_RESTRICT a, const IndexType b_idx, size_t size) const
621 {
622 DistanceType result = DistanceType();
623 const size_t multof4 = (size >> 2) << 2; // largest multiple of 4
624 size_t d;
625
626 for (d = 0; d < multof4; d += 4)
627 {
628 const DistanceType diff0 = a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0);
629 const DistanceType diff1 = a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1);
630 const DistanceType diff2 = a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2);
631 const DistanceType diff3 = a[d + 3] - data_source.kdtree_get_pt(b_idx, d + 3);
632 /* Parentheses break dependency chain: */
633 result += (diff0 * diff0 + diff1 * diff1) + (diff2 * diff2 + diff3 * diff3);
634 }
635 /* Process last 0-3 components. Unrolled loop with fall-through switch.
636 */
637 DistanceType diff;
638 switch (size - multof4)
639 {
640 case 3:
641 diff = a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2);
642 result += diff * diff;
643 NANOFLANN_FALLTHROUGH;
644 case 2:
645 diff = a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1);
646 result += diff * diff;
647 NANOFLANN_FALLTHROUGH;
648 case 1:
649 diff = a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0);
650 result += diff * diff;
651 NANOFLANN_FALLTHROUGH;
652 case 0:
653 break;
654 }
655 return result;
656 }
657
658 template <typename U, typename V>
659 inline DistanceType accum_dist(const U a, const V b, const size_t) const
660 {
661 auto diff = a - b;
662 return diff * diff;
663 }
664};
665
676template <class T, class DataSource, typename _DistanceType = T, typename IndexType = size_t>
677struct L2_Simple_Adaptor
678{
679 using ElementType = T;
680 using DistanceType = _DistanceType;
681
682 const DataSource& data_source;
683
684 L2_Simple_Adaptor(const DataSource& _data_source) : data_source(_data_source) {}
685
686 inline DistanceType evalMetric(const T* a, const IndexType b_idx, size_t size) const
687 {
688 DistanceType result = DistanceType();
689 for (size_t i = 0; i < size; ++i)
690 {
691 const DistanceType diff = a[i] - data_source.kdtree_get_pt(b_idx, i);
692 result += diff * diff;
693 }
694 return result;
695 }
696
697 template <typename U, typename V>
698 inline DistanceType accum_dist(const U a, const V b, const size_t) const
699 {
700 auto diff = a - b;
701 return diff * diff;
702 }
703};
704
715template <class T, class DataSource, typename _DistanceType = T, typename IndexType = size_t>
716struct SO2_Adaptor
717{
718 using ElementType = T;
719 using DistanceType = _DistanceType;
720
721 const DataSource& data_source;
722
723 SO2_Adaptor(const DataSource& _data_source) : data_source(_data_source) {}
724
725 inline DistanceType evalMetric(const T* a, const IndexType b_idx, size_t size) const
726 {
727 return accum_dist(a[size - 1], data_source.kdtree_get_pt(b_idx, size - 1), size - 1);
728 }
729
735 template <typename U, typename V>
736 inline DistanceType accum_dist(const U a, const V b, const size_t) const
737 {
738 DistanceType diff = static_cast<DistanceType>(b) - static_cast<DistanceType>(a);
739 const DistanceType PI = pi_const<DistanceType>();
740 if (diff > PI)
741 diff -= 2 * PI;
742 else if (diff < -PI)
743 diff += 2 * PI;
744 return diff < DistanceType(0) ? -diff : diff; // abs without <cmath> dependency
745 }
746};
747
758template <class T, class DataSource, typename _DistanceType = T, typename IndexType = size_t>
759struct SO3_Adaptor
760{
761 using ElementType = T;
762 using DistanceType = _DistanceType;
763
765
766 SO3_Adaptor(const DataSource& _data_source) : distance_L2_Simple(_data_source) {}
767
768 inline DistanceType evalMetric(const T* a, const IndexType b_idx, size_t size) const
769 {
770 return distance_L2_Simple.evalMetric(a, b_idx, size);
771 }
772
773 template <typename U, typename V>
774 inline DistanceType accum_dist(const U a, const V b, const size_t idx) const
775 {
776 return distance_L2_Simple.accum_dist(a, b, idx);
777 }
778};
779
781struct metric_L1 : public Metric
782{
783 template <class T, class DataSource, typename IndexType = size_t>
784 struct traits
785 {
787 };
788};
789
791struct metric_L2 : public Metric
792{
793 template <class T, class DataSource, typename IndexType = size_t>
794 struct traits
795 {
797 };
798};
799
802{
803 template <class T, class DataSource, typename IndexType = size_t>
804 struct traits
805 {
807 };
808};
809
810struct metric_SO2 : public Metric
811{
812 template <class T, class DataSource, typename IndexType = size_t>
813 struct traits
814 {
816 };
817};
818
819struct metric_SO3 : public Metric
820{
821 template <class T, class DataSource, typename IndexType = size_t>
822 struct traits
823 {
825 };
826};
827
829
832
833enum class KDTreeSingleIndexAdaptorFlags
834{
835 None = 0,
836 SkipInitialBuildIndex = 1
837};
838
839inline std::underlying_type<KDTreeSingleIndexAdaptorFlags>::type operator&(
840 KDTreeSingleIndexAdaptorFlags lhs, KDTreeSingleIndexAdaptorFlags rhs)
841{
842 using underlying = typename std::underlying_type<KDTreeSingleIndexAdaptorFlags>::type;
843 return static_cast<underlying>(lhs) & static_cast<underlying>(rhs);
844}
845
848inline bool has_flag(KDTreeSingleIndexAdaptorFlags f, KDTreeSingleIndexAdaptorFlags flag)
849{
850 return (f & flag) != 0;
851}
852
854struct KDTreeSingleIndexAdaptorParams
855{
856 KDTreeSingleIndexAdaptorParams(
857 size_t _leaf_max_size = 10,
858 KDTreeSingleIndexAdaptorFlags _flags = KDTreeSingleIndexAdaptorFlags::None,
859 unsigned int _n_thread_build = 1)
860 : leaf_max_size(_leaf_max_size), flags(_flags), n_thread_build(_n_thread_build)
861 {
862 }
863
864 size_t leaf_max_size;
865 KDTreeSingleIndexAdaptorFlags flags;
866 unsigned int n_thread_build;
867};
868
870struct SearchParameters
871{
872 SearchParameters(float eps_ = 0, bool sorted_ = true) : eps(eps_), sorted(sorted_) {}
873
874 float eps;
875 bool sorted;
877};
878
879
882
898{
899 static constexpr size_t WORDSIZE = 16; // WORDSIZE must >= 8
900 static constexpr size_t BLOCKSIZE = 8192;
901
902 /* We maintain memory alignment to word boundaries by requiring that all
903 allocations be in multiples of the machine wordsize. */
904 /* Size of machine word in bytes. Must be power of 2. */
905 /* Minimum number of bytes requested at a time from the system. Must be
906 * multiple of WORDSIZE. */
907
908 using Size = size_t;
909
910 Size remaining_ = 0;
911 void* base_ = nullptr;
912 void* loc_ = nullptr;
913
914 void internal_init()
915 {
916 remaining_ = 0;
917 base_ = nullptr;
918 usedMemory = 0;
919 wastedMemory = 0;
920 }
921
922 public:
923 Size usedMemory = 0;
924 Size wastedMemory = 0;
925
929 PooledAllocator() { internal_init(); }
930
935
937 void free_all()
938 {
939 while (base_ != nullptr)
940 {
941 // Get pointer to prev block
942 void* prev = *(static_cast<void**>(base_));
943 ::free(base_);
944 base_ = prev;
945 }
946 internal_init();
947 }
948
953 void* allocateBytes(const size_t req_size)
954 {
955 /* Round size up to a multiple of wordsize. The following expression
956 only works for WORDSIZE that is a power of 2, by masking last bits
957 of incremented size to zero.
958 */
959 const Size size = (req_size + (WORDSIZE - 1)) & ~(WORDSIZE - 1);
960
961 /* Check whether a new block must be allocated. Note that the first
962 word of a block is reserved for a pointer to the previous block.
963 */
964 if (size > remaining_)
965 {
966 wastedMemory += remaining_;
967
968 /* Allocate new storage. */
969 const Size blocksize = size > BLOCKSIZE ? size + WORDSIZE : BLOCKSIZE + WORDSIZE;
970
971 // use the standard C malloc to allocate memory
972 void* m = ::malloc(blocksize);
973 if (!m)
974 {
975 throw std::bad_alloc();
976 }
977
978 /* Fill first word of new block with pointer to previous block. */
979 static_cast<void**>(m)[0] = base_;
980 base_ = m;
981
982 remaining_ = blocksize - WORDSIZE;
983 loc_ = static_cast<char*>(m) + WORDSIZE;
984 }
985 void* rloc = loc_;
986 loc_ = static_cast<char*>(loc_) + size;
987 remaining_ -= size;
988
989 usedMemory += size;
990
991 return rloc;
992 }
993
1001 template <typename T>
1002 T* allocate(const size_t count = 1)
1003 {
1004 T* mem = static_cast<T*>(this->allocateBytes(sizeof(T) * count));
1005 return mem;
1006 }
1007};
1008
1009
1012
1016template <int32_t DIM, typename T>
1018{
1019 using type = std::array<T, DIM>;
1020};
1021
1022template <typename T>
1023struct array_or_vector<-1, T>
1024{
1025 using type = std::vector<T>;
1026};
1027
1029
1044template <
1045 class Derived, typename Distance, class DatasetAdaptor, int32_t DIM = -1,
1046 typename index_t = uint32_t>
1048{
1049 public:
1052 void freeIndex(Derived& obj)
1053 {
1054 obj.pool_.free_all();
1055 obj.root_node_ = nullptr;
1056 obj.size_at_index_build_ = 0;
1057 }
1058
1059 using ElementType = typename Distance::ElementType;
1060 using DistanceType = typename Distance::DistanceType;
1061 using IndexType = index_t;
1062
1066 std::vector<IndexType> vAcc_;
1067
1068 using Offset = typename decltype(vAcc_)::size_type;
1069 using Size = typename decltype(vAcc_)::size_type;
1070 using Dimension = int32_t;
1071
1072 /*-------------------------------------------------------------------
1073 * Internal Data Structures
1074 *
1075 * "Node" below can be declared with alignas(N) to improve
1076 * cache friendliness and SIMD load/store performance.
1077 *
1078 * The optimal N depends on the underlying hardware:
1079 * + Intel x86-64: 16 for SSE, 32 for AVX/AVX2 and 64 for AVX-512
1080 * + NVIDIA Jetson: 16 for ARM + NEON and CUDA float4/
1081 * To avoid unnecessary padding, the smallest alignment
1082 * compatible with a platform's vector width should be chosen.
1083 * ------------------------------------------------------------------*/
1084 struct alignas(NANOFLANN_NODE_ALIGNMENT) Node
1085 {
1088 union
1089 {
1090 struct leaf
1091 {
1092 Offset left, right;
1093 } lr;
1094 struct nonleaf
1095 {
1096 Dimension divfeat;
1098 DistanceType divlow, divhigh;
1099 } sub;
1100 } node_type;
1101
1103 Node *child1 = nullptr, *child2 = nullptr;
1104 };
1105
1106 using NodePtr = Node*;
1107 using NodeConstPtr = const Node*;
1108
1110 {
1111 ElementType low, high;
1112 };
1113
1114 NodePtr root_node_ = nullptr;
1115
1116 Size leaf_max_size_ = 0;
1117
1121 Size size_ = 0;
1124 Dimension dim_ = 0;
1125
1128 using BoundingBox = typename array_or_vector<DIM, Interval>::type;
1129
1132 using distance_vector_t = typename array_or_vector<DIM, DistanceType>::type;
1133
1136
1145
1147 NANOFLANN_NODISCARD Size size(const Derived& obj) const noexcept { return obj.size_; }
1148
1153 NANOFLANN_NODISCARD Size veclen(const Derived& obj) const noexcept
1154 {
1155#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
1156 if constexpr (DIM > 0)
1157 {
1158 return DIM;
1159 }
1160 else
1161 {
1162 return obj.dim_;
1163 }
1164#else
1165 return DIM > 0 ? DIM : obj.dim_;
1166#endif
1167 }
1168
1170 ElementType dataset_get(const Derived& obj, IndexType element, Dimension component) const
1171 {
1172 return obj.dataset_.kdtree_get_pt(element, component);
1173 }
1174
1179 NANOFLANN_NODISCARD Size usedMemory(const Derived& obj) const
1180 {
1181 return obj.pool_.usedMemory + obj.pool_.wastedMemory +
1182 obj.dataset_.kdtree_get_point_count() *
1183 sizeof(IndexType); // pool memory and vind array memory
1184 }
1185
1190 const Derived& obj, Offset ind, Size count, Dimension element, ElementType& min_elem,
1191 ElementType& max_elem) const
1192 {
1193 min_elem = dataset_get(obj, vAcc_[ind], element);
1194 max_elem = min_elem;
1195 for (Offset i = 1; i < count; ++i)
1196 {
1197 ElementType val = dataset_get(obj, vAcc_[ind + i], element);
1198 if (val < min_elem) min_elem = val;
1199 if (val > max_elem) max_elem = val;
1200 }
1201 }
1202
1206 NANOFLANN_NODISCARD bool isActive(IndexType /*idx*/) const { return true; }
1207
1212 {
1213 Derived& obj = static_cast<Derived&>(*this);
1214 const Dimension dims = static_cast<Dimension>(veclen(obj));
1215 resize(bbox, dims);
1216 if (obj.dataset_.kdtree_get_bbox(bbox)) return;
1217 if (!size_)
1218 throw std::runtime_error(
1219 "[nanoflann] computeBoundingBox() called but "
1220 "no data points found.");
1221 for (Dimension i = 0; i < dims; ++i)
1222 bbox[i].low = bbox[i].high = dataset_get(obj, vAcc_[0], i);
1223 for (Offset k = 1; k < size_; ++k)
1224 for (Dimension i = 0; i < dims; ++i)
1225 {
1226 const auto val = dataset_get(obj, vAcc_[k], i);
1227 if (val < bbox[i].low) bbox[i].low = val;
1228 if (val > bbox[i].high) bbox[i].high = val;
1229 }
1230 }
1231
1240 template <class RESULTSET>
1242 RESULTSET& result_set, const ElementType* vec, const NodePtr node, DistanceType mindist,
1243 distance_vector_t& dists, const DistanceType epsError) const
1244 {
1245 const Derived& obj = static_cast<const Derived&>(*this);
1246 // If this is a leaf node, then do check and return.
1247 if (!node->child1) // (if one node is nullptr, both are)
1248 {
1249 // Hoist the point length out of the per-point loop. For a
1250 // fixed-size tree (DIM > 0) this is a compile-time constant; for a
1251 // runtime dimension it avoids re-reading obj.dim_ on every point.
1252 const Size dim = veclen(obj);
1253 for (Offset i = node->node_type.lr.left; i < node->node_type.lr.right; ++i)
1254 {
1255 const IndexType accessor = vAcc_[i];
1256 if (!obj.isActive(accessor)) continue;
1257 DistanceType dist = obj.distance_.evalMetric(vec, accessor, dim);
1258 if (dist < result_set.worstDist())
1259 {
1260 if (!result_set.addPoint(
1261 static_cast<typename RESULTSET::DistanceType>(dist),
1262 static_cast<typename RESULTSET::IndexType>(accessor)))
1263 return false;
1264 }
1265 }
1266 return true;
1267 }
1268
1269 /* Which child branch should be taken first? */
1270 Dimension idx = node->node_type.sub.divfeat;
1271 ElementType val = vec[idx];
1272 DistanceType diff1 = val - node->node_type.sub.divlow;
1273 DistanceType diff2 = val - node->node_type.sub.divhigh;
1274
1275 NodePtr bestChild;
1276 NodePtr otherChild;
1277 DistanceType cut_dist;
1278 if ((diff1 + diff2) < 0)
1279 {
1280 bestChild = node->child1;
1281 otherChild = node->child2;
1282 cut_dist = obj.distance_.accum_dist(val, node->node_type.sub.divhigh, idx);
1283 }
1284 else
1285 {
1286 bestChild = node->child2;
1287 otherChild = node->child1;
1288 cut_dist = obj.distance_.accum_dist(val, node->node_type.sub.divlow, idx);
1289 }
1290
1291 /* Call recursively to search next level down. */
1292 if (!searchLevel(result_set, vec, bestChild, mindist, dists, epsError)) return false;
1293
1294 DistanceType dst = dists[idx];
1295 mindist = mindist + cut_dist - dst;
1296 dists[idx] = cut_dist;
1297 if (mindist * epsError <= result_set.worstDist())
1298 {
1299 if (!searchLevel(result_set, vec, otherChild, mindist, dists, epsError)) return false;
1300 }
1301 dists[idx] = dst;
1302 return true;
1303 }
1304
1325 Derived& obj, NodePtr node, const Offset left, const Offset right, BoundingBox& bbox,
1326 Offset& idx, Dimension& cutfeat, DistanceType& cutval)
1327 {
1328 const Dimension dims = static_cast<Dimension>(veclen(obj));
1329
1330 /* If too few exemplars remain, then make this a leaf node. */
1331 if ((right - left) <= static_cast<Offset>(obj.leaf_max_size_))
1332 {
1333 node->child1 = node->child2 = nullptr; /* Mark as leaf node. */
1334 node->node_type.lr.left = left;
1335 node->node_type.lr.right = right;
1336
1337 // compute bounding-box of leaf points
1338 for (Dimension i = 0; i < dims; ++i)
1339 {
1340 bbox[i].low = dataset_get(obj, obj.vAcc_[left], i);
1341 bbox[i].high = dataset_get(obj, obj.vAcc_[left], i);
1342 }
1343 for (Offset k = left + 1; k < right; ++k)
1344 {
1345 for (Dimension i = 0; i < dims; ++i)
1346 {
1347 const auto val = dataset_get(obj, obj.vAcc_[k], i);
1348 if (bbox[i].low > val) bbox[i].low = val;
1349 if (bbox[i].high < val) bbox[i].high = val;
1350 }
1351 }
1352 return true;
1353 }
1354
1355 /* Determine the index, dimension and value for split plane */
1356 middleSplit_(obj, left, right - left, idx, cutfeat, cutval, bbox);
1357 node->node_type.sub.divfeat = cutfeat;
1358 return false;
1359 }
1360
1367 Derived& obj, NodePtr node, const Dimension cutfeat, const BoundingBox& left_bbox,
1368 const BoundingBox& right_bbox, BoundingBox& bbox)
1369 {
1370 node->node_type.sub.divlow = left_bbox[cutfeat].high;
1371 node->node_type.sub.divhigh = right_bbox[cutfeat].low;
1372
1373 const Dimension dims = static_cast<Dimension>(veclen(obj));
1374 for (Dimension i = 0; i < dims; ++i)
1375 {
1376 bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low);
1377 bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high);
1378 }
1379 }
1380
1381 NodePtr divideTree(Derived& obj, const Offset left, const Offset right, BoundingBox& bbox)
1382 {
1383 assert(static_cast<Size>(obj.vAcc_.at(left)) < obj.dataset_.kdtree_get_point_count());
1384
1385 NodePtr node = obj.pool_.template allocate<Node>(); // allocate memory
1386 Offset idx;
1387 Dimension cutfeat;
1388 DistanceType cutval;
1389 if (makeNode(obj, node, left, right, bbox, idx, cutfeat, cutval)) return node;
1390
1391 /* Recurse on left */
1392 BoundingBox left_bbox(bbox);
1393 left_bbox[cutfeat].high = cutval;
1394 node->child1 = this->divideTree(obj, left, left + idx, left_bbox);
1395
1396 /* Recurse on right */
1397 BoundingBox right_bbox(bbox);
1398 right_bbox[cutfeat].low = cutval;
1399 node->child2 = this->divideTree(obj, left + idx, right, right_bbox);
1400
1401 finalizeSplitNode(obj, node, cutfeat, left_bbox, right_bbox, bbox);
1402
1403 return node;
1404 }
1405
1419 Derived& obj, const Offset left, const Offset right, BoundingBox& bbox,
1420 std::atomic<unsigned int>& thread_count, std::mutex& mutex)
1421 {
1422 std::unique_lock<std::mutex> lock(mutex);
1423 NodePtr node = obj.pool_.template allocate<Node>(); // allocate memory
1424 lock.unlock();
1425
1426 Offset idx;
1427 Dimension cutfeat;
1428 DistanceType cutval;
1429 if (makeNode(obj, node, left, right, bbox, idx, cutfeat, cutval)) return node;
1430
1431 std::future<NodePtr> right_future;
1432
1433 /* Recurse on right concurrently, if possible */
1434
1435 BoundingBox right_bbox(bbox);
1436 right_bbox[cutfeat].low = cutval;
1437 if (++thread_count < n_thread_build_)
1438 {
1439 /* Concurrent thread for right recursion */
1440
1441 right_future = std::async(
1442 std::launch::async, &KDTreeBaseClass::divideTreeConcurrent, this, std::ref(obj),
1443 left + idx, right, std::ref(right_bbox), std::ref(thread_count), std::ref(mutex));
1444 }
1445 else
1446 {
1447 --thread_count;
1448 }
1449
1450 /* Recurse on left in this thread */
1451
1452 BoundingBox left_bbox(bbox);
1453 left_bbox[cutfeat].high = cutval;
1454 node->child1 =
1455 this->divideTreeConcurrent(obj, left, left + idx, left_bbox, thread_count, mutex);
1456
1457 if (right_future.valid())
1458 {
1459 /* Block and wait for concurrent right from above */
1460
1461 node->child2 = right_future.get();
1462 --thread_count;
1463 }
1464 else
1465 {
1466 /* Otherwise, recurse on right in this thread */
1467
1468 node->child2 =
1469 this->divideTreeConcurrent(obj, left + idx, right, right_bbox, thread_count, mutex);
1470 }
1471
1472 finalizeSplitNode(obj, node, cutfeat, left_bbox, right_bbox, bbox);
1473
1474 return node;
1475 }
1476
1477 void middleSplit_(
1478 const Derived& obj, const Offset ind, const Size count, Offset& index, Dimension& cutfeat,
1479 DistanceType& cutval, const BoundingBox& bbox)
1480 {
1481 const Dimension dims = static_cast<Dimension>(veclen(obj));
1482 const auto EPS = static_cast<DistanceType>(0.00001);
1483
1484 // Pre-compute max_span once
1485 ElementType max_span = bbox[0].high - bbox[0].low;
1486 for (Dimension i = 1; i < dims; ++i)
1487 {
1488 ElementType span = bbox[i].high - bbox[i].low;
1489 if (span > max_span) max_span = span;
1490 }
1491
1492 // Two-pass: first find max_span (done above), then scan candidate dims
1493 // inline — no heap allocation for a candidates vector.
1494 cutfeat = 0;
1495 ElementType max_spread = -1;
1496 ElementType min_elem = 0, max_elem = 0;
1497 const ElementType threshold = (1 - EPS) * max_span;
1498
1499 for (Dimension dim = 0; dim < dims; ++dim)
1500 {
1501 if (bbox[dim].high - bbox[dim].low < threshold) continue;
1502
1503 ElementType local_min = dataset_get(obj, vAcc_[ind], dim);
1504 ElementType local_max = local_min;
1505
1506 // Unrolled loop for better performance
1507 constexpr size_t UNROLL = 4;
1508 Offset k = 1;
1509 for (; k + UNROLL <= count; k += UNROLL)
1510 {
1511 ElementType v0 = dataset_get(obj, vAcc_[ind + k], dim);
1512 ElementType v1 = dataset_get(obj, vAcc_[ind + k + 1], dim);
1513 ElementType v2 = dataset_get(obj, vAcc_[ind + k + 2], dim);
1514 ElementType v3 = dataset_get(obj, vAcc_[ind + k + 3], dim);
1515
1516 local_min = std::min({local_min, v0, v1, v2, v3});
1517 local_max = std::max({local_max, v0, v1, v2, v3});
1518 }
1519
1520 // Handle remainder
1521 for (; k < count; ++k)
1522 {
1523 ElementType val = dataset_get(obj, vAcc_[ind + k], dim);
1524 local_min = std::min(local_min, val);
1525 local_max = std::max(local_max, val);
1526 }
1527
1528 ElementType spread = local_max - local_min;
1529 if (spread > max_spread)
1530 {
1531 cutfeat = dim;
1532 max_spread = spread;
1533 min_elem = local_min;
1534 max_elem = local_max;
1535 }
1536 }
1537
1538 // Median-of-three for better balance
1539 DistanceType split_val = (bbox[cutfeat].low + bbox[cutfeat].high) / 2;
1540 if (split_val < min_elem) split_val = min_elem;
1541 if (split_val > max_elem) split_val = max_elem;
1542
1543 cutval = split_val;
1544
1545 // Optimized partitioning
1546 Offset lim1, lim2;
1547 planeSplit(obj, ind, count, cutfeat, cutval, lim1, lim2);
1548
1549 index = (lim1 > count / 2) ? lim1 : (lim2 < count / 2) ? lim2 : count / 2;
1550 }
1551
1562 const Derived& obj, const Offset ind, const Size count, const Dimension cutfeat,
1563 const DistanceType& cutval, Offset& lim1, Offset& lim2)
1564 {
1565 // Dutch National Flag algorithm for three-way partitioning
1566 Offset left = 0;
1567 Offset mid = 0;
1568 Offset right = count - 1;
1569
1570 while (mid <= right)
1571 {
1572 ElementType val = dataset_get(obj, vAcc_[ind + mid], cutfeat);
1573
1574 if (val < cutval)
1575 {
1576 std::swap(vAcc_[ind + left], vAcc_[ind + mid]);
1577 left++;
1578 mid++;
1579 }
1580 else if (val > cutval)
1581 {
1582 std::swap(vAcc_[ind + mid], vAcc_[ind + right]);
1583 right--;
1584 }
1585 else
1586 {
1587 mid++;
1588 }
1589 }
1590
1591 lim1 = left;
1592 lim2 = mid;
1593 }
1594
1595 DistanceType computeInitialDistances(
1596 const Derived& obj, const ElementType* vec, distance_vector_t& dists) const
1597 {
1598 assert(vec);
1599 DistanceType dist = DistanceType();
1600
1601 const Dimension dims = static_cast<Dimension>(veclen(obj));
1602 for (Dimension i = 0; i < dims; ++i)
1603 {
1604 if (vec[i] < obj.root_bbox_[i].low)
1605 {
1606 dists[i] = obj.distance_.accum_dist(vec[i], obj.root_bbox_[i].low, i);
1607 dist += dists[i];
1608 }
1609 else if (vec[i] > obj.root_bbox_[i].high)
1610 {
1611 dists[i] = obj.distance_.accum_dist(vec[i], obj.root_bbox_[i].high, i);
1612 dist += dists[i];
1613 }
1614 }
1615 return dist;
1616 }
1617
1618 static void save_tree(const Derived& obj, std::ostream& stream, const NodeConstPtr tree)
1619 {
1620 save_value(stream, *tree);
1621 if (tree->child1 != nullptr)
1622 {
1623 save_tree(obj, stream, tree->child1);
1624 }
1625 if (tree->child2 != nullptr)
1626 {
1627 save_tree(obj, stream, tree->child2);
1628 }
1629 }
1630
1631 static void load_tree(Derived& obj, std::istream& stream, NodePtr& tree)
1632 {
1633 tree = obj.pool_.template allocate<Node>();
1634 load_value(stream, *tree);
1635 if (tree->child1 != nullptr)
1636 {
1637 load_tree(obj, stream, tree->child1);
1638 }
1639 if (tree->child2 != nullptr)
1640 {
1641 load_tree(obj, stream, tree->child2);
1642 }
1643 }
1644
1647 static constexpr uint32_t SAVE_MAGIC = 0x4E464C4E;
1648
1667 void saveIndex(const Derived& obj, std::ostream& stream) const
1668 {
1669 // 10-byte header: magic | version | sizeof_size_t | sizeof_IndexType
1670 // | sizeof_ElementType | sizeof_DistanceType
1671 // Use local copies: passing a static constexpr by const-ref ODR-uses it
1672 // in C++11/14, which requires an out-of-class definition we cannot provide
1673 // in a header-only library.
1674 const uint32_t hdr_magic = SAVE_MAGIC;
1675 const uint32_t hdr_version = static_cast<uint32_t>(NANOFLANN_VERSION);
1676 const uint8_t hdr_sz_st = static_cast<uint8_t>(sizeof(size_t));
1677 const uint8_t hdr_sz_idx = static_cast<uint8_t>(sizeof(IndexType));
1678 const uint8_t hdr_sz_elem = static_cast<uint8_t>(sizeof(ElementType));
1679 const uint8_t hdr_sz_dist = static_cast<uint8_t>(sizeof(DistanceType));
1680 save_value(stream, hdr_magic);
1681 save_value(stream, hdr_version);
1682 save_value(stream, hdr_sz_st);
1683 save_value(stream, hdr_sz_idx);
1684 save_value(stream, hdr_sz_elem);
1685 save_value(stream, hdr_sz_dist);
1686
1687 save_value(stream, obj.size_);
1688 save_value(stream, obj.dim_);
1689 save_value(stream, obj.root_bbox_);
1690 save_value(stream, obj.leaf_max_size_);
1691 save_value(stream, obj.vAcc_);
1692 if (obj.root_node_)
1693 {
1694 save_tree(obj, stream, obj.root_node_);
1695 }
1696 }
1697
1713 void loadIndex(Derived& obj, std::istream& stream)
1714 {
1715 // Validate header
1716 uint32_t magic = 0;
1717 load_value(stream, magic);
1718 if (stream.fail() || magic != SAVE_MAGIC)
1719 {
1720 throw std::runtime_error(
1721 "nanoflann loadIndex: invalid file (wrong magic number). "
1722 "The stream was not written by nanoflann saveIndex().");
1723 }
1724
1725 uint32_t file_version = 0;
1726 load_value(stream, file_version);
1727 if (file_version != static_cast<uint32_t>(NANOFLANN_VERSION))
1728 {
1729 char msg[200];
1730 snprintf(
1731 msg, sizeof(msg),
1732 "nanoflann loadIndex: version mismatch "
1733 "(file=0x%03X, library=0x%03X). Rebuild the index.",
1734 file_version, static_cast<unsigned>(NANOFLANN_VERSION));
1735 throw std::runtime_error(msg);
1736 }
1737
1738 uint8_t sz_size_t = 0;
1739 uint8_t sz_idx = 0;
1740 uint8_t sz_elem = 0;
1741 uint8_t sz_dist = 0;
1742 load_value(stream, sz_size_t);
1743 load_value(stream, sz_idx);
1744 load_value(stream, sz_elem);
1745 load_value(stream, sz_dist);
1746 if (sz_size_t != static_cast<uint8_t>(sizeof(size_t)) ||
1747 sz_idx != static_cast<uint8_t>(sizeof(IndexType)) ||
1748 sz_elem != static_cast<uint8_t>(sizeof(ElementType)) ||
1749 sz_dist != static_cast<uint8_t>(sizeof(DistanceType)))
1750 {
1751 throw std::runtime_error(
1752 "nanoflann loadIndex: type-size mismatch between saved index and "
1753 "current template instantiation (sizeof size_t / IndexType / "
1754 "ElementType / DistanceType differ). Rebuild the index.");
1755 }
1756
1757 load_value(stream, obj.size_);
1758 load_value(stream, obj.dim_);
1759 load_value(stream, obj.root_bbox_);
1760 load_value(stream, obj.leaf_max_size_);
1761 load_value(stream, obj.vAcc_);
1762
1763 if (obj.size_ > 0)
1764 {
1765 load_tree(obj, stream, obj.root_node_);
1766 }
1767
1768 if (stream.fail())
1769 {
1770 throw std::runtime_error(
1771 "nanoflann loadIndex: unexpected end of stream or read error.");
1772 }
1773 }
1774};
1775
1778
1829template <typename Distance, class DatasetAdaptor, int32_t DIM = -1, typename index_t = uint32_t>
1831 : public KDTreeBaseClass<
1832 KDTreeSingleIndexAdaptor<Distance, DatasetAdaptor, DIM, index_t>, Distance,
1833 DatasetAdaptor, DIM, index_t>
1834{
1835 public:
1839
1841 const DatasetAdaptor& dataset_;
1842
1843 const KDTreeSingleIndexAdaptorParams indexParams;
1844
1845 Distance distance_;
1846
1847 using Base = typename nanoflann::KDTreeBaseClass<
1849 DatasetAdaptor, DIM, index_t>;
1850
1851 using Offset = typename Base::Offset;
1852 using Size = typename Base::Size;
1853 using Dimension = typename Base::Dimension;
1854
1855 using ElementType = typename Base::ElementType;
1856 using DistanceType = typename Base::DistanceType;
1857 using IndexType = typename Base::IndexType;
1858
1859 using Node = typename Base::Node;
1860 using NodePtr = Node*;
1861
1862 using Interval = typename Base::Interval;
1863
1866 using BoundingBox = typename Base::BoundingBox;
1867
1870 using distance_vector_t = typename Base::distance_vector_t;
1871
1892 template <class... Args>
1894 const Dimension dimensionality, const DatasetAdaptor& inputData,
1895 const KDTreeSingleIndexAdaptorParams& params, Args&&... args)
1896 : dataset_(inputData),
1897 indexParams(params),
1898 distance_(inputData, std::forward<Args>(args)...)
1899 {
1900 init(dimensionality, params);
1901 }
1902
1903 explicit KDTreeSingleIndexAdaptor(
1904 const Dimension dimensionality, const DatasetAdaptor& inputData,
1905 const KDTreeSingleIndexAdaptorParams& params = {})
1906 : dataset_(inputData), indexParams(params), distance_(inputData)
1907 {
1908 init(dimensionality, params);
1909 }
1910
1911 private:
1912 void init(const Dimension dimensionality, const KDTreeSingleIndexAdaptorParams& params)
1913 {
1914 Base::size_ = dataset_.kdtree_get_point_count();
1915 Base::size_at_index_build_ = Base::size_;
1916 Base::dim_ = dimensionality;
1917 if (DIM > 0) Base::dim_ = DIM;
1918 Base::leaf_max_size_ = params.leaf_max_size;
1919 if (params.n_thread_build > 0)
1920 {
1921 Base::n_thread_build_ = params.n_thread_build;
1922 }
1923 else
1924 {
1925 Base::n_thread_build_ = std::max(std::thread::hardware_concurrency(), 1u);
1926 }
1927
1928 if (!has_flag(params.flags, KDTreeSingleIndexAdaptorFlags::SkipInitialBuildIndex))
1929 {
1930 // Build KD-tree:
1931 buildIndex();
1932 }
1933 }
1934
1935 public:
1940 {
1941 Base::size_ = dataset_.kdtree_get_point_count();
1942 Base::size_at_index_build_ = Base::size_;
1943 init_vind();
1944 this->freeIndex(*this);
1945 Base::size_at_index_build_ = Base::size_;
1946 if (Base::size_ == 0) return;
1947 this->computeBoundingBox(Base::root_bbox_);
1948 // construct the tree
1949 if (Base::n_thread_build_ == 1)
1950 {
1951 Base::root_node_ = this->divideTree(*this, 0, Base::size_, Base::root_bbox_);
1952 }
1953 else
1954 {
1955#ifndef NANOFLANN_NO_THREADS
1956 std::atomic<unsigned int> thread_count(0u);
1957 std::mutex mutex;
1958 Base::root_node_ = this->divideTreeConcurrent(
1959 *this, 0, Base::size_, Base::root_bbox_, thread_count, mutex);
1960#else /* NANOFLANN_NO_THREADS */
1961 throw std::runtime_error("Multithreading is disabled");
1962#endif /* NANOFLANN_NO_THREADS */
1963 }
1964 }
1965
1968
1985 template <typename RESULTSET>
1987 RESULTSET& result, const ElementType* vec, const SearchParameters& searchParams = {}) const
1988 {
1989 assert(vec);
1990 if (this->size(*this) == 0) return false;
1991 if (!Base::root_node_)
1992 throw std::runtime_error(
1993 "[nanoflann] findNeighbors() called before building the "
1994 "index.");
1995 DistanceType epsError = 1 + static_cast<DistanceType>(searchParams.eps);
1996
1997 // fixed or variable-sized container (depending on DIM)
1998 distance_vector_t dists;
1999 // Fill it with zeros.
2000 auto zero = static_cast<typename RESULTSET::DistanceType>(0);
2001 assign(dists, this->veclen(*this), zero);
2002 DistanceType dist = this->computeInitialDistances(*this, vec, dists);
2003 this->searchLevel(result, vec, Base::root_node_, dist, dists, epsError);
2004
2005 if (searchParams.sorted) result.sort();
2006
2007 return result.full();
2008 }
2009
2025 template <typename RESULTSET>
2026 NANOFLANN_NODISCARD Size findWithinBox(RESULTSET& result, const BoundingBox& bbox) const
2027 {
2028 if (this->size(*this) == 0) return 0;
2029 if (!Base::root_node_)
2030 throw std::runtime_error(
2031 "[nanoflann] findWithinBox() called before building the "
2032 "index.");
2033
2034 std::stack<NodePtr> stack;
2035 stack.push(Base::root_node_);
2036
2037 while (!stack.empty())
2038 {
2039 const NodePtr node = stack.top();
2040 stack.pop();
2041
2042 // If this is a leaf node, then do check and return.
2043 if (!node->child1) // (if one node is nullptr, both are)
2044 {
2045 for (Offset i = node->node_type.lr.left; i < node->node_type.lr.right; ++i)
2046 {
2047 if (contains(bbox, Base::vAcc_[i]))
2048 {
2049 if (!result.addPoint(0, Base::vAcc_[i]))
2050 {
2051 // the resultset doesn't want to receive any more
2052 // points, we're done searching!
2053 return result.size();
2054 }
2055 }
2056 }
2057 }
2058 else
2059 {
2060 const Dimension idx = node->node_type.sub.divfeat;
2061 const auto low_bound = node->node_type.sub.divlow;
2062 const auto high_bound = node->node_type.sub.divhigh;
2063
2064 if (bbox[idx].low <= low_bound) stack.push(node->child1);
2065 if (bbox[idx].high >= high_bound) stack.push(node->child2);
2066 }
2067 }
2068
2069 return result.size();
2070 }
2071
2087 NANOFLANN_NODISCARD Size knnSearch(
2088 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
2089 DistanceType* out_distances) const
2090 {
2092 resultSet.init(out_indices, out_distances);
2093 findNeighbors(resultSet, query_point);
2094 return resultSet.size();
2095 }
2096
2116 NANOFLANN_NODISCARD Size radiusSearch(
2117 const ElementType* query_point, const DistanceType& radius,
2118 std::vector<ResultItem<IndexType, DistanceType>>& IndicesDists,
2119 const SearchParameters& searchParams = {}) const
2120 {
2121 RadiusResultSet<DistanceType, IndexType> resultSet(radius, IndicesDists);
2122 const Size nFound = radiusSearchCustomCallback(query_point, resultSet, searchParams);
2123 return nFound;
2124 }
2125
2131 template <class SEARCH_CALLBACK>
2132 NANOFLANN_NODISCARD Size radiusSearchCustomCallback(
2133 const ElementType* query_point, SEARCH_CALLBACK& resultSet,
2134 const SearchParameters& searchParams = {}) const
2135 {
2136 findNeighbors(resultSet, query_point, searchParams);
2137 return resultSet.size();
2138 }
2139
2155 NANOFLANN_NODISCARD Size rknnSearch(
2156 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
2157 DistanceType* out_distances, const DistanceType& radius) const
2158 {
2159 nanoflann::RKNNResultSet<DistanceType, IndexType> resultSet(num_closest, radius);
2160 resultSet.init(out_indices, out_distances);
2161 findNeighbors(resultSet, query_point);
2162 return resultSet.size();
2163 }
2164
2166
2167 public:
2171 {
2172 // Create a permutable array of indices to the input vectors.
2173 Base::size_ = dataset_.kdtree_get_point_count();
2174 if (Base::vAcc_.size() != Base::size_) Base::vAcc_.resize(Base::size_);
2175 for (IndexType i = 0; i < static_cast<IndexType>(Base::size_); i++) Base::vAcc_[i] = i;
2176 }
2177
2178 bool contains(const BoundingBox& bbox, IndexType idx) const
2179 {
2180 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
2181 for (Dimension i = 0; i < dims; ++i)
2182 {
2183 const auto point = this->dataset_.kdtree_get_pt(idx, i);
2184 if (point < bbox[i].low || point > bbox[i].high) return false;
2185 }
2186 return true;
2187 }
2188
2189 public:
2195 void saveIndex(std::ostream& stream) const { Base::saveIndex(*this, stream); }
2196
2202 void loadIndex(std::istream& stream) { Base::loadIndex(*this, stream); }
2203
2204}; // class KDTree
2205
2243template <typename Distance, class DatasetAdaptor, int32_t DIM = -1, typename IndexType = uint32_t>
2245 : public KDTreeBaseClass<
2246 KDTreeSingleIndexDynamicAdaptor_<Distance, DatasetAdaptor, DIM, IndexType>, Distance,
2247 DatasetAdaptor, DIM, IndexType>
2248{
2249 public:
2253 const DatasetAdaptor& dataset_;
2254
2255 KDTreeSingleIndexAdaptorParams index_params_;
2256
2257 std::vector<int>& treeIndex_;
2258
2259 Distance distance_;
2260
2261 using Base = typename nanoflann::KDTreeBaseClass<
2263 Distance, DatasetAdaptor, DIM, IndexType>;
2264
2265 using ElementType = typename Base::ElementType;
2266 using DistanceType = typename Base::DistanceType;
2267
2268 using Offset = typename Base::Offset;
2269 using Size = typename Base::Size;
2270 using Dimension = typename Base::Dimension;
2271
2272 using Node = typename Base::Node;
2273 using NodePtr = Node*;
2274
2275 using Interval = typename Base::Interval;
2278 using BoundingBox = typename Base::BoundingBox;
2279
2282 using distance_vector_t = typename Base::distance_vector_t;
2283
2285 NANOFLANN_NODISCARD bool isActive(IndexType idx) const { return treeIndex_[idx] != -1; }
2286
2303 const Dimension dimensionality, const DatasetAdaptor& inputData,
2304 std::vector<int>& treeIndex,
2306 : dataset_(inputData), index_params_(params), treeIndex_(treeIndex), distance_(inputData)
2307 {
2308 Base::size_ = 0;
2309 Base::size_at_index_build_ = 0;
2310 for (auto& v : Base::root_bbox_) v = {};
2311 Base::dim_ = dimensionality;
2312 if (DIM > 0) Base::dim_ = DIM;
2313 Base::leaf_max_size_ = params.leaf_max_size;
2314 if (params.n_thread_build > 0)
2315 {
2316 Base::n_thread_build_ = params.n_thread_build;
2317 }
2318 else
2319 {
2320 Base::n_thread_build_ = std::max(std::thread::hardware_concurrency(), 1u);
2321 }
2322 }
2323
2326
2329 {
2330 if (this == &rhs) return *this;
2332 std::swap(Base::vAcc_, tmp.Base::vAcc_);
2333 std::swap(Base::leaf_max_size_, tmp.Base::leaf_max_size_);
2334 std::swap(index_params_, tmp.index_params_);
2335 // treeIndex_ is a reference member and cannot be rebound; do not swap.
2336 std::swap(Base::size_, tmp.Base::size_);
2337 std::swap(Base::size_at_index_build_, tmp.Base::size_at_index_build_);
2338 std::swap(Base::root_node_, tmp.Base::root_node_);
2339 std::swap(Base::root_bbox_, tmp.Base::root_bbox_);
2340 std::swap(Base::pool_, tmp.Base::pool_);
2341 return *this;
2342 }
2343
2348 {
2349 Base::size_ = Base::vAcc_.size();
2350 this->freeIndex(*this);
2351 Base::size_at_index_build_ = Base::size_;
2352 if (Base::size_ == 0) return;
2353 this->computeBoundingBox(Base::root_bbox_);
2354 // construct the tree
2355 if (Base::n_thread_build_ == 1)
2356 {
2357 Base::root_node_ = this->divideTree(*this, 0, Base::size_, Base::root_bbox_);
2358 }
2359 else
2360 {
2361#ifndef NANOFLANN_NO_THREADS
2362 std::atomic<unsigned int> thread_count(0u);
2363 std::mutex mutex;
2364 Base::root_node_ = this->divideTreeConcurrent(
2365 *this, 0, Base::size_, Base::root_bbox_, thread_count, mutex);
2366#else /* NANOFLANN_NO_THREADS */
2367 throw std::runtime_error("Multithreading is disabled");
2368#endif /* NANOFLANN_NO_THREADS */
2369 }
2370 }
2371
2374
2395 template <typename RESULTSET>
2397 RESULTSET& result, const ElementType* vec, const SearchParameters& searchParams = {}) const
2398 {
2399 assert(vec);
2400 if (this->size(*this) == 0) return false;
2401 if (!Base::root_node_) return false;
2402 DistanceType epsError = 1 + static_cast<DistanceType>(searchParams.eps);
2403
2404 // fixed or variable-sized container (depending on DIM)
2405 distance_vector_t dists;
2406 // Fill it with zeros.
2407 assign(dists, this->veclen(*this), static_cast<typename distance_vector_t::value_type>(0));
2408 DistanceType dist = this->computeInitialDistances(*this, vec, dists);
2409 this->searchLevel(result, vec, Base::root_node_, dist, dists, epsError);
2410
2411 if (searchParams.sorted) result.sort();
2412
2413 return result.full();
2414 }
2415
2430 NANOFLANN_NODISCARD Size knnSearch(
2431 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
2432 DistanceType* out_distances, const SearchParameters& searchParams = {}) const
2433 {
2435 resultSet.init(out_indices, out_distances);
2436 findNeighbors(resultSet, query_point, searchParams);
2437 return resultSet.size();
2438 }
2439
2459 NANOFLANN_NODISCARD Size radiusSearch(
2460 const ElementType* query_point, const DistanceType& radius,
2461 std::vector<ResultItem<IndexType, DistanceType>>& IndicesDists,
2462 const SearchParameters& searchParams = {}) const
2463 {
2464 RadiusResultSet<DistanceType, IndexType> resultSet(radius, IndicesDists);
2465 const Size nFound = radiusSearchCustomCallback(query_point, resultSet, searchParams);
2466 return nFound;
2467 }
2468
2474 template <class SEARCH_CALLBACK>
2475 NANOFLANN_NODISCARD Size radiusSearchCustomCallback(
2476 const ElementType* query_point, SEARCH_CALLBACK& resultSet,
2477 const SearchParameters& searchParams = {}) const
2478 {
2479 findNeighbors(resultSet, query_point, searchParams);
2480 return resultSet.size();
2481 }
2482
2484
2485 public:
2486 public:
2492 void saveIndex(std::ostream& stream) { Base::saveIndex(*this, stream); }
2493
2499 void loadIndex(std::istream& stream) { Base::loadIndex(*this, stream); }
2500};
2501
2516template <typename Distance, class DatasetAdaptor, int32_t DIM = -1, typename IndexType = uint32_t>
2518{
2519 public:
2520 using ElementType = typename Distance::ElementType;
2521 using DistanceType = typename Distance::DistanceType;
2522
2523 using Offset = typename KDTreeSingleIndexDynamicAdaptor_<Distance, DatasetAdaptor, DIM>::Offset;
2524 using Size = typename KDTreeSingleIndexDynamicAdaptor_<Distance, DatasetAdaptor, DIM>::Size;
2525 using Dimension =
2526 typename KDTreeSingleIndexDynamicAdaptor_<Distance, DatasetAdaptor, DIM>::Dimension;
2527
2528 protected:
2529 Size leaf_max_size_;
2530 Size treeCount_;
2531 Size pointCount_;
2532
2536 const DatasetAdaptor& dataset_;
2537
2540 std::vector<int> treeIndex_;
2544 std::unordered_map<IndexType, int> removedPoints_;
2545
2546 KDTreeSingleIndexAdaptorParams index_params_;
2547
2548 Dimension dim_;
2549
2550 using index_container_t =
2552 std::vector<index_container_t> index_;
2553
2554 public:
2557 const std::vector<index_container_t>& getAllIndices() const { return index_; }
2558
2559 private:
2561 int First0Bit(Size num)
2562 {
2563 int pos = 0;
2564 while (num & 1)
2565 {
2566 num = num >> 1;
2567 pos++;
2568 }
2569 return pos;
2570 }
2571
2573 void init()
2574 {
2575 using my_kd_tree_t =
2576 KDTreeSingleIndexDynamicAdaptor_<Distance, DatasetAdaptor, DIM, IndexType>;
2577 std::vector<my_kd_tree_t> index(
2578 treeCount_, my_kd_tree_t(dim_ /*dim*/, dataset_, treeIndex_, index_params_));
2579 index_ = index;
2580 }
2581
2582 public:
2583 Distance distance_;
2584
2601 const int dimensionality, const DatasetAdaptor& inputData,
2603 const size_t maximumPointCount = 1000000000U)
2604 : dataset_(inputData), index_params_(params), distance_(inputData)
2605 {
2606 treeCount_ = static_cast<size_t>(std::log2(maximumPointCount)) + 1;
2607 pointCount_ = 0U;
2608 dim_ = dimensionality;
2609 treeIndex_.clear();
2610 if (DIM > 0) dim_ = DIM;
2611 leaf_max_size_ = params.leaf_max_size;
2612 init();
2613 const size_t num_initial_points = dataset_.kdtree_get_point_count();
2614 if (num_initial_points > 0)
2615 {
2616 addPoints(0, static_cast<IndexType>(num_initial_points - 1));
2617 }
2618 }
2619
2623
2625 void addPoints(IndexType start, IndexType end)
2626 {
2627 int maxIndex = 0;
2628 for (IndexType idx = start; idx <= end; idx++)
2629 {
2630 // If this index was previously removed, its point is still
2631 // physically present in its sub-tree (removal is lazy and never
2632 // deletes from vAcc_). Just clear the "removed" mark and restore its
2633 // tree index. Re-inserting it would create a duplicate entry that
2634 // grows the trees without bound and yields duplicate search results.
2635 const auto it = removedPoints_.find(idx);
2636 if (it != removedPoints_.end())
2637 {
2638 treeIndex_[idx] = it->second;
2639 removedPoints_.erase(it);
2640 continue;
2641 }
2642
2643 const int pos = First0Bit(pointCount_);
2644 maxIndex = std::max(pos, maxIndex);
2645 if (treeIndex_.size() <= static_cast<size_t>(pointCount_))
2646 treeIndex_.resize(static_cast<size_t>(pointCount_) + 1);
2647 treeIndex_[pointCount_] = pos;
2648
2649 for (int i = 0; i < pos; i++)
2650 {
2651 for (size_t j = 0; j < index_[i].vAcc_.size(); j++)
2652 {
2653 const IndexType e = index_[i].vAcc_[j];
2654 index_[pos].vAcc_.push_back(e);
2655 if (treeIndex_[e] != -1)
2656 treeIndex_[e] = pos;
2657 else
2658 removedPoints_[e] = pos; // keep tombstone's tree index current
2659 }
2660 index_[i].vAcc_.clear();
2661 }
2662 index_[pos].vAcc_.push_back(idx);
2663 pointCount_++;
2664 }
2665
2666 for (int i = 0; i <= maxIndex; ++i)
2667 {
2668 index_[i].freeIndex(index_[i]);
2669 if (!index_[i].vAcc_.empty()) index_[i].buildIndex();
2670 }
2671 }
2672
2674 void removePoint(size_t idx)
2675 {
2676 if (idx >= pointCount_) return;
2677 if (treeIndex_[idx] == -1) return; // already removed
2678 // Remember which sub-tree still physically holds this point, so it can
2679 // be reactivated in place if re-added later (see addPoints).
2680 removedPoints_[static_cast<IndexType>(idx)] = treeIndex_[idx];
2681 treeIndex_[idx] = -1;
2682 }
2683
2700 template <typename RESULTSET>
2702 RESULTSET& result, const ElementType* vec, const SearchParameters& searchParams = {}) const
2703 {
2704 for (size_t i = 0; i < treeCount_; i++)
2705 {
2706 index_[i].findNeighbors(result, &vec[0], searchParams);
2707 }
2708 return result.full();
2709 }
2710};
2711
2724struct KDTreeIncrementalIndexParams
2725{
2726 KDTreeIncrementalIndexParams(float alpha_balance_ = 0.75f, float alpha_deleted_ = 0.5f)
2727 : alpha_balance(alpha_balance_), alpha_deleted(alpha_deleted_)
2728 {
2729 }
2730
2731 float alpha_balance;
2732 float alpha_deleted;
2733};
2734
2769template <typename Distance, class DatasetAdaptor, int32_t DIM = -1, typename IndexType = uint32_t>
2771 : public KDTreeBaseClass<
2772 KDTreeSingleIndexIncrementalAdaptor<Distance, DatasetAdaptor, DIM, IndexType>, Distance,
2773 DatasetAdaptor, DIM, IndexType>
2774{
2775 public:
2776 using Base = typename nanoflann::KDTreeBaseClass<
2778 DatasetAdaptor, DIM, IndexType>;
2779
2780 using ElementType = typename Base::ElementType;
2781 using DistanceType = typename Base::DistanceType;
2782
2783 using Offset = typename Base::Offset;
2784 using Size = typename Base::Size;
2785 using Dimension = typename Base::Dimension;
2786
2787 using Interval = typename Base::Interval;
2788 using BoundingBox = typename Base::BoundingBox;
2789 using distance_vector_t = typename Base::distance_vector_t;
2790
2792 const DatasetAdaptor& dataset_;
2793
2794 Distance distance_;
2795
2798 struct INode
2799 {
2800 IndexType ptIdx = 0;
2801 Dimension divfeat = 0;
2802 bool deleted = false;
2803 bool treeDeleted = false;
2804 INode* child1 = nullptr;
2805 INode* child2 = nullptr;
2806 INode* parent = nullptr;
2807 Size subtree_size = 0;
2808 Size invalid_count = 0;
2809 BoundingBox box;
2814 typename array_or_vector<DIM, ElementType>::type pcoord;
2815 };
2816
2821#if defined(NANOFLANN_INCREMENTAL_NO_COORD_CACHE)
2822 static constexpr bool kCacheCoords = false;
2823#else
2824 static constexpr bool kCacheCoords = (DIM > 0);
2825#endif
2826
2827 private:
2828 INode* iroot_ = nullptr;
2829 INode* freeList_ = nullptr;
2830
2831 Size liveCount_ = 0;
2832 Size totalCount_ = 0;
2833
2834 float alphaBal_ = 0.75f;
2835 float alphaDel_ = 0.5f;
2837 static constexpr Size kMinBalanceRebuild = 4;
2840 static constexpr double kBulkInsertFraction = 0.5;
2841
2843 INode* pendingRebuild_ = nullptr;
2844
2849 bool inlineRebuild_ = true;
2850
2852 std::vector<INode*> nodeOfPoint_;
2853
2855 std::vector<IndexType> buildBuf_;
2856
2858 bool collectRemoved_ = false;
2859 std::vector<IndexType> removedSink_;
2860
2861 public:
2871 const Dimension dimensionality, const DatasetAdaptor& inputData,
2872 const KDTreeIncrementalIndexParams& params = {})
2873 : dataset_(inputData), distance_(inputData)
2874 {
2875 Base::dim_ = dimensionality;
2876 if (DIM > 0) Base::dim_ = DIM;
2877 alphaBal_ = params.alpha_balance;
2878 alphaDel_ = params.alpha_deleted;
2879 resize(Base::root_bbox_, static_cast<Dimension>(this->veclen(*this)));
2880 }
2881
2885 delete;
2886
2887 ~KDTreeSingleIndexIncrementalAdaptor() { destroyNodeObjects(); }
2888
2891
2893 void addPoint(IndexType idx)
2894 {
2895 ensureNodeMap(idx);
2896 insertOne(idx);
2897 syncRootBox();
2898 }
2899
2908 void addPoints(IndexType start, IndexType end)
2909 {
2910 if (end < start) return;
2911 ensureNodeMap(end);
2912 const Size batch = static_cast<Size>(end - start) + 1;
2913 if (!iroot_ ||
2914 static_cast<double>(batch) >= kBulkInsertFraction * static_cast<double>(liveCount_))
2915 {
2916 buildBuf_.clear();
2917 if (iroot_) collectLiveAndFree(iroot_, buildBuf_); // keep existing live points
2918 for (IndexType idx = start; idx <= end; ++idx) buildBuf_.push_back(idx);
2919 iroot_ = buildBalanced(buildBuf_, 0, buildBuf_.size(), 0, nullptr);
2920 liveCount_ = buildBuf_.size();
2921 totalCount_ = liveCount_;
2922 }
2923 else
2924 {
2925 for (IndexType idx = start; idx <= end; ++idx) insertOne(idx);
2926 }
2927 syncRootBox();
2928 }
2929
2931 void removePoint(IndexType idx)
2932 {
2933 if (idx >= nodeOfPoint_.size()) return;
2934 INode* n = nodeOfPoint_[idx];
2935 if (!n || n->deleted) return;
2936 // A point can also be logically dead via a treeDeleted ancestor (a
2937 // lazily-killed box region). Detect that and treat it as already gone.
2938 for (INode* p = n->parent; p; p = p->parent)
2939 if (p->treeDeleted) return;
2940
2941 n->deleted = true;
2942 for (INode* p = n; p; p = p->parent) ++p->invalid_count;
2943 --liveCount_;
2944 maybeRebuildForDeletion();
2945 syncRootBox();
2946 }
2947
2949 void removeBox(const BoundingBox& box)
2950 {
2951 if (iroot_) removeBoxRec(iroot_, box);
2952 maybeRebuildForDeletion();
2953 syncRootBox();
2954 }
2955
2958 void removeOutsideBox(const BoundingBox& keep)
2959 {
2960 if (iroot_) removeOutsideBoxRec(iroot_, keep);
2961 maybeRebuildForDeletion();
2962 syncRootBox();
2963 }
2964
2967 void setCollectRemovedPoints(bool enable)
2968 {
2969 collectRemoved_ = enable;
2970 if (!enable) std::vector<IndexType>().swap(removedSink_);
2971 }
2972
2975 std::vector<IndexType> acquireRemovedPoints()
2976 {
2977 std::vector<IndexType> out;
2978 out.swap(removedSink_);
2979 return out;
2980 }
2981
2985 void setInlineRebuild(bool enable) { inlineRebuild_ = enable; }
2986
2989 void snapshotLiveIndices(std::vector<IndexType>& out) const { snapshotRec(iroot_, out); }
2990
2994 void collectPhysicalIndices(std::vector<IndexType>& out) const { collectAllRec(iroot_, out); }
2995
2998 NANOFLANN_NODISCARD bool referencesIndex(IndexType idx) const
2999 {
3000 return idx < nodeOfPoint_.size() && nodeOfPoint_[idx] != nullptr;
3001 }
3002
3005 void buildFromIndices(const std::vector<IndexType>& idxs)
3006 {
3007 if (iroot_)
3008 {
3009 buildBuf_.clear();
3010 collectLiveAndFree(iroot_, buildBuf_); // recycle existing nodes
3011 iroot_ = nullptr;
3012 }
3013 IndexType maxIdx = 0;
3014 for (IndexType v : idxs) maxIdx = std::max(maxIdx, v);
3015 if (!idxs.empty()) ensureNodeMap(maxIdx);
3016 buildBuf_.assign(idxs.begin(), idxs.end());
3017 iroot_ = buildBalanced(buildBuf_, 0, buildBuf_.size(), 0, nullptr);
3018 liveCount_ = buildBuf_.size();
3019 totalCount_ = liveCount_;
3020 syncRootBox();
3021 }
3022
3024
3027
3029 NANOFLANN_NODISCARD Size size() const noexcept { return liveCount_; }
3030 NANOFLANN_NODISCARD bool empty() const noexcept { return liveCount_ == 0; }
3031
3033 NANOFLANN_NODISCARD Size physicalSize() const noexcept { return totalCount_; }
3034
3036 NANOFLANN_NODISCARD Size usedMemory() const
3037 {
3038 return Base::pool_.usedMemory + Base::pool_.wastedMemory +
3039 nodeOfPoint_.capacity() * sizeof(INode*);
3040 }
3041
3045 NANOFLANN_NODISCARD BoundingBox boundingBox() const { return Base::root_bbox_; }
3046
3049 void reserve(Size n)
3050 {
3051 nodeOfPoint_.reserve(n);
3052 buildBuf_.reserve(n);
3053 }
3054
3056
3059
3061 template <typename RESULTSET>
3063 RESULTSET& result, const ElementType* vec, const SearchParameters& searchParams = {}) const
3064 {
3065 assert(vec);
3066 if (!iroot_ || liveCount_ == 0) return false;
3067 const DistanceType epsError = 1 + static_cast<DistanceType>(searchParams.eps);
3068
3069 distance_vector_t dists;
3070 assign(dists, this->veclen(*this), static_cast<typename distance_vector_t::value_type>(0));
3071 const DistanceType dist = this->computeInitialDistances(*this, vec, dists);
3072 searchLevelInc(result, vec, iroot_, dist, dists, epsError, this->veclen(*this));
3073 if (searchParams.sorted) result.sort();
3074 return result.full();
3075 }
3076
3078 NANOFLANN_NODISCARD Size knnSearch(
3079 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
3080 DistanceType* out_distances, const SearchParameters& searchParams = {}) const
3081 {
3083 resultSet.init(out_indices, out_distances);
3084 findNeighbors(resultSet, query_point, searchParams);
3085 return resultSet.size();
3086 }
3087
3089 NANOFLANN_NODISCARD Size radiusSearch(
3090 const ElementType* query_point, const DistanceType& radius,
3091 std::vector<ResultItem<IndexType, DistanceType>>& IndicesDists,
3092 const SearchParameters& searchParams = {}) const
3093 {
3094 RadiusResultSet<DistanceType, IndexType> resultSet(radius, IndicesDists);
3095 findNeighbors(resultSet, query_point, searchParams);
3096 return resultSet.size();
3097 }
3098
3100 template <class SEARCH_CALLBACK>
3101 NANOFLANN_NODISCARD Size radiusSearchCustomCallback(
3102 const ElementType* query_point, SEARCH_CALLBACK& resultSet,
3103 const SearchParameters& searchParams = {}) const
3104 {
3105 findNeighbors(resultSet, query_point, searchParams);
3106 return resultSet.size();
3107 }
3108
3110 NANOFLANN_NODISCARD Size rknnSearch(
3111 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
3112 DistanceType* out_distances, const DistanceType& radius) const
3113 {
3114 nanoflann::RKNNResultSet<DistanceType, IndexType> resultSet(num_closest, radius);
3115 resultSet.init(out_indices, out_distances);
3116 findNeighbors(resultSet, query_point);
3117 return resultSet.size();
3118 }
3119
3121 template <typename RESULTSET>
3122 NANOFLANN_NODISCARD Size findWithinBox(RESULTSET& result, const BoundingBox& bbox) const
3123 {
3124 if (iroot_) findWithinBoxRec(result, iroot_, bbox);
3125 return result.size();
3126 }
3127
3129
3130 private:
3131 // --------------------------------------------------------------------
3132 // Node allocation (bump-allocate from the pool, recycle via free-list)
3133 // --------------------------------------------------------------------
3134 INode* allocNode()
3135 {
3136 if (freeList_)
3137 {
3138 INode* n = freeList_;
3139 freeList_ = n->child1;
3140 return n; // already constructed; box storage reused
3141 }
3142 INode* n = Base::pool_.template allocate<INode>();
3143 // Placement-new so that, for DIM=-1, the std::vector box is constructed.
3144 ::new (static_cast<void*>(n)) INode();
3145 resize(n->box, static_cast<Dimension>(this->veclen(*this)));
3146 if (kCacheCoords) resize(n->pcoord, static_cast<Dimension>(this->veclen(*this)));
3147 return n;
3148 }
3149
3151 void cacheCoords(INode* n)
3152 {
3153 if (!kCacheCoords) return;
3154 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3155 for (Dimension i = 0; i < dims; ++i) n->pcoord[i] = pt(n->ptIdx, i);
3156 }
3157
3159 ElementType nodeCoord(const INode* n, Dimension d) const
3160 {
3161 return kCacheCoords ? n->pcoord[d] : pt(n->ptIdx, d);
3162 }
3163
3165 bool nodeInBox(const INode* n, const BoundingBox& b) const
3166 {
3167 if (!kCacheCoords) return pointInBox(n->ptIdx, b);
3168 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3169 for (Dimension i = 0; i < dims; ++i)
3170 if (n->pcoord[i] < b[i].low || n->pcoord[i] > b[i].high) return false;
3171 return true;
3172 }
3173
3174 void recycleNode(INode* n)
3175 {
3176 n->child1 = freeList_;
3177 freeList_ = n;
3178 }
3179
3182 void destroyNodeObjects()
3183 {
3184 if (std::is_trivially_destructible<INode>::value) return;
3185 destroySubtree(iroot_);
3186 iroot_ = nullptr;
3187 while (freeList_)
3188 {
3189 INode* n = freeList_;
3190 freeList_ = n->child1;
3191 n->~INode();
3192 }
3193 }
3194
3195 void destroySubtree(INode* n)
3196 {
3197 if (!n) return;
3198 destroySubtree(n->child1);
3199 destroySubtree(n->child2);
3200 n->~INode();
3201 }
3202
3203 // --------------------------------------------------------------------
3204 // Helpers
3205 // --------------------------------------------------------------------
3206 ElementType pt(IndexType idx, Dimension d) const { return dataset_.kdtree_get_pt(idx, d); }
3207
3208 void ensureNodeMap(IndexType idx)
3209 {
3210 if (idx >= nodeOfPoint_.size()) nodeOfPoint_.resize(static_cast<size_t>(idx) + 1, nullptr);
3211 }
3212
3213 void syncRootBox()
3214 {
3215 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3216 if (iroot_)
3217 for (Dimension i = 0; i < dims; ++i) Base::root_bbox_[i] = iroot_->box[i];
3218 else
3219 for (Dimension i = 0; i < dims; ++i) Base::root_bbox_[i] = Interval{0, 0};
3220 }
3221
3222 void initBoxToPoint(INode* n)
3223 {
3224 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3225 for (Dimension i = 0; i < dims; ++i)
3226 {
3227 const ElementType v = pt(n->ptIdx, i);
3228 n->box[i].low = n->box[i].high = v;
3229 }
3230 }
3231
3232 void expandBoxToPoint(INode* n, IndexType idx)
3233 {
3234 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3235 for (Dimension i = 0; i < dims; ++i)
3236 {
3237 const ElementType v = pt(idx, i);
3238 if (v < n->box[i].low) n->box[i].low = v;
3239 if (v > n->box[i].high) n->box[i].high = v;
3240 }
3241 }
3242
3243 void unionBox(INode* n, const INode* c)
3244 {
3245 if (!c) return;
3246 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3247 for (Dimension i = 0; i < dims; ++i)
3248 {
3249 if (c->box[i].low < n->box[i].low) n->box[i].low = c->box[i].low;
3250 if (c->box[i].high > n->box[i].high) n->box[i].high = c->box[i].high;
3251 }
3252 }
3253
3254 bool pointInBox(IndexType idx, const BoundingBox& b) const
3255 {
3256 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3257 for (Dimension i = 0; i < dims; ++i)
3258 {
3259 const ElementType v = pt(idx, i);
3260 if (v < b[i].low || v > b[i].high) return false;
3261 }
3262 return true;
3263 }
3264
3265 bool boxFullyInside(const BoundingBox& inner, const BoundingBox& outer) const
3266 {
3267 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3268 for (Dimension i = 0; i < dims; ++i)
3269 if (inner[i].low < outer[i].low || inner[i].high > outer[i].high) return false;
3270 return true;
3271 }
3272
3273 bool boxDisjoint(const BoundingBox& a, const BoundingBox& b) const
3274 {
3275 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3276 for (Dimension i = 0; i < dims; ++i)
3277 if (a[i].high < b[i].low || a[i].low > b[i].high) return true;
3278 return false;
3279 }
3280
3281 // --------------------------------------------------------------------
3282 // Insertion
3283 // --------------------------------------------------------------------
3284 INode* makeLeaf(IndexType idx, Dimension depth, INode* parent)
3285 {
3286 INode* n = allocNode();
3287 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3288 n->ptIdx = idx;
3289 n->divfeat = static_cast<Dimension>(depth % dims);
3290 n->deleted = false;
3291 n->treeDeleted = false;
3292 n->child1 = n->child2 = nullptr;
3293 n->parent = parent;
3294 n->subtree_size = 1;
3295 n->invalid_count = 0;
3296 initBoxToPoint(n);
3297 cacheCoords(n);
3298 nodeOfPoint_[idx] = n;
3299 return n;
3300 }
3301
3304 void insertOne(IndexType idx)
3305 {
3306 pendingRebuild_ = nullptr;
3307 iroot_ = insertRec(iroot_, idx, 0, nullptr);
3308 ++liveCount_;
3309 ++totalCount_;
3310 if (pendingRebuild_ && inlineRebuild_) rebuildAt(pendingRebuild_);
3311 }
3312
3313 INode* insertRec(INode* node, IndexType idx, Dimension depth, INode* parent)
3314 {
3315 if (!node) return makeLeaf(idx, depth, parent);
3316 if (node->treeDeleted) pushDownDelete(node);
3317
3318 ++node->subtree_size;
3319 expandBoxToPoint(node, idx);
3320
3321 const Dimension axis = node->divfeat;
3322 if (pt(idx, axis) < nodeCoord(node, axis))
3323 node->child1 = insertRec(node->child1, idx, static_cast<Dimension>(depth + 1), node);
3324 else
3325 node->child2 = insertRec(node->child2, idx, static_cast<Dimension>(depth + 1), node);
3326
3327 // On the unwind, remember the *highest* unbalanced node seen on the
3328 // path (ancestors are visited after descendants, so the last write
3329 // wins). insertOne() rebuilds it once, avoiding a second descent.
3330 if (isBalanceScapegoat(node)) pendingRebuild_ = node;
3331 return node;
3332 }
3333
3336 void pushDownDelete(INode* node)
3337 {
3338 node->deleted = true;
3339 if (node->child1)
3340 {
3341 node->child1->treeDeleted = true;
3342 node->child1->invalid_count = node->child1->subtree_size;
3343 }
3344 if (node->child2)
3345 {
3346 node->child2->treeDeleted = true;
3347 node->child2->invalid_count = node->child2->subtree_size;
3348 }
3349 node->treeDeleted = false; // invalid_count already == subtree_size
3350 }
3351
3352 Size maxChildSize(const INode* node) const
3353 {
3354 const Size l = node->child1 ? node->child1->subtree_size : 0;
3355 const Size r = node->child2 ? node->child2->subtree_size : 0;
3356 return l > r ? l : r;
3357 }
3358
3359 bool isBalanceScapegoat(const INode* node) const
3360 {
3361 if (node->subtree_size < kMinBalanceRebuild) return false;
3362 return static_cast<float>(maxChildSize(node)) >
3363 alphaBal_ * static_cast<float>(node->subtree_size);
3364 }
3365
3366 // --------------------------------------------------------------------
3367 // Deletion (lazy) + box-region deletion
3368 // --------------------------------------------------------------------
3370 void killSubtree(INode* node)
3371 {
3372 node->treeDeleted = true;
3373 node->invalid_count = node->subtree_size;
3374 }
3375
3377 Size removeOutsideBoxRec(INode* node, const BoundingBox& keep)
3378 {
3379 if (!node) return 0;
3380 if (node->invalid_count == node->subtree_size) return 0; // already all dead
3381 if (boxFullyInside(node->box, keep)) return 0; // keep entire subtree
3382 if (boxDisjoint(node->box, keep))
3383 {
3384 const Size newly = node->subtree_size - node->invalid_count;
3385 killSubtree(node);
3386 liveCount_ -= newly;
3387 return newly;
3388 }
3389 Size newly = 0;
3390 if (!node->deleted && !nodeInBox(node, keep))
3391 {
3392 node->deleted = true;
3393 ++newly;
3394 --liveCount_;
3395 }
3396 newly += removeOutsideBoxRec(node->child1, keep);
3397 newly += removeOutsideBoxRec(node->child2, keep);
3398 node->invalid_count += newly;
3399 return newly;
3400 }
3401
3403 Size removeBoxRec(INode* node, const BoundingBox& box)
3404 {
3405 if (!node) return 0;
3406 if (node->invalid_count == node->subtree_size) return 0;
3407 if (boxDisjoint(node->box, box)) return 0; // nothing inside
3408 if (boxFullyInside(node->box, box))
3409 {
3410 const Size newly = node->subtree_size - node->invalid_count;
3411 killSubtree(node);
3412 liveCount_ -= newly;
3413 return newly;
3414 }
3415 Size newly = 0;
3416 if (!node->deleted && nodeInBox(node, box))
3417 {
3418 node->deleted = true;
3419 ++newly;
3420 --liveCount_;
3421 }
3422 newly += removeBoxRec(node->child1, box);
3423 newly += removeBoxRec(node->child2, box);
3424 node->invalid_count += newly;
3425 return newly;
3426 }
3427
3428 bool isDeletionScapegoat(const INode* node) const
3429 {
3430 if (node->subtree_size == 0) return false;
3431 return static_cast<float>(node->invalid_count) >
3432 alphaDel_ * static_cast<float>(node->subtree_size);
3433 }
3434
3435 INode* findDeletionScapegoat(INode* node) const
3436 {
3437 if (!node) return nullptr;
3438 if (isDeletionScapegoat(node)) return node; // highest wins
3439 if (INode* l = findDeletionScapegoat(node->child1)) return l;
3440 return findDeletionScapegoat(node->child2);
3441 }
3442
3443 void maybeRebuildForDeletion()
3444 {
3445 if (!iroot_ || !inlineRebuild_) return;
3446 if (INode* sg = findDeletionScapegoat(iroot_)) rebuildAt(sg);
3447 }
3448
3449 // --------------------------------------------------------------------
3450 // Partial rebuild (scapegoat): flatten live points, rebuild balanced
3451 // --------------------------------------------------------------------
3452 void rebuildAt(INode* node)
3453 {
3454 INode* par = node->parent;
3455 INode** link = par ? (par->child1 == node ? &par->child1 : &par->child2) : &iroot_;
3456
3457 const Size oldSize = node->subtree_size;
3458 const Size oldInvalid = node->invalid_count;
3459
3460 buildBuf_.clear();
3461 collectLiveAndFree(node, buildBuf_);
3462
3463 INode* nb = buildBalanced(buildBuf_, 0, buildBuf_.size(), 0, par);
3464 *link = nb;
3465
3466 const Size newSize = nb ? nb->subtree_size : 0; // == number of live pts
3467 // Propagate the change in (size, invalid) up to the ancestors.
3468 for (INode* p = par; p; p = p->parent)
3469 {
3470 p->subtree_size = p->subtree_size - oldSize + newSize;
3471 p->invalid_count = p->invalid_count - oldInvalid;
3472 }
3473 totalCount_ = totalCount_ - oldSize + newSize;
3474 }
3475
3479 void collectLiveAndFree(INode* node, std::vector<IndexType>& out)
3480 {
3481 if (!node) return;
3482 if (node->treeDeleted)
3483 {
3484 freeDeadSubtree(node);
3485 return;
3486 }
3487 if (!node->deleted)
3488 out.push_back(node->ptIdx);
3489 else
3490 dropDeadPoint(node->ptIdx);
3491 collectLiveAndFree(node->child1, out);
3492 collectLiveAndFree(node->child2, out);
3493 recycleNode(node);
3494 }
3495
3496 void freeDeadSubtree(INode* node)
3497 {
3498 if (!node) return;
3499 dropDeadPoint(node->ptIdx);
3500 freeDeadSubtree(node->child1);
3501 freeDeadSubtree(node->child2);
3502 recycleNode(node);
3503 }
3504
3505 void dropDeadPoint(IndexType idx)
3506 {
3507 if (idx < nodeOfPoint_.size()) nodeOfPoint_[idx] = nullptr;
3508 if (collectRemoved_) removedSink_.push_back(idx);
3509 }
3510
3512 void snapshotRec(const INode* node, std::vector<IndexType>& out) const
3513 {
3514 if (!node) return;
3515 if (node->invalid_count == node->subtree_size) return; // whole subtree dead
3516 if (!node->deleted) out.push_back(node->ptIdx);
3517 snapshotRec(node->child1, out);
3518 snapshotRec(node->child2, out);
3519 }
3520
3522 void collectAllRec(const INode* node, std::vector<IndexType>& out) const
3523 {
3524 if (!node) return;
3525 out.push_back(node->ptIdx);
3526 collectAllRec(node->child1, out);
3527 collectAllRec(node->child2, out);
3528 }
3529
3531 INode* buildBalanced(
3532 std::vector<IndexType>& buf, size_t lo, size_t hi, Dimension depth, INode* parent)
3533 {
3534 if (lo >= hi) return nullptr;
3535 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3536
3537 // Widest-spread axis over buf[lo,hi).
3538 Dimension axis = static_cast<Dimension>(depth % dims);
3539 ElementType bestSpan = -1;
3540 for (Dimension d = 0; d < dims; ++d)
3541 {
3542 ElementType mn = pt(buf[lo], d), mx = mn;
3543 for (size_t k = lo + 1; k < hi; ++k)
3544 {
3545 const ElementType v = pt(buf[k], d);
3546 if (v < mn) mn = v;
3547 if (v > mx) mx = v;
3548 }
3549 const ElementType span = mx - mn;
3550 if (span > bestSpan)
3551 {
3552 bestSpan = span;
3553 axis = d;
3554 }
3555 }
3556
3557 const size_t mid = lo + (hi - lo) / 2;
3558 std::nth_element(
3559 buf.begin() + lo, buf.begin() + mid, buf.begin() + hi,
3560 [this, axis](IndexType a, IndexType b) { return pt(a, axis) < pt(b, axis); });
3561
3562 INode* node = allocNode();
3563 node->ptIdx = buf[mid];
3564 node->divfeat = axis;
3565 node->deleted = false;
3566 node->treeDeleted = false;
3567 node->parent = parent;
3568 cacheCoords(node);
3569 nodeOfPoint_[buf[mid]] = node;
3570
3571 node->child1 = buildBalanced(buf, lo, mid, static_cast<Dimension>(depth + 1), node);
3572 node->child2 = buildBalanced(buf, mid + 1, hi, static_cast<Dimension>(depth + 1), node);
3573
3574 node->subtree_size = hi - lo;
3575 node->invalid_count = 0;
3576 initBoxToPoint(node);
3577 unionBox(node, node->child1);
3578 unionBox(node, node->child2);
3579 return node;
3580 }
3581
3582 // --------------------------------------------------------------------
3583 // Search
3584 // --------------------------------------------------------------------
3585 template <class RESULTSET>
3586 void searchLevelInc(
3587 RESULTSET& rs, const ElementType* vec, const INode* node, DistanceType mindist,
3588 distance_vector_t& dists, const DistanceType epsError, const Size dim) const
3589 {
3590 if (!node) return;
3591 if (node->invalid_count == node->subtree_size) return; // whole subtree dead
3592
3593 if (!node->deleted)
3594 {
3595#if defined(NANOFLANN_INCREMENTAL_INNODE_DISTANCE)
3596 // Opt-in: compute the node distance from the in-node coordinate cache
3597 // as a sum of per-axis accum_dist contributions. This avoids the
3598 // dataset_get() indirection and is ~12% faster on KNN, but is only
3599 // valid for *additive* (axis-decomposable) metrics — L1, L2,
3600 // L2_Simple. Do NOT enable it for SO2/SO3.
3601 DistanceType d = DistanceType();
3602 if (kCacheCoords)
3603 for (Size i = 0; i < dim; ++i)
3604 d += distance_.accum_dist(
3605 vec[i], node->pcoord[static_cast<Dimension>(i)], static_cast<Dimension>(i));
3606 else
3607 d = distance_.evalMetric(vec, node->ptIdx, dim);
3608#else
3609 const DistanceType d = distance_.evalMetric(vec, node->ptIdx, dim);
3610#endif
3611 if (d < rs.worstDist())
3612 rs.addPoint(
3613 static_cast<typename RESULTSET::DistanceType>(d),
3614 static_cast<typename RESULTSET::IndexType>(node->ptIdx));
3615 }
3616
3617 const Dimension axis = node->divfeat;
3618 const ElementType splitval = nodeCoord(node, axis);
3619 const ElementType val = vec[axis];
3620 const DistanceType cut = distance_.accum_dist(val, splitval, axis);
3621
3622 const INode* nearChild;
3623 const INode* farChild;
3624 if (val < splitval)
3625 {
3626 nearChild = node->child1;
3627 farChild = node->child2;
3628 }
3629 else
3630 {
3631 nearChild = node->child2;
3632 farChild = node->child1;
3633 }
3634
3635 searchLevelInc(rs, vec, nearChild, mindist, dists, epsError, dim);
3636
3637 const DistanceType dst = dists[axis];
3638 const DistanceType newmin = mindist + cut - dst;
3639 dists[axis] = cut;
3640 if (newmin * epsError <= rs.worstDist())
3641 searchLevelInc(rs, vec, farChild, newmin, dists, epsError, dim);
3642 dists[axis] = dst;
3643 }
3644
3645 template <typename RESULTSET>
3646 void findWithinBoxRec(RESULTSET& result, const INode* node, const BoundingBox& bbox) const
3647 {
3648 if (!node) return;
3649 if (node->invalid_count == node->subtree_size) return;
3650 if (boxDisjoint(node->box, bbox)) return;
3651 if (!node->deleted && nodeInBox(node, bbox)) result.addPoint(0, node->ptIdx);
3652 findWithinBoxRec(result, node->child1, bbox);
3653 findWithinBoxRec(result, node->child2, bbox);
3654 }
3655};
3656
3657#ifndef NANOFLANN_NO_THREADS
3686template <typename Distance, class DatasetAdaptor, int32_t DIM = -1, typename IndexType = uint32_t>
3688{
3689 public:
3691 using ElementType = typename Inner::ElementType;
3692 using DistanceType = typename Inner::DistanceType;
3693 using Size = typename Inner::Size;
3694 using Dimension = typename Inner::Dimension;
3695 using BoundingBox = typename Inner::BoundingBox;
3696
3703 const Dimension dimensionality, const DatasetAdaptor& inputData,
3704 const KDTreeIncrementalIndexParams& params = {}, double rebuild_growth = 1.3,
3705 Size min_rebuild_size = 10000)
3706 : dataset_(inputData),
3707 dim_(dimensionality),
3708 params_(params),
3709 rebuildGrowth_(rebuild_growth),
3710 minRebuildSize_(min_rebuild_size)
3711 {
3712 active_.reset(new Inner(dimensionality, inputData, params));
3713 active_->setInlineRebuild(false);
3714 }
3715
3716 KDTreeSingleIndexIncrementalAdaptorMT(const KDTreeSingleIndexIncrementalAdaptorMT&) = delete;
3717 KDTreeSingleIndexIncrementalAdaptorMT& operator=(const KDTreeSingleIndexIncrementalAdaptorMT&) =
3718 delete;
3719
3720 ~KDTreeSingleIndexIncrementalAdaptorMT()
3721 {
3722 if (fut_.valid()) fut_.wait(); // let the worker finish before teardown
3723 }
3724
3726 void addPoints(IndexType start, IndexType end)
3727 {
3728 integrateIfReady();
3729 active_->addPoints(start, end);
3730 if (building_) log_.push_back({OpKind::Add, start, end, {}});
3731 maybeTriggerRebuild();
3732 }
3733 void addPoint(IndexType idx) { addPoints(idx, idx); }
3734
3735 void removePoint(IndexType idx)
3736 {
3737 integrateIfReady();
3738 active_->removePoint(idx);
3739 if (building_) log_.push_back({OpKind::Remove, idx, idx, {}});
3740 maybeTriggerRebuild();
3741 }
3742 void removeBox(const BoundingBox& box)
3743 {
3744 integrateIfReady();
3745 active_->removeBox(box);
3746 if (building_) log_.push_back({OpKind::RemoveBox, 0, 0, box});
3747 maybeTriggerRebuild();
3748 }
3749 void removeOutsideBox(const BoundingBox& keep)
3750 {
3751 integrateIfReady();
3752 active_->removeOutsideBox(keep);
3753 if (building_) log_.push_back({OpKind::RemoveOutsideBox, 0, 0, keep});
3754 maybeTriggerRebuild();
3755 }
3757
3759 template <typename RESULTSET>
3760 bool findNeighbors(
3761 RESULTSET& result, const ElementType* vec, const SearchParameters& sp = {}) const
3762 {
3763 return active_->findNeighbors(result, vec, sp);
3764 }
3765 Size knnSearch(
3766 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
3767 DistanceType* out_distances, const SearchParameters& sp = {}) const
3768 {
3769 return active_->knnSearch(query_point, num_closest, out_indices, out_distances, sp);
3770 }
3771 Size radiusSearch(
3772 const ElementType* query_point, const DistanceType& radius,
3773 std::vector<ResultItem<IndexType, DistanceType>>& IndicesDists,
3774 const SearchParameters& sp = {}) const
3775 {
3776 return active_->radiusSearch(query_point, radius, IndicesDists, sp);
3777 }
3778 Size rknnSearch(
3779 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
3780 DistanceType* out_distances, const DistanceType& radius) const
3781 {
3782 return active_->rknnSearch(query_point, num_closest, out_indices, out_distances, radius);
3783 }
3784 template <typename RESULTSET>
3785 Size findWithinBox(RESULTSET& result, const BoundingBox& bbox) const
3786 {
3787 return active_->findWithinBox(result, bbox);
3788 }
3790
3792 Size size() const noexcept { return active_->size(); }
3793 bool empty() const noexcept { return active_->empty(); }
3794 Size physicalSize() const noexcept { return active_->physicalSize(); }
3795 bool isRebuilding() const noexcept { return building_; }
3796
3798 NANOFLANN_NODISCARD BoundingBox boundingBox() const { return active_->boundingBox(); }
3799
3801 void snapshotLiveIndices(std::vector<IndexType>& out) const
3802 {
3803 active_->snapshotLiveIndices(out);
3804 }
3805
3807 void reserve(Size n) { active_->reserve(n); }
3808
3810 void sync()
3811 {
3812 if (building_ && fut_.valid()) fut_.wait();
3813 integrateIfReady();
3814 }
3815
3817 const Inner& activeIndex() const { return *active_; }
3819
3827 void setRebuildCallback(std::function<void(Inner&)> cb) { rebuildCallback_ = std::move(cb); }
3828
3830
3834 void setCollectRemovedPoints(bool enable)
3835 {
3836 collectRemoved_ = enable;
3837 if (!enable) std::vector<IndexType>().swap(removedSink_);
3838 }
3839
3843 std::vector<IndexType> acquireRemovedPoints()
3844 {
3845 std::vector<IndexType> out;
3846 out.swap(removedSink_);
3847 return out;
3848 }
3849
3850
3851 private:
3852 enum class OpKind
3853 {
3854 Add,
3855 Remove,
3856 RemoveBox,
3857 RemoveOutsideBox
3858 };
3859 struct LoggedOp
3860 {
3861 OpKind kind;
3862 IndexType a, b;
3863 BoundingBox box;
3864 };
3865
3866 void maybeTriggerRebuild()
3867 {
3868 if (building_) return;
3869 const Size phys = active_->physicalSize();
3870 if (phys < minRebuildSize_) return;
3871 const Size base = lastBuildLive_ ? lastBuildLive_ : Size(1);
3872 if (static_cast<double>(phys) < rebuildGrowth_ * static_cast<double>(base)) return;
3873
3874 // Snapshot the live indices on the foreground thread, then build a fresh
3875 // balanced tree from them on a background thread.
3876 auto snapshot = std::make_shared<std::vector<IndexType>>();
3877 active_->snapshotLiveIndices(*snapshot);
3878
3879 const DatasetAdaptor& ds = dataset_;
3880 const Dimension d = dim_;
3881 KDTreeIncrementalIndexParams p = params_;
3882 std::function<void(Inner&)> cb = rebuildCallback_;
3883 fut_ = std::async(
3884 std::launch::async,
3885 [snapshot, &ds, d, p, cb]() -> std::unique_ptr<Inner>
3886 {
3887 std::unique_ptr<Inner> t(new Inner(d, ds, p));
3888 t->setInlineRebuild(false);
3889 t->buildFromIndices(*snapshot);
3890 if (cb) cb(*t); // background post-rebuild hook (e.g. recompute covariances)
3891 return t;
3892 });
3893 building_ = true;
3894 log_.clear();
3895 }
3896
3897 void integrateIfReady()
3898 {
3899 if (!building_ || !fut_.valid()) return;
3900 if (fut_.wait_for(std::chrono::seconds(0)) != std::future_status::ready) return;
3901
3902 std::unique_ptr<Inner> fresh = fut_.get();
3903 fresh->setInlineRebuild(false);
3904 // Replay the operations buffered while the background build was running.
3905 for (const auto& op : log_)
3906 {
3907 switch (op.kind)
3908 {
3909 case OpKind::Add:
3910 fresh->addPoints(op.a, op.b);
3911 break;
3912 case OpKind::Remove:
3913 fresh->removePoint(op.a);
3914 break;
3915 case OpKind::RemoveBox:
3916 fresh->removeBox(op.box);
3917 break;
3918 case OpKind::RemoveOutsideBox:
3919 fresh->removeOutsideBox(op.box);
3920 break;
3921 }
3922 }
3923 log_.clear();
3924 // Dataset slots referenced by the OLD tree but not by the fresh one are
3925 // now free for the caller to recycle (no node references them anymore).
3926 if (collectRemoved_)
3927 {
3928 std::vector<IndexType> oldPhysical;
3929 active_->collectPhysicalIndices(oldPhysical);
3930 for (IndexType idx : oldPhysical)
3931 if (!fresh->referencesIndex(idx)) removedSink_.push_back(idx);
3932 }
3933 active_ = std::move(fresh);
3934 lastBuildLive_ = active_->size();
3935 building_ = false;
3936 }
3937
3938 const DatasetAdaptor& dataset_;
3939 Dimension dim_;
3940 KDTreeIncrementalIndexParams params_;
3941 double rebuildGrowth_;
3942 Size minRebuildSize_;
3943
3944 std::unique_ptr<Inner> active_;
3945 std::future<std::unique_ptr<Inner>> fut_;
3946 bool building_ = false;
3947 Size lastBuildLive_ = 0;
3948 std::vector<LoggedOp> log_;
3949
3950 bool collectRemoved_ = false;
3951 std::vector<IndexType> removedSink_;
3952 std::function<void(Inner&)> rebuildCallback_;
3953};
3954#endif // NANOFLANN_NO_THREADS
3955
3981template <
3982 class MatrixType, int32_t DIM = -1, class Distance = nanoflann::metric_L2,
3983 bool row_major = true>
3985{
3987 using num_t = typename MatrixType::Scalar;
3988 using IndexType = typename MatrixType::Index;
3989 using metric_t = typename Distance::template traits<num_t, self_t, IndexType>::distance_t;
3990
3991 using index_t = KDTreeSingleIndexAdaptor<
3992 metric_t, self_t, row_major ? MatrixType::ColsAtCompileTime : MatrixType::RowsAtCompileTime,
3993 IndexType>;
3994
3995 index_t* index_;
3997
3998 using Offset = typename index_t::Offset;
3999 using Size = typename index_t::Size;
4000 using Dimension = typename index_t::Dimension;
4001
4004 const Dimension dimensionality, const std::reference_wrapper<const MatrixType>& mat,
4005 const int leaf_max_size = 10, const unsigned int n_thread_build = 1)
4006 : m_data_matrix(mat)
4007 {
4008 const auto dims = row_major ? mat.get().cols() : mat.get().rows();
4009 if (static_cast<Dimension>(dims) != dimensionality)
4010 throw std::runtime_error(
4011 "Error: 'dimensionality' must match column count in data "
4012 "matrix");
4013 if (DIM > 0 && static_cast<int32_t>(dims) != DIM)
4014 throw std::runtime_error(
4015 "Data set dimensionality does not match the 'DIM' template "
4016 "argument");
4017 index_ = new index_t(
4018 static_cast<Dimension>(dims), *this /* adaptor */,
4020 leaf_max_size, nanoflann::KDTreeSingleIndexAdaptorFlags::None, n_thread_build));
4021 }
4022
4023 public:
4025 KDTreeEigenMatrixAdaptor(const self_t&) = delete;
4026 self_t& operator=(const self_t&) = delete;
4027
4032 KDTreeEigenMatrixAdaptor(self_t&&) = delete;
4033 self_t& operator=(self_t&&) = delete;
4034
4035 ~KDTreeEigenMatrixAdaptor() { delete index_; }
4036
4037 const std::reference_wrapper<const MatrixType> m_data_matrix;
4038
4047 void query(
4048 const num_t* query_point, const Size num_closest, IndexType* out_indices,
4049 num_t* out_distances) const
4050 {
4051 nanoflann::KNNResultSet<num_t, IndexType> resultSet(num_closest);
4052 resultSet.init(out_indices, out_distances);
4053 index_->findNeighbors(resultSet, query_point);
4054 }
4055
4058
4059 inline const self_t& derived() const noexcept { return *this; }
4060 inline self_t& derived() noexcept { return *this; }
4061
4062 // Must return the number of data points
4063 inline Size kdtree_get_point_count() const
4064 {
4065 if (row_major)
4066 return m_data_matrix.get().rows();
4067 else
4068 return m_data_matrix.get().cols();
4069 }
4070
4071 // Returns the dim'th component of the idx'th point in the class:
4072 inline num_t kdtree_get_pt(const IndexType idx, size_t dim) const
4073 {
4074 if (row_major)
4075 return m_data_matrix.get().coeff(idx, IndexType(dim));
4076 else
4077 return m_data_matrix.get().coeff(IndexType(dim), idx);
4078 }
4079
4080 // Optional bounding-box computation: return false to default to a standard
4081 // bbox computation loop.
4082 // Return true if the BBOX was already computed by the class and returned
4083 // in "bb" so it can be avoided to redo it again. Look at bb.size() to
4084 // find out the expected dimensionality (e.g. 2 or 3 for point clouds)
4085 template <class BBOX>
4086 inline bool kdtree_get_bbox(BBOX& /*bb*/) const
4087 {
4088 return false;
4089 }
4090
4092
4093}; // end of KDTreeEigenMatrixAdaptor
4094
4095 // end of grouping
4097} // namespace nanoflann
4098
4099#undef NANOFLANN_RESTRICT
bool addPoint(DistanceType, IndexType index)
Definition nanoflann.hpp:487
Definition nanoflann.hpp:1048
NANOFLANN_NODISCARD bool isActive(IndexType) const
Definition nanoflann.hpp:1206
void freeIndex(Derived &obj)
Definition nanoflann.hpp:1052
NANOFLANN_NODISCARD Size veclen(const Derived &obj) const noexcept
Definition nanoflann.hpp:1153
void computeMinMax(const Derived &obj, Offset ind, Size count, Dimension element, ElementType &min_elem, ElementType &max_elem) const
Definition nanoflann.hpp:1189
BoundingBox root_bbox_
Definition nanoflann.hpp:1135
void saveIndex(const Derived &obj, std::ostream &stream) const
Definition nanoflann.hpp:1667
void computeBoundingBox(BoundingBox &bbox)
Definition nanoflann.hpp:1211
NANOFLANN_NODISCARD Size usedMemory(const Derived &obj) const
Definition nanoflann.hpp:1179
Dimension dim_
Dimensionality of each data point.
Definition nanoflann.hpp:1124
typename array_or_vector< DIM, DistanceType >::type distance_vector_t
Definition nanoflann.hpp:1132
void planeSplit(const Derived &obj, const Offset ind, const Size count, const Dimension cutfeat, const DistanceType &cutval, Offset &lim1, Offset &lim2)
Definition nanoflann.hpp:1561
Size n_thread_build_
Number of thread for concurrent tree build.
Definition nanoflann.hpp:1119
NANOFLANN_NODISCARD Size size(const Derived &obj) const noexcept
Definition nanoflann.hpp:1147
std::vector< IndexType > vAcc_
Definition nanoflann.hpp:1066
bool makeNode(Derived &obj, NodePtr node, const Offset left, const Offset right, BoundingBox &bbox, Offset &idx, Dimension &cutfeat, DistanceType &cutval)
Definition nanoflann.hpp:1324
bool searchLevel(RESULTSET &result_set, const ElementType *vec, const NodePtr node, DistanceType mindist, distance_vector_t &dists, const DistanceType epsError) const
Definition nanoflann.hpp:1241
Size size_at_index_build_
Number of points in the dataset when the index was built.
Definition nanoflann.hpp:1123
NodePtr divideTreeConcurrent(Derived &obj, const Offset left, const Offset right, BoundingBox &bbox, std::atomic< unsigned int > &thread_count, std::mutex &mutex)
Definition nanoflann.hpp:1418
Size size_
Number of current points in the dataset.
Definition nanoflann.hpp:1121
void finalizeSplitNode(Derived &obj, NodePtr node, const Dimension cutfeat, const BoundingBox &left_bbox, const BoundingBox &right_bbox, BoundingBox &bbox)
Definition nanoflann.hpp:1366
void loadIndex(Derived &obj, std::istream &stream)
Definition nanoflann.hpp:1713
PooledAllocator pool_
Definition nanoflann.hpp:1144
ElementType dataset_get(const Derived &obj, IndexType element, Dimension component) const
Helper accessor to the dataset points:
Definition nanoflann.hpp:1170
typename array_or_vector< DIM, Interval >::type BoundingBox
Definition nanoflann.hpp:1128
static constexpr uint32_t SAVE_MAGIC
Definition nanoflann.hpp:1647
Definition nanoflann.hpp:1834
void saveIndex(std::ostream &stream) const
Definition nanoflann.hpp:2195
NANOFLANN_NODISCARD Size findWithinBox(RESULTSET &result, const BoundingBox &bbox) const
Definition nanoflann.hpp:2026
void buildIndex()
Definition nanoflann.hpp:1939
NANOFLANN_NODISCARD Size radiusSearch(const ElementType *query_point, const DistanceType &radius, std::vector< ResultItem< IndexType, DistanceType > > &IndicesDists, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2116
NANOFLANN_NODISCARD Size radiusSearchCustomCallback(const ElementType *query_point, SEARCH_CALLBACK &resultSet, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2132
NANOFLANN_NODISCARD Size knnSearch(const ElementType *query_point, const Size num_closest, IndexType *out_indices, DistanceType *out_distances) const
Definition nanoflann.hpp:2087
KDTreeSingleIndexAdaptor(const KDTreeSingleIndexAdaptor< Distance, DatasetAdaptor, DIM, index_t > &)=delete
bool findNeighbors(RESULTSET &result, const ElementType *vec, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:1986
NANOFLANN_NODISCARD Size rknnSearch(const ElementType *query_point, const Size num_closest, IndexType *out_indices, DistanceType *out_distances, const DistanceType &radius) const
Definition nanoflann.hpp:2155
typename Base::distance_vector_t distance_vector_t
Definition nanoflann.hpp:1870
void loadIndex(std::istream &stream)
Definition nanoflann.hpp:2202
typename Base::BoundingBox BoundingBox
Definition nanoflann.hpp:1866
KDTreeSingleIndexAdaptor(const Dimension dimensionality, const DatasetAdaptor &inputData, const KDTreeSingleIndexAdaptorParams &params, Args &&... args)
Definition nanoflann.hpp:1893
Definition nanoflann.hpp:2248
KDTreeSingleIndexDynamicAdaptor_(const Dimension dimensionality, const DatasetAdaptor &inputData, std::vector< int > &treeIndex, const KDTreeSingleIndexAdaptorParams &params=KDTreeSingleIndexAdaptorParams())
Definition nanoflann.hpp:2302
typename Base::BoundingBox BoundingBox
Definition nanoflann.hpp:2278
NANOFLANN_NODISCARD Size knnSearch(const ElementType *query_point, const Size num_closest, IndexType *out_indices, DistanceType *out_distances, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2430
KDTreeSingleIndexDynamicAdaptor_(const KDTreeSingleIndexDynamicAdaptor_ &rhs)=default
void buildIndex()
Definition nanoflann.hpp:2347
void saveIndex(std::ostream &stream)
Definition nanoflann.hpp:2492
NANOFLANN_NODISCARD bool isActive(IndexType idx) const
Definition nanoflann.hpp:2285
NANOFLANN_NODISCARD Size radiusSearchCustomCallback(const ElementType *query_point, SEARCH_CALLBACK &resultSet, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2475
typename Base::distance_vector_t distance_vector_t
Definition nanoflann.hpp:2282
void loadIndex(std::istream &stream)
Definition nanoflann.hpp:2499
KDTreeSingleIndexDynamicAdaptor_ & operator=(const KDTreeSingleIndexDynamicAdaptor_ &rhs)
Definition nanoflann.hpp:2328
NANOFLANN_NODISCARD Size radiusSearch(const ElementType *query_point, const DistanceType &radius, std::vector< ResultItem< IndexType, DistanceType > > &IndicesDists, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2459
bool findNeighbors(RESULTSET &result, const ElementType *vec, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2396
bool findNeighbors(RESULTSET &result, const ElementType *vec, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2701
const DatasetAdaptor & dataset_
The source of our data.
Definition nanoflann.hpp:2536
void removePoint(size_t idx)
Definition nanoflann.hpp:2674
std::unordered_map< IndexType, int > removedPoints_
Definition nanoflann.hpp:2544
void addPoints(IndexType start, IndexType end)
Definition nanoflann.hpp:2625
KDTreeSingleIndexDynamicAdaptor(const int dimensionality, const DatasetAdaptor &inputData, const KDTreeSingleIndexAdaptorParams &params=KDTreeSingleIndexAdaptorParams(), const size_t maximumPointCount=1000000000U)
Definition nanoflann.hpp:2600
std::vector< int > treeIndex_
Definition nanoflann.hpp:2540
const std::vector< index_container_t > & getAllIndices() const
Definition nanoflann.hpp:2557
Dimension dim_
Dimensionality of each data point.
Definition nanoflann.hpp:2548
KDTreeSingleIndexDynamicAdaptor(const KDTreeSingleIndexDynamicAdaptor< Distance, DatasetAdaptor, DIM, IndexType > &)=delete
void snapshotLiveIndices(std::vector< IndexType > &out) const
Definition nanoflann.hpp:3801
KDTreeSingleIndexIncrementalAdaptorMT(const Dimension dimensionality, const DatasetAdaptor &inputData, const KDTreeIncrementalIndexParams &params={}, double rebuild_growth=1.3, Size min_rebuild_size=10000)
Definition nanoflann.hpp:3702
void setCollectRemovedPoints(bool enable)
Definition nanoflann.hpp:3834
void sync()
Definition nanoflann.hpp:3810
const Inner & activeIndex() const
Definition nanoflann.hpp:3817
void reserve(Size n)
Definition nanoflann.hpp:3807
std::vector< IndexType > acquireRemovedPoints()
Definition nanoflann.hpp:3843
void setRebuildCallback(std::function< void(Inner &)> cb)
Definition nanoflann.hpp:3827
NANOFLANN_NODISCARD BoundingBox boundingBox() const
Definition nanoflann.hpp:3798
NANOFLANN_NODISCARD Size radiusSearch(const ElementType *query_point, const DistanceType &radius, std::vector< ResultItem< IndexType, DistanceType > > &IndicesDists, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:3089
NANOFLANN_NODISCARD Size rknnSearch(const ElementType *query_point, const Size num_closest, IndexType *out_indices, DistanceType *out_distances, const DistanceType &radius) const
Definition nanoflann.hpp:3110
void removeBox(const BoundingBox &box)
Definition nanoflann.hpp:2949
void setCollectRemovedPoints(bool enable)
Definition nanoflann.hpp:2967
NANOFLANN_NODISCARD Size radiusSearchCustomCallback(const ElementType *query_point, SEARCH_CALLBACK &resultSet, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:3101
void addPoint(IndexType idx)
Definition nanoflann.hpp:2893
NANOFLANN_NODISCARD BoundingBox boundingBox() const
Definition nanoflann.hpp:3045
void setInlineRebuild(bool enable)
Definition nanoflann.hpp:2985
KDTreeSingleIndexIncrementalAdaptor(const Dimension dimensionality, const DatasetAdaptor &inputData, const KDTreeIncrementalIndexParams &params={})
Definition nanoflann.hpp:2870
void snapshotLiveIndices(std::vector< IndexType > &out) const
Definition nanoflann.hpp:2989
NANOFLANN_NODISCARD Size usedMemory() const
Definition nanoflann.hpp:3036
void removePoint(IndexType idx)
Definition nanoflann.hpp:2931
NANOFLANN_NODISCARD Size knnSearch(const ElementType *query_point, const Size num_closest, IndexType *out_indices, DistanceType *out_distances, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:3078
void reserve(Size n)
Definition nanoflann.hpp:3049
NANOFLANN_NODISCARD Size size() const noexcept
Definition nanoflann.hpp:3029
NANOFLANN_NODISCARD Size physicalSize() const noexcept
Definition nanoflann.hpp:3033
bool findNeighbors(RESULTSET &result, const ElementType *vec, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:3062
void collectPhysicalIndices(std::vector< IndexType > &out) const
Definition nanoflann.hpp:2994
KDTreeSingleIndexIncrementalAdaptor(const KDTreeSingleIndexIncrementalAdaptor &)=delete
static constexpr bool kCacheCoords
Definition nanoflann.hpp:2824
void removeOutsideBox(const BoundingBox &keep)
Definition nanoflann.hpp:2958
NANOFLANN_NODISCARD Size findWithinBox(RESULTSET &result, const BoundingBox &bbox) const
Definition nanoflann.hpp:3122
void addPoints(IndexType start, IndexType end)
Definition nanoflann.hpp:2908
void buildFromIndices(const std::vector< IndexType > &idxs)
Definition nanoflann.hpp:3005
NANOFLANN_NODISCARD bool referencesIndex(IndexType idx) const
Definition nanoflann.hpp:2998
std::vector< IndexType > acquireRemovedPoints()
Definition nanoflann.hpp:2975
Definition nanoflann.hpp:288
bool addPoint(DistanceType dist, IndexType index)
Definition nanoflann.hpp:322
NANOFLANN_NODISCARD DistanceType worstDist() const noexcept
Returns the worst distance among found solutions if the search result is full, or the maximum possibl...
Definition nanoflann.hpp:329
Definition nanoflann.hpp:898
~PooledAllocator()
Definition nanoflann.hpp:934
void free_all()
Definition nanoflann.hpp:937
void * allocateBytes(const size_t req_size)
Definition nanoflann.hpp:953
T * allocate(const size_t count=1)
Definition nanoflann.hpp:1002
PooledAllocator()
Definition nanoflann.hpp:929
Definition nanoflann.hpp:344
bool addPoint(DistanceType dist, IndexType index)
Definition nanoflann.hpp:384
NANOFLANN_NODISCARD DistanceType worstDist() const noexcept
Returns the worst distance among found solutions if the search result is full, or the maximum possibl...
Definition nanoflann.hpp:391
Definition nanoflann.hpp:407
ResultItem< IndexType, DistanceType > worst_item() const
Definition nanoflann.hpp:448
bool addPoint(DistanceType dist, IndexType index)
Definition nanoflann.hpp:436
std::enable_if< has_assign< Container >::value, void >::type assign(Container &c, const size_t nElements, const T &value)
Definition nanoflann.hpp:195
std::enable_if< has_resize< Container >::value, void >::type resize(Container &c, const size_t nElements)
Definition nanoflann.hpp:174
constexpr T pi_const()
Definition nanoflann.hpp:141
bool has_flag(KDTreeSingleIndexAdaptorFlags f, KDTreeSingleIndexAdaptorFlags flag)
Definition nanoflann.hpp:848
Definition nanoflann.hpp:213
bool operator()(const PairType &p1, const PairType &p2) const
Definition nanoflann.hpp:216
Definition nanoflann.hpp:1110
Definition nanoflann.hpp:1085
Offset right
Indices of points in leaf node.
Definition nanoflann.hpp:1092
Dimension divfeat
Dimension used for subdivision. The values used for subdivision.
Definition nanoflann.hpp:1096
Node * child1
Definition nanoflann.hpp:1103
union nanoflann::KDTreeBaseClass::Node::@327270127162340211203002370327206303122355110302 node_type
void query(const num_t *query_point, const Size num_closest, IndexType *out_indices, num_t *out_distances) const
Definition nanoflann.hpp:4047
KDTreeEigenMatrixAdaptor(const self_t &)=delete
KDTreeEigenMatrixAdaptor(self_t &&)=delete
typename index_t::Offset Offset
Definition nanoflann.hpp:3998
KDTreeEigenMatrixAdaptor(const Dimension dimensionality, const std::reference_wrapper< const MatrixType > &mat, const int leaf_max_size=10, const unsigned int n_thread_build=1)
Constructor: takes a const ref to the matrix object with the data points.
Definition nanoflann.hpp:4003
Definition nanoflann.hpp:2725
Definition nanoflann.hpp:855
Size invalid_count
number of tombstoned nodes in subtree
Definition nanoflann.hpp:2808
Dimension divfeat
splitting axis at this node
Definition nanoflann.hpp:2801
bool treeDeleted
whole subtree lazily tombstoned
Definition nanoflann.hpp:2803
IndexType ptIdx
index of the stored data point
Definition nanoflann.hpp:2800
INode * parent
parent (nullptr at the root)
Definition nanoflann.hpp:2806
Size subtree_size
number of nodes in this subtree
Definition nanoflann.hpp:2807
INode * child1
"< split" child (also free-list link)
Definition nanoflann.hpp:2804
INode * child2
">= split" child
Definition nanoflann.hpp:2805
BoundingBox box
AABB of all points (live+dead) in this subtree Cache of this node's own point coordinates,...
Definition nanoflann.hpp:2809
bool deleted
this node's point is tombstoned
Definition nanoflann.hpp:2802
Definition nanoflann.hpp:549
Definition nanoflann.hpp:611
Definition nanoflann.hpp:678
Definition nanoflann.hpp:534
Definition nanoflann.hpp:232
DistanceType second
Distance from sample to query point.
Definition nanoflann.hpp:239
IndexType first
Index of the sample in the dataset.
Definition nanoflann.hpp:238
Definition nanoflann.hpp:717
DistanceType accum_dist(const U a, const V b, const size_t) const
Definition nanoflann.hpp:736
Definition nanoflann.hpp:760
Definition nanoflann.hpp:871
bool sorted
only for radius search, require neighbors sorted by distance (default: true)
Definition nanoflann.hpp:875
float eps
search for eps-approximate neighbors (default: 0)
Definition nanoflann.hpp:874
Definition nanoflann.hpp:1018
Definition nanoflann.hpp:162
Definition nanoflann.hpp:152
Definition nanoflann.hpp:785
Definition nanoflann.hpp:782
Definition nanoflann.hpp:795
Definition nanoflann.hpp:805
Definition nanoflann.hpp:802
Definition nanoflann.hpp:792
Definition nanoflann.hpp:814
Definition nanoflann.hpp:811
Definition nanoflann.hpp:823
Definition nanoflann.hpp:820