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 <condition_variable> // rebuild worker of the async incremental index
73#include <cstdint>
74#include <cstdio> // snprintf
75#include <cstdlib> // for abs()
76#include <exception> // std::exception_ptr (async incremental index)
77#include <functional> // std::reference_wrapper
78#include <future>
79#include <istream>
80#include <limits> // std::numeric_limits
81#include <memory> // std::unique_ptr (async incremental index)
82#include <mutex> // rebuild worker of the async incremental index
83#include <new> // placement new (incremental index node pool)
84#include <ostream>
85#include <stack>
86#include <stdexcept>
87#include <thread>
88#include <type_traits> // std::is_trivially_destructible
89#include <unordered_map>
90#include <vector>
91
93#define NANOFLANN_VERSION_STRING "1.12.1"
95#define NANOFLANN_VERSION 0x010C01
96
97// Avoid conflicting declaration of min/max macros in Windows headers
98#if !defined(NOMINMAX) && (defined(_WIN32) || defined(_WIN32_) || defined(WIN32) || defined(_WIN64))
99#define NOMINMAX
100#ifdef max
101#undef max
102#undef min
103#endif
104#endif
105// Avoid conflicts with X11 headers
106#ifdef None
107#undef None
108#endif
109
110// Handle restricted pointers
111#if defined(__GNUC__) || defined(__clang__)
112#define NANOFLANN_RESTRICT __restrict__
113#elif defined(_MSC_VER)
114#define NANOFLANN_RESTRICT __restrict
115#else
116#define NANOFLANN_RESTRICT
117#endif
118
119// [[nodiscard]] support
120#if defined(__has_cpp_attribute) && __has_cpp_attribute(nodiscard)
121#define NANOFLANN_NODISCARD [[nodiscard]]
122#else
123#define NANOFLANN_NODISCARD
124#endif
125
126// [[fallthrough]] support for intentional switch fall-throughs
127#if defined(__has_cpp_attribute) && __has_cpp_attribute(fallthrough)
128#define NANOFLANN_FALLTHROUGH [[fallthrough]]
129#else
130#define NANOFLANN_FALLTHROUGH
131#endif
132
133// Memory alignment of KD-tree nodes:
134#ifndef NANOFLANN_NODE_ALIGNMENT
135#define NANOFLANN_NODE_ALIGNMENT 16
136#endif
137
138namespace nanoflann
139{
142
144template <typename T>
145constexpr T pi_const()
146{
147 return static_cast<T>(3.14159265358979323846);
148}
149
154template <typename T, typename = int>
155struct has_resize : std::false_type
156{
157};
158
159template <typename T>
160struct has_resize<T, decltype((void)std::declval<T>().resize(1), 0)> : std::true_type
161{
162};
163
164template <typename T, typename = int>
165struct has_assign : std::false_type
166{
167};
168
169template <typename T>
170struct has_assign<T, decltype((void)std::declval<T>().assign(1, 0), 0)> : std::true_type
171{
172};
173
177template <typename Container>
178inline typename std::enable_if<has_resize<Container>::value, void>::type resize(
179 Container& c, const size_t nElements)
180{
181 c.resize(nElements);
182}
183
188template <typename Container>
189inline typename std::enable_if<!has_resize<Container>::value, void>::type resize(
190 Container& c, const size_t nElements)
191{
192 if (nElements != c.size()) throw std::logic_error("Attempt to resize a fixed size container.");
193}
194
198template <typename Container, typename T>
199inline typename std::enable_if<has_assign<Container>::value, void>::type assign(
200 Container& c, const size_t nElements, const T& value)
201{
202 c.assign(nElements, value);
203}
204
208template <typename Container, typename T>
209inline typename std::enable_if<!has_assign<Container>::value, void>::type assign(
210 Container& c, const size_t nElements, const T& value)
211{
212 for (size_t i = 0; i < nElements; i++) c[i] = value;
213}
214
217{
219 template <typename PairType>
220 bool operator()(const PairType& p1, const PairType& p2) const
221 {
222 return p1.second < p2.second;
223 }
224};
225
234template <typename IndexType = size_t, typename DistanceType = double>
235struct ResultItem
236{
237 ResultItem() = default;
238 ResultItem(const IndexType index, const DistanceType distance) : first(index), second(distance)
239 {
240 }
241
242 IndexType first;
243 DistanceType second;
244};
245
246namespace detail
247{
252template <typename DistanceType, typename IndexType, typename CountType>
253bool addPointToSortedResultSet(
254 DistanceType* dists, IndexType* indices, CountType& count, CountType capacity,
255 DistanceType dist, IndexType index)
256{
257 CountType i;
258 for (i = count; i > 0; --i)
259 {
260#ifdef NANOFLANN_FIRST_MATCH
261 if ((dists[i - 1] > dist) || ((dist == dists[i - 1]) && (indices[i - 1] > index)))
262 {
263#else
264 if (dists[i - 1] > dist)
265 {
266#endif
267 if (i < capacity)
268 {
269 dists[i] = dists[i - 1];
270 indices[i] = indices[i - 1];
271 }
272 }
273 else
274 break;
275 }
276 if (i < capacity)
277 {
278 dists[i] = dist;
279 indices[i] = index;
280 }
281 if (count < capacity) count++;
282 return true;
283}
284} // namespace detail
285
288
290template <typename _DistanceType, typename _IndexType = size_t, typename _CountType = size_t>
291class KNNResultSet
292{
293 public:
294 using DistanceType = _DistanceType;
295 using IndexType = _IndexType;
296 using CountType = _CountType;
297
298 private:
299 IndexType* indices;
300 DistanceType* dists;
301 CountType capacity;
302 CountType count;
303
304 public:
305 explicit KNNResultSet(CountType capacity_)
306 : indices(nullptr), dists(nullptr), capacity(capacity_), count(0)
307 {
308 }
309
310 void init(IndexType* indices_, DistanceType* dists_)
311 {
312 indices = indices_;
313 dists = dists_;
314 count = 0;
315 }
316
317 NANOFLANN_NODISCARD CountType size() const noexcept { return count; }
318 NANOFLANN_NODISCARD bool empty() const noexcept { return count == 0; }
319 NANOFLANN_NODISCARD bool full() const noexcept { return count == capacity; }
320
326 bool addPoint(DistanceType dist, IndexType index)
327 {
328 return detail::addPointToSortedResultSet(dists, indices, count, capacity, dist, index);
329 }
330
333 NANOFLANN_NODISCARD DistanceType worstDist() const noexcept
334 {
335 return (count < capacity || !count) ? std::numeric_limits<DistanceType>::max()
336 : dists[count - 1];
337 }
338
339 void sort()
340 {
341 // already sorted
342 }
343};
344
346template <typename _DistanceType, typename _IndexType = size_t, typename _CountType = size_t>
347class RKNNResultSet
348{
349 public:
350 using DistanceType = _DistanceType;
351 using IndexType = _IndexType;
352 using CountType = _CountType;
353
354 private:
355 IndexType* indices;
356 DistanceType* dists;
357 CountType capacity;
358 CountType count;
359 DistanceType maximumSearchDistanceSquared;
360
361 public:
362 explicit RKNNResultSet(CountType capacity_, DistanceType maximumSearchDistanceSquared_)
363 : indices(nullptr),
364 dists(nullptr),
365 capacity(capacity_),
366 count(0),
367 maximumSearchDistanceSquared(maximumSearchDistanceSquared_)
368 {
369 }
370
371 void init(IndexType* indices_, DistanceType* dists_)
372 {
373 indices = indices_;
374 dists = dists_;
375 count = 0;
376 if (capacity) dists[capacity - 1] = maximumSearchDistanceSquared;
377 }
378
379 NANOFLANN_NODISCARD CountType size() const noexcept { return count; }
380 NANOFLANN_NODISCARD bool empty() const noexcept { return count == 0; }
381 NANOFLANN_NODISCARD bool full() const noexcept { return count == capacity; }
382
388 bool addPoint(DistanceType dist, IndexType index)
389 {
390 return detail::addPointToSortedResultSet(dists, indices, count, capacity, dist, index);
391 }
392
395 NANOFLANN_NODISCARD DistanceType worstDist() const noexcept
396 {
397 return (count < capacity || !count) ? maximumSearchDistanceSquared : dists[count - 1];
398 }
399
400 void sort()
401 {
402 // already sorted
403 }
404};
405
409template <typename _DistanceType, typename _IndexType = size_t>
410class RadiusResultSet
411{
412 public:
413 using DistanceType = _DistanceType;
414 using IndexType = _IndexType;
415
416 public:
417 const DistanceType radius;
418
419 std::vector<ResultItem<IndexType, DistanceType>>& m_indices_dists;
420
421 explicit RadiusResultSet(
422 DistanceType radius_, std::vector<ResultItem<IndexType, DistanceType>>& indices_dists)
423 : radius(radius_), m_indices_dists(indices_dists)
424 {
425 init();
426 }
427
428 void init() { clear(); }
429 void clear() { m_indices_dists.clear(); }
430
431 NANOFLANN_NODISCARD size_t size() const noexcept { return m_indices_dists.size(); }
432 NANOFLANN_NODISCARD bool empty() const noexcept { return m_indices_dists.empty(); }
433 NANOFLANN_NODISCARD bool full() const noexcept { return true; }
434
440 bool addPoint(DistanceType dist, IndexType index)
441 {
442 if (dist < radius) m_indices_dists.emplace_back(index, dist);
443 return true;
444 }
445
446 NANOFLANN_NODISCARD DistanceType worstDist() const noexcept { return radius; }
447
453 {
454 if (m_indices_dists.empty())
455 throw std::runtime_error(
456 "Cannot invoke RadiusResultSet::worst_item() on "
457 "an empty list of results.");
458 auto it =
459 std::max_element(m_indices_dists.begin(), m_indices_dists.end(), IndexDist_Sorter());
460 return *it;
461 }
462
463 void sort() { std::sort(m_indices_dists.begin(), m_indices_dists.end(), IndexDist_Sorter()); }
464};
465
471template <typename _IndexType = size_t>
472class BoxResultSet
473{
474 public:
475 using IndexType = _IndexType;
476
477 std::vector<IndexType>& m_indices;
478
479 explicit BoxResultSet(std::vector<IndexType>& indices) : m_indices(indices)
480 {
481 m_indices.clear();
482 }
483
484 NANOFLANN_NODISCARD size_t size() const noexcept { return m_indices.size(); }
485 NANOFLANN_NODISCARD bool empty() const noexcept { return m_indices.empty(); }
486 NANOFLANN_NODISCARD bool full() const noexcept { return true; }
487
490 template <typename DistanceType>
491 bool addPoint(DistanceType /*dist*/, IndexType index)
492 {
493 m_indices.push_back(index);
494 return true;
495 }
496
497 void sort() { std::sort(m_indices.begin(), m_indices.end()); }
498};
499
501
504template <typename T>
505void save_value(std::ostream& stream, const T& value)
506{
507 stream.write(reinterpret_cast<const char*>(&value), sizeof(T));
508}
509
510template <typename T>
511void save_value(std::ostream& stream, const std::vector<T>& value)
512{
513 size_t size = value.size();
514 stream.write(reinterpret_cast<const char*>(&size), sizeof(size_t));
515 stream.write(reinterpret_cast<const char*>(value.data()), sizeof(T) * size);
516}
517
518template <typename T>
519void load_value(std::istream& stream, T& value)
520{
521 stream.read(reinterpret_cast<char*>(&value), sizeof(T));
522}
523
524template <typename T>
525void load_value(std::istream& stream, std::vector<T>& value)
526{
527 size_t size;
528 stream.read(reinterpret_cast<char*>(&size), sizeof(size_t));
529 value.resize(size);
530 stream.read(reinterpret_cast<char*>(value.data()), sizeof(T) * size);
531}
533
536
537struct Metric
538{
539};
540
551template <class T, class DataSource, typename _DistanceType = T, typename IndexType = size_t>
552struct L1_Adaptor
553{
554 using ElementType = T;
555 using DistanceType = _DistanceType;
556
557 const DataSource& data_source;
558
559 L1_Adaptor(const DataSource& _data_source) : data_source(_data_source) {}
560
561 inline DistanceType evalMetric(
562 const T* NANOFLANN_RESTRICT a, const IndexType b_idx, size_t size) const
563 {
564 DistanceType result = DistanceType();
565 const size_t multof4 = (size >> 2) << 2; // largest multiple of 4
566 size_t d;
567
568 for (d = 0; d < multof4; d += 4)
569 {
570 const DistanceType diff0 = std::abs(a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0));
571 const DistanceType diff1 = std::abs(a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1));
572 const DistanceType diff2 = std::abs(a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2));
573 const DistanceType diff3 = std::abs(a[d + 3] - data_source.kdtree_get_pt(b_idx, d + 3));
574 /* Parentheses break dependency chain: */
575 result += (diff0 + diff1) + (diff2 + diff3);
576 }
577 /* Process last 0-3 components. Unrolled loop with fall-through switch.
578 */
579 switch (size - multof4)
580 {
581 case 3:
582 result += std::abs(a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2));
583 NANOFLANN_FALLTHROUGH;
584 case 2:
585 result += std::abs(a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1));
586 NANOFLANN_FALLTHROUGH;
587 case 1:
588 result += std::abs(a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0));
589 NANOFLANN_FALLTHROUGH;
590 case 0:
591 break;
592 }
593 return result;
594 }
595
596 template <typename U, typename V>
597 inline DistanceType accum_dist(const U a, const V b, const size_t) const
598 {
599 return std::abs(a - b);
600 }
601};
602
613template <class T, class DataSource, typename _DistanceType = T, typename IndexType = size_t>
614struct L2_Adaptor
615{
616 using ElementType = T;
617 using DistanceType = _DistanceType;
618
619 const DataSource& data_source;
620
621 L2_Adaptor(const DataSource& _data_source) : data_source(_data_source) {}
622
623 inline DistanceType evalMetric(
624 const T* NANOFLANN_RESTRICT a, const IndexType b_idx, size_t size) const
625 {
626 DistanceType result = DistanceType();
627 const size_t multof4 = (size >> 2) << 2; // largest multiple of 4
628 size_t d;
629
630 for (d = 0; d < multof4; d += 4)
631 {
632 const DistanceType diff0 = a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0);
633 const DistanceType diff1 = a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1);
634 const DistanceType diff2 = a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2);
635 const DistanceType diff3 = a[d + 3] - data_source.kdtree_get_pt(b_idx, d + 3);
636 /* Parentheses break dependency chain: */
637 result += (diff0 * diff0 + diff1 * diff1) + (diff2 * diff2 + diff3 * diff3);
638 }
639 /* Process last 0-3 components. Unrolled loop with fall-through switch.
640 */
641 DistanceType diff;
642 switch (size - multof4)
643 {
644 case 3:
645 diff = a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2);
646 result += diff * diff;
647 NANOFLANN_FALLTHROUGH;
648 case 2:
649 diff = a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1);
650 result += diff * diff;
651 NANOFLANN_FALLTHROUGH;
652 case 1:
653 diff = a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0);
654 result += diff * diff;
655 NANOFLANN_FALLTHROUGH;
656 case 0:
657 break;
658 }
659 return result;
660 }
661
662 template <typename U, typename V>
663 inline DistanceType accum_dist(const U a, const V b, const size_t) const
664 {
665 auto diff = a - b;
666 return diff * diff;
667 }
668};
669
680template <class T, class DataSource, typename _DistanceType = T, typename IndexType = size_t>
681struct L2_Simple_Adaptor
682{
683 using ElementType = T;
684 using DistanceType = _DistanceType;
685
686 const DataSource& data_source;
687
688 L2_Simple_Adaptor(const DataSource& _data_source) : data_source(_data_source) {}
689
690 inline DistanceType evalMetric(const T* a, const IndexType b_idx, size_t size) const
691 {
692 DistanceType result = DistanceType();
693 for (size_t i = 0; i < size; ++i)
694 {
695 const DistanceType diff = a[i] - data_source.kdtree_get_pt(b_idx, i);
696 result += diff * diff;
697 }
698 return result;
699 }
700
701 template <typename U, typename V>
702 inline DistanceType accum_dist(const U a, const V b, const size_t) const
703 {
704 auto diff = a - b;
705 return diff * diff;
706 }
707};
708
719template <class T, class DataSource, typename _DistanceType = T, typename IndexType = size_t>
720struct SO2_Adaptor
721{
722 using ElementType = T;
723 using DistanceType = _DistanceType;
724
725 const DataSource& data_source;
726
727 SO2_Adaptor(const DataSource& _data_source) : data_source(_data_source) {}
728
729 inline DistanceType evalMetric(const T* a, const IndexType b_idx, size_t size) const
730 {
731 return accum_dist(a[size - 1], data_source.kdtree_get_pt(b_idx, size - 1), size - 1);
732 }
733
739 template <typename U, typename V>
740 inline DistanceType accum_dist(const U a, const V b, const size_t) const
741 {
742 DistanceType diff = static_cast<DistanceType>(b) - static_cast<DistanceType>(a);
743 const DistanceType PI = pi_const<DistanceType>();
744 if (diff > PI)
745 diff -= 2 * PI;
746 else if (diff < -PI)
747 diff += 2 * PI;
748 return diff < DistanceType(0) ? -diff : diff; // abs without <cmath> dependency
749 }
750};
751
762template <class T, class DataSource, typename _DistanceType = T, typename IndexType = size_t>
763struct SO3_Adaptor
764{
765 using ElementType = T;
766 using DistanceType = _DistanceType;
767
769
770 SO3_Adaptor(const DataSource& _data_source) : distance_L2_Simple(_data_source) {}
771
772 inline DistanceType evalMetric(const T* a, const IndexType b_idx, size_t size) const
773 {
774 return distance_L2_Simple.evalMetric(a, b_idx, size);
775 }
776
777 template <typename U, typename V>
778 inline DistanceType accum_dist(const U a, const V b, const size_t idx) const
779 {
780 return distance_L2_Simple.accum_dist(a, b, idx);
781 }
782};
783
785struct metric_L1 : public Metric
786{
787 template <class T, class DataSource, typename IndexType = size_t>
788 struct traits
789 {
791 };
792};
793
795struct metric_L2 : public Metric
796{
797 template <class T, class DataSource, typename IndexType = size_t>
798 struct traits
799 {
801 };
802};
803
806{
807 template <class T, class DataSource, typename IndexType = size_t>
808 struct traits
809 {
811 };
812};
813
814struct metric_SO2 : public Metric
815{
816 template <class T, class DataSource, typename IndexType = size_t>
817 struct traits
818 {
820 };
821};
822
823struct metric_SO3 : public Metric
824{
825 template <class T, class DataSource, typename IndexType = size_t>
826 struct traits
827 {
829 };
830};
831
833
836
837enum class KDTreeSingleIndexAdaptorFlags
838{
839 None = 0,
840 SkipInitialBuildIndex = 1
841};
842
843inline std::underlying_type<KDTreeSingleIndexAdaptorFlags>::type operator&(
844 KDTreeSingleIndexAdaptorFlags lhs, KDTreeSingleIndexAdaptorFlags rhs)
845{
846 using underlying = typename std::underlying_type<KDTreeSingleIndexAdaptorFlags>::type;
847 return static_cast<underlying>(lhs) & static_cast<underlying>(rhs);
848}
849
852inline bool has_flag(KDTreeSingleIndexAdaptorFlags f, KDTreeSingleIndexAdaptorFlags flag)
853{
854 return (f & flag) != 0;
855}
856
858struct KDTreeSingleIndexAdaptorParams
859{
860 KDTreeSingleIndexAdaptorParams(
861 size_t _leaf_max_size = 10,
862 KDTreeSingleIndexAdaptorFlags _flags = KDTreeSingleIndexAdaptorFlags::None,
863 unsigned int _n_thread_build = 1)
864 : leaf_max_size(_leaf_max_size), flags(_flags), n_thread_build(_n_thread_build)
865 {
866 }
867
868 size_t leaf_max_size;
869 KDTreeSingleIndexAdaptorFlags flags;
870 unsigned int n_thread_build;
871};
872
874struct SearchParameters
875{
876 SearchParameters(float eps_ = 0, bool sorted_ = true) : eps(eps_), sorted(sorted_) {}
877
878 float eps;
879 bool sorted;
881};
882
883
886
902{
903 static constexpr size_t WORDSIZE = 16; // WORDSIZE must >= 8
904 static constexpr size_t BLOCKSIZE = 8192;
905
906 /* We maintain memory alignment to word boundaries by requiring that all
907 allocations be in multiples of the machine wordsize. */
908 /* Size of machine word in bytes. Must be power of 2. */
909 /* Minimum number of bytes requested at a time from the system. Must be
910 * multiple of WORDSIZE. */
911
912 using Size = size_t;
913
914 Size remaining_ = 0;
915 void* base_ = nullptr;
916 void* loc_ = nullptr;
917
918 void internal_init()
919 {
920 remaining_ = 0;
921 base_ = nullptr;
922 usedMemory = 0;
923 wastedMemory = 0;
924 }
925
926 public:
927 Size usedMemory = 0;
928 Size wastedMemory = 0;
929
933 PooledAllocator() { internal_init(); }
934
939
941 void free_all()
942 {
943 while (base_ != nullptr)
944 {
945 // Get pointer to prev block
946 void* prev = *(static_cast<void**>(base_));
947 ::free(base_);
948 base_ = prev;
949 }
950 internal_init();
951 }
952
957 void* allocateBytes(const size_t req_size)
958 {
959 /* Round size up to a multiple of wordsize. The following expression
960 only works for WORDSIZE that is a power of 2, by masking last bits
961 of incremented size to zero.
962 */
963 const Size size = (req_size + (WORDSIZE - 1)) & ~(WORDSIZE - 1);
964
965 /* Check whether a new block must be allocated. Note that the first
966 word of a block is reserved for a pointer to the previous block.
967 */
968 if (size > remaining_)
969 {
970 wastedMemory += remaining_;
971
972 /* Allocate new storage. */
973 const Size blocksize = size > BLOCKSIZE ? size + WORDSIZE : BLOCKSIZE + WORDSIZE;
974
975 // use the standard C malloc to allocate memory
976 void* m = ::malloc(blocksize);
977 if (!m)
978 {
979 throw std::bad_alloc();
980 }
981
982 /* Fill first word of new block with pointer to previous block. */
983 static_cast<void**>(m)[0] = base_;
984 base_ = m;
985
986 remaining_ = blocksize - WORDSIZE;
987 loc_ = static_cast<char*>(m) + WORDSIZE;
988 }
989 void* rloc = loc_;
990 loc_ = static_cast<char*>(loc_) + size;
991 remaining_ -= size;
992
993 usedMemory += size;
994
995 return rloc;
996 }
997
1005 template <typename T>
1006 T* allocate(const size_t count = 1)
1007 {
1008 T* mem = static_cast<T*>(this->allocateBytes(sizeof(T) * count));
1009 return mem;
1010 }
1011};
1012
1013
1016
1020template <int32_t DIM, typename T>
1022{
1023 using type = std::array<T, DIM>;
1024};
1025
1026template <typename T>
1027struct array_or_vector<-1, T>
1028{
1029 using type = std::vector<T>;
1030};
1031
1033
1048template <
1049 class Derived, typename Distance, class DatasetAdaptor, int32_t DIM = -1,
1050 typename index_t = uint32_t>
1052{
1053 public:
1056 void freeIndex(Derived& obj)
1057 {
1058 obj.pool_.free_all();
1059 obj.root_node_ = nullptr;
1060 obj.size_at_index_build_ = 0;
1061 }
1062
1063 using ElementType = typename Distance::ElementType;
1064 using DistanceType = typename Distance::DistanceType;
1065 using IndexType = index_t;
1066
1070 std::vector<IndexType> vAcc_;
1071
1072 using Offset = typename decltype(vAcc_)::size_type;
1073 using Size = typename decltype(vAcc_)::size_type;
1074 using Dimension = int32_t;
1075
1076 /*-------------------------------------------------------------------
1077 * Internal Data Structures
1078 *
1079 * "Node" below can be declared with alignas(N) to improve
1080 * cache friendliness and SIMD load/store performance.
1081 *
1082 * The optimal N depends on the underlying hardware:
1083 * + Intel x86-64: 16 for SSE, 32 for AVX/AVX2 and 64 for AVX-512
1084 * + NVIDIA Jetson: 16 for ARM + NEON and CUDA float4/
1085 * To avoid unnecessary padding, the smallest alignment
1086 * compatible with a platform's vector width should be chosen.
1087 * ------------------------------------------------------------------*/
1088 struct alignas(NANOFLANN_NODE_ALIGNMENT) Node
1089 {
1092 union
1093 {
1094 struct leaf
1095 {
1096 Offset left, right;
1097 } lr;
1098 struct nonleaf
1099 {
1100 Dimension divfeat;
1102 DistanceType divlow, divhigh;
1103 } sub;
1104 } node_type;
1105
1107 Node *child1 = nullptr, *child2 = nullptr;
1108 };
1109
1110 using NodePtr = Node*;
1111 using NodeConstPtr = const Node*;
1112
1114 {
1115 ElementType low, high;
1116 };
1117
1118 NodePtr root_node_ = nullptr;
1119
1120 Size leaf_max_size_ = 0;
1121
1125 Size size_ = 0;
1128 Dimension dim_ = 0;
1129
1132 using BoundingBox = typename array_or_vector<DIM, Interval>::type;
1133
1136 using distance_vector_t = typename array_or_vector<DIM, DistanceType>::type;
1137
1140
1149
1151 NANOFLANN_NODISCARD Size size(const Derived& obj) const noexcept { return obj.size_; }
1152
1157 NANOFLANN_NODISCARD Size veclen(const Derived& obj) const noexcept
1158 {
1159#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
1160 if constexpr (DIM > 0)
1161 {
1162 return DIM;
1163 }
1164 else
1165 {
1166 return obj.dim_;
1167 }
1168#else
1169 return DIM > 0 ? DIM : obj.dim_;
1170#endif
1171 }
1172
1174 ElementType dataset_get(const Derived& obj, IndexType element, Dimension component) const
1175 {
1176 return obj.dataset_.kdtree_get_pt(element, component);
1177 }
1178
1183 NANOFLANN_NODISCARD Size usedMemory(const Derived& obj) const
1184 {
1185 return obj.pool_.usedMemory + obj.pool_.wastedMemory +
1186 obj.dataset_.kdtree_get_point_count() *
1187 sizeof(IndexType); // pool memory and vind array memory
1188 }
1189
1194 const Derived& obj, Offset ind, Size count, Dimension element, ElementType& min_elem,
1195 ElementType& max_elem) const
1196 {
1197 min_elem = dataset_get(obj, vAcc_[ind], element);
1198 max_elem = min_elem;
1199 for (Offset i = 1; i < count; ++i)
1200 {
1201 ElementType val = dataset_get(obj, vAcc_[ind + i], element);
1202 if (val < min_elem) min_elem = val;
1203 if (val > max_elem) max_elem = val;
1204 }
1205 }
1206
1210 NANOFLANN_NODISCARD bool isActive(IndexType /*idx*/) const { return true; }
1211
1216 {
1217 Derived& obj = static_cast<Derived&>(*this);
1218 const Dimension dims = static_cast<Dimension>(veclen(obj));
1219 resize(bbox, dims);
1220 if (obj.dataset_.kdtree_get_bbox(bbox)) return;
1221 if (!size_)
1222 throw std::runtime_error(
1223 "[nanoflann] computeBoundingBox() called but "
1224 "no data points found.");
1225 for (Dimension i = 0; i < dims; ++i)
1226 bbox[i].low = bbox[i].high = dataset_get(obj, vAcc_[0], i);
1227 for (Offset k = 1; k < size_; ++k)
1228 for (Dimension i = 0; i < dims; ++i)
1229 {
1230 const auto val = dataset_get(obj, vAcc_[k], i);
1231 if (val < bbox[i].low) bbox[i].low = val;
1232 if (val > bbox[i].high) bbox[i].high = val;
1233 }
1234 }
1235
1244 template <class RESULTSET>
1246 RESULTSET& result_set, const ElementType* vec, const NodePtr node, DistanceType mindist,
1247 distance_vector_t& dists, const DistanceType epsError) const
1248 {
1249 const Derived& obj = static_cast<const Derived&>(*this);
1250 // If this is a leaf node, then do check and return.
1251 if (!node->child1) // (if one node is nullptr, both are)
1252 {
1253 // Hoist the point length out of the per-point loop. For a
1254 // fixed-size tree (DIM > 0) this is a compile-time constant; for a
1255 // runtime dimension it avoids re-reading obj.dim_ on every point.
1256 const Size dim = veclen(obj);
1257 for (Offset i = node->node_type.lr.left; i < node->node_type.lr.right; ++i)
1258 {
1259 const IndexType accessor = vAcc_[i];
1260 if (!obj.isActive(accessor)) continue;
1261 DistanceType dist = obj.distance_.evalMetric(vec, accessor, dim);
1262 if (dist < result_set.worstDist())
1263 {
1264 if (!result_set.addPoint(
1265 static_cast<typename RESULTSET::DistanceType>(dist),
1266 static_cast<typename RESULTSET::IndexType>(accessor)))
1267 return false;
1268 }
1269 }
1270 return true;
1271 }
1272
1273 /* Which child branch should be taken first? */
1274 Dimension idx = node->node_type.sub.divfeat;
1275 ElementType val = vec[idx];
1276 DistanceType diff1 = val - node->node_type.sub.divlow;
1277 DistanceType diff2 = val - node->node_type.sub.divhigh;
1278
1279 NodePtr bestChild;
1280 NodePtr otherChild;
1281 DistanceType cut_dist;
1282 if ((diff1 + diff2) < 0)
1283 {
1284 bestChild = node->child1;
1285 otherChild = node->child2;
1286 cut_dist = obj.distance_.accum_dist(val, node->node_type.sub.divhigh, idx);
1287 }
1288 else
1289 {
1290 bestChild = node->child2;
1291 otherChild = node->child1;
1292 cut_dist = obj.distance_.accum_dist(val, node->node_type.sub.divlow, idx);
1293 }
1294
1295 /* Call recursively to search next level down. */
1296 if (!searchLevel(result_set, vec, bestChild, mindist, dists, epsError)) return false;
1297
1298 DistanceType dst = dists[idx];
1299 mindist = mindist + cut_dist - dst;
1300 dists[idx] = cut_dist;
1301 if (mindist * epsError <= result_set.worstDist())
1302 {
1303 if (!searchLevel(result_set, vec, otherChild, mindist, dists, epsError)) return false;
1304 }
1305 dists[idx] = dst;
1306 return true;
1307 }
1308
1329 Derived& obj, NodePtr node, const Offset left, const Offset right, BoundingBox& bbox,
1330 Offset& idx, Dimension& cutfeat, DistanceType& cutval)
1331 {
1332 const Dimension dims = static_cast<Dimension>(veclen(obj));
1333
1334 /* If too few exemplars remain, then make this a leaf node. */
1335 if ((right - left) <= static_cast<Offset>(obj.leaf_max_size_))
1336 {
1337 node->child1 = node->child2 = nullptr; /* Mark as leaf node. */
1338 node->node_type.lr.left = left;
1339 node->node_type.lr.right = right;
1340
1341 // compute bounding-box of leaf points
1342 for (Dimension i = 0; i < dims; ++i)
1343 {
1344 bbox[i].low = dataset_get(obj, obj.vAcc_[left], i);
1345 bbox[i].high = dataset_get(obj, obj.vAcc_[left], i);
1346 }
1347 for (Offset k = left + 1; k < right; ++k)
1348 {
1349 for (Dimension i = 0; i < dims; ++i)
1350 {
1351 const auto val = dataset_get(obj, obj.vAcc_[k], i);
1352 if (bbox[i].low > val) bbox[i].low = val;
1353 if (bbox[i].high < val) bbox[i].high = val;
1354 }
1355 }
1356 return true;
1357 }
1358
1359 /* Determine the index, dimension and value for split plane */
1360 middleSplit_(obj, left, right - left, idx, cutfeat, cutval, bbox);
1361 node->node_type.sub.divfeat = cutfeat;
1362 return false;
1363 }
1364
1371 Derived& obj, NodePtr node, const Dimension cutfeat, const BoundingBox& left_bbox,
1372 const BoundingBox& right_bbox, BoundingBox& bbox)
1373 {
1374 node->node_type.sub.divlow = left_bbox[cutfeat].high;
1375 node->node_type.sub.divhigh = right_bbox[cutfeat].low;
1376
1377 const Dimension dims = static_cast<Dimension>(veclen(obj));
1378 for (Dimension i = 0; i < dims; ++i)
1379 {
1380 bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low);
1381 bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high);
1382 }
1383 }
1384
1385 NodePtr divideTree(Derived& obj, const Offset left, const Offset right, BoundingBox& bbox)
1386 {
1387 assert(static_cast<Size>(obj.vAcc_.at(left)) < obj.dataset_.kdtree_get_point_count());
1388
1389 NodePtr node = obj.pool_.template allocate<Node>(); // allocate memory
1390 Offset idx;
1391 Dimension cutfeat;
1392 DistanceType cutval;
1393 if (makeNode(obj, node, left, right, bbox, idx, cutfeat, cutval)) return node;
1394
1395 /* Recurse on left */
1396 BoundingBox left_bbox(bbox);
1397 left_bbox[cutfeat].high = cutval;
1398 node->child1 = this->divideTree(obj, left, left + idx, left_bbox);
1399
1400 /* Recurse on right */
1401 BoundingBox right_bbox(bbox);
1402 right_bbox[cutfeat].low = cutval;
1403 node->child2 = this->divideTree(obj, left + idx, right, right_bbox);
1404
1405 finalizeSplitNode(obj, node, cutfeat, left_bbox, right_bbox, bbox);
1406
1407 return node;
1408 }
1409
1423 Derived& obj, const Offset left, const Offset right, BoundingBox& bbox,
1424 std::atomic<unsigned int>& thread_count, std::mutex& mutex)
1425 {
1426 std::unique_lock<std::mutex> lock(mutex);
1427 NodePtr node = obj.pool_.template allocate<Node>(); // allocate memory
1428 lock.unlock();
1429
1430 Offset idx;
1431 Dimension cutfeat;
1432 DistanceType cutval;
1433 if (makeNode(obj, node, left, right, bbox, idx, cutfeat, cutval)) return node;
1434
1435 std::future<NodePtr> right_future;
1436
1437 /* Recurse on right concurrently, if possible */
1438
1439 BoundingBox right_bbox(bbox);
1440 right_bbox[cutfeat].low = cutval;
1441 if (++thread_count < n_thread_build_)
1442 {
1443 /* Concurrent thread for right recursion */
1444
1445 right_future = std::async(
1446 std::launch::async, &KDTreeBaseClass::divideTreeConcurrent, this, std::ref(obj),
1447 left + idx, right, std::ref(right_bbox), std::ref(thread_count), std::ref(mutex));
1448 }
1449 else
1450 {
1451 --thread_count;
1452 }
1453
1454 /* Recurse on left in this thread */
1455
1456 BoundingBox left_bbox(bbox);
1457 left_bbox[cutfeat].high = cutval;
1458 node->child1 =
1459 this->divideTreeConcurrent(obj, left, left + idx, left_bbox, thread_count, mutex);
1460
1461 if (right_future.valid())
1462 {
1463 /* Block and wait for concurrent right from above */
1464
1465 node->child2 = right_future.get();
1466 --thread_count;
1467 }
1468 else
1469 {
1470 /* Otherwise, recurse on right in this thread */
1471
1472 node->child2 =
1473 this->divideTreeConcurrent(obj, left + idx, right, right_bbox, thread_count, mutex);
1474 }
1475
1476 finalizeSplitNode(obj, node, cutfeat, left_bbox, right_bbox, bbox);
1477
1478 return node;
1479 }
1480
1481 void middleSplit_(
1482 const Derived& obj, const Offset ind, const Size count, Offset& index, Dimension& cutfeat,
1483 DistanceType& cutval, const BoundingBox& bbox)
1484 {
1485 const Dimension dims = static_cast<Dimension>(veclen(obj));
1486 const auto EPS = static_cast<DistanceType>(0.00001);
1487
1488 // Pre-compute max_span once
1489 ElementType max_span = bbox[0].high - bbox[0].low;
1490 for (Dimension i = 1; i < dims; ++i)
1491 {
1492 ElementType span = bbox[i].high - bbox[i].low;
1493 if (span > max_span) max_span = span;
1494 }
1495
1496 // Two-pass: first find max_span (done above), then scan candidate dims
1497 // inline — no heap allocation for a candidates vector.
1498 cutfeat = 0;
1499 ElementType max_spread = -1;
1500 ElementType min_elem = 0, max_elem = 0;
1501 const ElementType threshold = (1 - EPS) * max_span;
1502
1503 for (Dimension dim = 0; dim < dims; ++dim)
1504 {
1505 if (bbox[dim].high - bbox[dim].low < threshold) continue;
1506
1507 ElementType local_min = dataset_get(obj, vAcc_[ind], dim);
1508 ElementType local_max = local_min;
1509
1510 // Unrolled loop for better performance
1511 constexpr size_t UNROLL = 4;
1512 Offset k = 1;
1513 for (; k + UNROLL <= count; k += UNROLL)
1514 {
1515 ElementType v0 = dataset_get(obj, vAcc_[ind + k], dim);
1516 ElementType v1 = dataset_get(obj, vAcc_[ind + k + 1], dim);
1517 ElementType v2 = dataset_get(obj, vAcc_[ind + k + 2], dim);
1518 ElementType v3 = dataset_get(obj, vAcc_[ind + k + 3], dim);
1519
1520 local_min = std::min({local_min, v0, v1, v2, v3});
1521 local_max = std::max({local_max, v0, v1, v2, v3});
1522 }
1523
1524 // Handle remainder
1525 for (; k < count; ++k)
1526 {
1527 ElementType val = dataset_get(obj, vAcc_[ind + k], dim);
1528 local_min = std::min(local_min, val);
1529 local_max = std::max(local_max, val);
1530 }
1531
1532 ElementType spread = local_max - local_min;
1533 if (spread > max_spread)
1534 {
1535 cutfeat = dim;
1536 max_spread = spread;
1537 min_elem = local_min;
1538 max_elem = local_max;
1539 }
1540 }
1541
1542 // Median-of-three for better balance
1543 DistanceType split_val = (bbox[cutfeat].low + bbox[cutfeat].high) / 2;
1544 if (split_val < min_elem) split_val = min_elem;
1545 if (split_val > max_elem) split_val = max_elem;
1546
1547 cutval = split_val;
1548
1549 // Optimized partitioning
1550 Offset lim1, lim2;
1551 planeSplit(obj, ind, count, cutfeat, cutval, lim1, lim2);
1552
1553 index = (lim1 > count / 2) ? lim1 : (lim2 < count / 2) ? lim2 : count / 2;
1554 }
1555
1566 const Derived& obj, const Offset ind, const Size count, const Dimension cutfeat,
1567 const DistanceType& cutval, Offset& lim1, Offset& lim2)
1568 {
1569 // Dutch National Flag algorithm for three-way partitioning
1570 Offset left = 0;
1571 Offset mid = 0;
1572 Offset right = count - 1;
1573
1574 while (mid <= right)
1575 {
1576 ElementType val = dataset_get(obj, vAcc_[ind + mid], cutfeat);
1577
1578 if (val < cutval)
1579 {
1580 std::swap(vAcc_[ind + left], vAcc_[ind + mid]);
1581 left++;
1582 mid++;
1583 }
1584 else if (val > cutval)
1585 {
1586 std::swap(vAcc_[ind + mid], vAcc_[ind + right]);
1587 right--;
1588 }
1589 else
1590 {
1591 mid++;
1592 }
1593 }
1594
1595 lim1 = left;
1596 lim2 = mid;
1597 }
1598
1599 DistanceType computeInitialDistances(
1600 const Derived& obj, const ElementType* vec, distance_vector_t& dists) const
1601 {
1602 assert(vec);
1603 DistanceType dist = DistanceType();
1604
1605 const Dimension dims = static_cast<Dimension>(veclen(obj));
1606 for (Dimension i = 0; i < dims; ++i)
1607 {
1608 if (vec[i] < obj.root_bbox_[i].low)
1609 {
1610 dists[i] = obj.distance_.accum_dist(vec[i], obj.root_bbox_[i].low, i);
1611 dist += dists[i];
1612 }
1613 else if (vec[i] > obj.root_bbox_[i].high)
1614 {
1615 dists[i] = obj.distance_.accum_dist(vec[i], obj.root_bbox_[i].high, i);
1616 dist += dists[i];
1617 }
1618 }
1619 return dist;
1620 }
1621
1622 static void save_tree(const Derived& obj, std::ostream& stream, const NodeConstPtr tree)
1623 {
1624 save_value(stream, *tree);
1625 if (tree->child1 != nullptr)
1626 {
1627 save_tree(obj, stream, tree->child1);
1628 }
1629 if (tree->child2 != nullptr)
1630 {
1631 save_tree(obj, stream, tree->child2);
1632 }
1633 }
1634
1635 static void load_tree(Derived& obj, std::istream& stream, NodePtr& tree)
1636 {
1637 tree = obj.pool_.template allocate<Node>();
1638 load_value(stream, *tree);
1639 if (tree->child1 != nullptr)
1640 {
1641 load_tree(obj, stream, tree->child1);
1642 }
1643 if (tree->child2 != nullptr)
1644 {
1645 load_tree(obj, stream, tree->child2);
1646 }
1647 }
1648
1651 static constexpr uint32_t SAVE_MAGIC = 0x4E464C4E;
1652
1671 void saveIndex(const Derived& obj, std::ostream& stream) const
1672 {
1673 // 10-byte header: magic | version | sizeof_size_t | sizeof_IndexType
1674 // | sizeof_ElementType | sizeof_DistanceType
1675 // Use local copies: passing a static constexpr by const-ref ODR-uses it
1676 // in C++11/14, which requires an out-of-class definition we cannot provide
1677 // in a header-only library.
1678 const uint32_t hdr_magic = SAVE_MAGIC;
1679 const uint32_t hdr_version = static_cast<uint32_t>(NANOFLANN_VERSION);
1680 const uint8_t hdr_sz_st = static_cast<uint8_t>(sizeof(size_t));
1681 const uint8_t hdr_sz_idx = static_cast<uint8_t>(sizeof(IndexType));
1682 const uint8_t hdr_sz_elem = static_cast<uint8_t>(sizeof(ElementType));
1683 const uint8_t hdr_sz_dist = static_cast<uint8_t>(sizeof(DistanceType));
1684 save_value(stream, hdr_magic);
1685 save_value(stream, hdr_version);
1686 save_value(stream, hdr_sz_st);
1687 save_value(stream, hdr_sz_idx);
1688 save_value(stream, hdr_sz_elem);
1689 save_value(stream, hdr_sz_dist);
1690
1691 save_value(stream, obj.size_);
1692 save_value(stream, obj.dim_);
1693 save_value(stream, obj.root_bbox_);
1694 save_value(stream, obj.leaf_max_size_);
1695 save_value(stream, obj.vAcc_);
1696 if (obj.root_node_)
1697 {
1698 save_tree(obj, stream, obj.root_node_);
1699 }
1700 }
1701
1717 void loadIndex(Derived& obj, std::istream& stream)
1718 {
1719 // Validate header
1720 uint32_t magic = 0;
1721 load_value(stream, magic);
1722 if (stream.fail() || magic != SAVE_MAGIC)
1723 {
1724 throw std::runtime_error(
1725 "nanoflann loadIndex: invalid file (wrong magic number). "
1726 "The stream was not written by nanoflann saveIndex().");
1727 }
1728
1729 uint32_t file_version = 0;
1730 load_value(stream, file_version);
1731 if (file_version != static_cast<uint32_t>(NANOFLANN_VERSION))
1732 {
1733 char msg[200];
1734 snprintf(
1735 msg, sizeof(msg),
1736 "nanoflann loadIndex: version mismatch "
1737 "(file=0x%03X, library=0x%03X). Rebuild the index.",
1738 file_version, static_cast<unsigned>(NANOFLANN_VERSION));
1739 throw std::runtime_error(msg);
1740 }
1741
1742 uint8_t sz_size_t = 0;
1743 uint8_t sz_idx = 0;
1744 uint8_t sz_elem = 0;
1745 uint8_t sz_dist = 0;
1746 load_value(stream, sz_size_t);
1747 load_value(stream, sz_idx);
1748 load_value(stream, sz_elem);
1749 load_value(stream, sz_dist);
1750 if (sz_size_t != static_cast<uint8_t>(sizeof(size_t)) ||
1751 sz_idx != static_cast<uint8_t>(sizeof(IndexType)) ||
1752 sz_elem != static_cast<uint8_t>(sizeof(ElementType)) ||
1753 sz_dist != static_cast<uint8_t>(sizeof(DistanceType)))
1754 {
1755 throw std::runtime_error(
1756 "nanoflann loadIndex: type-size mismatch between saved index and "
1757 "current template instantiation (sizeof size_t / IndexType / "
1758 "ElementType / DistanceType differ). Rebuild the index.");
1759 }
1760
1761 load_value(stream, obj.size_);
1762 load_value(stream, obj.dim_);
1763 load_value(stream, obj.root_bbox_);
1764 load_value(stream, obj.leaf_max_size_);
1765 load_value(stream, obj.vAcc_);
1766
1767 if (obj.size_ > 0)
1768 {
1769 load_tree(obj, stream, obj.root_node_);
1770 }
1771
1772 if (stream.fail())
1773 {
1774 throw std::runtime_error(
1775 "nanoflann loadIndex: unexpected end of stream or read error.");
1776 }
1777 }
1778};
1779
1782
1833template <typename Distance, class DatasetAdaptor, int32_t DIM = -1, typename index_t = uint32_t>
1835 : public KDTreeBaseClass<
1836 KDTreeSingleIndexAdaptor<Distance, DatasetAdaptor, DIM, index_t>, Distance,
1837 DatasetAdaptor, DIM, index_t>
1838{
1839 public:
1843
1845 const DatasetAdaptor& dataset_;
1846
1847 const KDTreeSingleIndexAdaptorParams indexParams;
1848
1849 Distance distance_;
1850
1851 using Base = typename nanoflann::KDTreeBaseClass<
1853 DatasetAdaptor, DIM, index_t>;
1854
1855 using Offset = typename Base::Offset;
1856 using Size = typename Base::Size;
1857 using Dimension = typename Base::Dimension;
1858
1859 using ElementType = typename Base::ElementType;
1860 using DistanceType = typename Base::DistanceType;
1861 using IndexType = typename Base::IndexType;
1862
1863 using Node = typename Base::Node;
1864 using NodePtr = Node*;
1865
1866 using Interval = typename Base::Interval;
1867
1870 using BoundingBox = typename Base::BoundingBox;
1871
1874 using distance_vector_t = typename Base::distance_vector_t;
1875
1896 template <class... Args>
1898 const Dimension dimensionality, const DatasetAdaptor& inputData,
1899 const KDTreeSingleIndexAdaptorParams& params, Args&&... args)
1900 : dataset_(inputData),
1901 indexParams(params),
1902 distance_(inputData, std::forward<Args>(args)...)
1903 {
1904 init(dimensionality, params);
1905 }
1906
1907 explicit KDTreeSingleIndexAdaptor(
1908 const Dimension dimensionality, const DatasetAdaptor& inputData,
1909 const KDTreeSingleIndexAdaptorParams& params = {})
1910 : dataset_(inputData), indexParams(params), distance_(inputData)
1911 {
1912 init(dimensionality, params);
1913 }
1914
1915 private:
1916 void init(const Dimension dimensionality, const KDTreeSingleIndexAdaptorParams& params)
1917 {
1918 Base::size_ = dataset_.kdtree_get_point_count();
1919 Base::size_at_index_build_ = Base::size_;
1920 Base::dim_ = dimensionality;
1921 if (DIM > 0) Base::dim_ = DIM;
1922 Base::leaf_max_size_ = params.leaf_max_size;
1923 if (params.n_thread_build > 0)
1924 {
1925 Base::n_thread_build_ = params.n_thread_build;
1926 }
1927 else
1928 {
1929 Base::n_thread_build_ = std::max(std::thread::hardware_concurrency(), 1u);
1930 }
1931
1932 if (!has_flag(params.flags, KDTreeSingleIndexAdaptorFlags::SkipInitialBuildIndex))
1933 {
1934 // Build KD-tree:
1935 buildIndex();
1936 }
1937 }
1938
1939 public:
1944 {
1945 Base::size_ = dataset_.kdtree_get_point_count();
1946 Base::size_at_index_build_ = Base::size_;
1947 init_vind();
1948 this->freeIndex(*this);
1949 Base::size_at_index_build_ = Base::size_;
1950 if (Base::size_ == 0) return;
1951 this->computeBoundingBox(Base::root_bbox_);
1952 // construct the tree
1953 if (Base::n_thread_build_ == 1)
1954 {
1955 Base::root_node_ = this->divideTree(*this, 0, Base::size_, Base::root_bbox_);
1956 }
1957 else
1958 {
1959#ifndef NANOFLANN_NO_THREADS
1960 std::atomic<unsigned int> thread_count(0u);
1961 std::mutex mutex;
1962 Base::root_node_ = this->divideTreeConcurrent(
1963 *this, 0, Base::size_, Base::root_bbox_, thread_count, mutex);
1964#else /* NANOFLANN_NO_THREADS */
1965 throw std::runtime_error("Multithreading is disabled");
1966#endif /* NANOFLANN_NO_THREADS */
1967 }
1968 }
1969
1972
1989 template <typename RESULTSET>
1991 RESULTSET& result, const ElementType* vec, const SearchParameters& searchParams = {}) const
1992 {
1993 assert(vec);
1994 if (this->size(*this) == 0) return false;
1995 if (!Base::root_node_)
1996 throw std::runtime_error(
1997 "[nanoflann] findNeighbors() called before building the "
1998 "index.");
1999 DistanceType epsError = 1 + static_cast<DistanceType>(searchParams.eps);
2000
2001 // fixed or variable-sized container (depending on DIM)
2002 distance_vector_t dists;
2003 // Fill it with zeros.
2004 auto zero = static_cast<typename RESULTSET::DistanceType>(0);
2005 assign(dists, this->veclen(*this), zero);
2006 DistanceType dist = this->computeInitialDistances(*this, vec, dists);
2007 this->searchLevel(result, vec, Base::root_node_, dist, dists, epsError);
2008
2009 if (searchParams.sorted) result.sort();
2010
2011 return result.full();
2012 }
2013
2029 template <typename RESULTSET>
2030 NANOFLANN_NODISCARD Size findWithinBox(RESULTSET& result, const BoundingBox& bbox) const
2031 {
2032 if (this->size(*this) == 0) return 0;
2033 if (!Base::root_node_)
2034 throw std::runtime_error(
2035 "[nanoflann] findWithinBox() called before building the "
2036 "index.");
2037
2038 std::stack<NodePtr> stack;
2039 stack.push(Base::root_node_);
2040
2041 while (!stack.empty())
2042 {
2043 const NodePtr node = stack.top();
2044 stack.pop();
2045
2046 // If this is a leaf node, then do check and return.
2047 if (!node->child1) // (if one node is nullptr, both are)
2048 {
2049 for (Offset i = node->node_type.lr.left; i < node->node_type.lr.right; ++i)
2050 {
2051 if (contains(bbox, Base::vAcc_[i]))
2052 {
2053 if (!result.addPoint(0, Base::vAcc_[i]))
2054 {
2055 // the resultset doesn't want to receive any more
2056 // points, we're done searching!
2057 return result.size();
2058 }
2059 }
2060 }
2061 }
2062 else
2063 {
2064 const Dimension idx = node->node_type.sub.divfeat;
2065 const auto low_bound = node->node_type.sub.divlow;
2066 const auto high_bound = node->node_type.sub.divhigh;
2067
2068 if (bbox[idx].low <= low_bound) stack.push(node->child1);
2069 if (bbox[idx].high >= high_bound) stack.push(node->child2);
2070 }
2071 }
2072
2073 return result.size();
2074 }
2075
2091 NANOFLANN_NODISCARD Size knnSearch(
2092 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
2093 DistanceType* out_distances) const
2094 {
2096 resultSet.init(out_indices, out_distances);
2097 findNeighbors(resultSet, query_point);
2098 return resultSet.size();
2099 }
2100
2120 NANOFLANN_NODISCARD Size radiusSearch(
2121 const ElementType* query_point, const DistanceType& radius,
2122 std::vector<ResultItem<IndexType, DistanceType>>& IndicesDists,
2123 const SearchParameters& searchParams = {}) const
2124 {
2125 RadiusResultSet<DistanceType, IndexType> resultSet(radius, IndicesDists);
2126 const Size nFound = radiusSearchCustomCallback(query_point, resultSet, searchParams);
2127 return nFound;
2128 }
2129
2135 template <class SEARCH_CALLBACK>
2136 NANOFLANN_NODISCARD Size radiusSearchCustomCallback(
2137 const ElementType* query_point, SEARCH_CALLBACK& resultSet,
2138 const SearchParameters& searchParams = {}) const
2139 {
2140 findNeighbors(resultSet, query_point, searchParams);
2141 return resultSet.size();
2142 }
2143
2159 NANOFLANN_NODISCARD Size rknnSearch(
2160 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
2161 DistanceType* out_distances, const DistanceType& radius) const
2162 {
2163 nanoflann::RKNNResultSet<DistanceType, IndexType> resultSet(num_closest, radius);
2164 resultSet.init(out_indices, out_distances);
2165 findNeighbors(resultSet, query_point);
2166 return resultSet.size();
2167 }
2168
2170
2171 public:
2175 {
2176 // Create a permutable array of indices to the input vectors.
2177 Base::size_ = dataset_.kdtree_get_point_count();
2178 if (Base::vAcc_.size() != Base::size_) Base::vAcc_.resize(Base::size_);
2179 for (IndexType i = 0; i < static_cast<IndexType>(Base::size_); i++) Base::vAcc_[i] = i;
2180 }
2181
2182 bool contains(const BoundingBox& bbox, IndexType idx) const
2183 {
2184 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
2185 for (Dimension i = 0; i < dims; ++i)
2186 {
2187 const auto point = this->dataset_.kdtree_get_pt(idx, i);
2188 if (point < bbox[i].low || point > bbox[i].high) return false;
2189 }
2190 return true;
2191 }
2192
2193 public:
2199 void saveIndex(std::ostream& stream) const { Base::saveIndex(*this, stream); }
2200
2206 void loadIndex(std::istream& stream) { Base::loadIndex(*this, stream); }
2207
2208}; // class KDTree
2209
2247template <typename Distance, class DatasetAdaptor, int32_t DIM = -1, typename IndexType = uint32_t>
2249 : public KDTreeBaseClass<
2250 KDTreeSingleIndexDynamicAdaptor_<Distance, DatasetAdaptor, DIM, IndexType>, Distance,
2251 DatasetAdaptor, DIM, IndexType>
2252{
2253 public:
2257 const DatasetAdaptor& dataset_;
2258
2259 KDTreeSingleIndexAdaptorParams index_params_;
2260
2261 std::vector<int>& treeIndex_;
2262
2263 Distance distance_;
2264
2265 using Base = typename nanoflann::KDTreeBaseClass<
2267 Distance, DatasetAdaptor, DIM, IndexType>;
2268
2269 using ElementType = typename Base::ElementType;
2270 using DistanceType = typename Base::DistanceType;
2271
2272 using Offset = typename Base::Offset;
2273 using Size = typename Base::Size;
2274 using Dimension = typename Base::Dimension;
2275
2276 using Node = typename Base::Node;
2277 using NodePtr = Node*;
2278
2279 using Interval = typename Base::Interval;
2282 using BoundingBox = typename Base::BoundingBox;
2283
2286 using distance_vector_t = typename Base::distance_vector_t;
2287
2289 NANOFLANN_NODISCARD bool isActive(IndexType idx) const { return treeIndex_[idx] != -1; }
2290
2307 const Dimension dimensionality, const DatasetAdaptor& inputData,
2308 std::vector<int>& treeIndex,
2310 : dataset_(inputData), index_params_(params), treeIndex_(treeIndex), distance_(inputData)
2311 {
2312 Base::size_ = 0;
2313 Base::size_at_index_build_ = 0;
2314 for (auto& v : Base::root_bbox_) v = {};
2315 Base::dim_ = dimensionality;
2316 if (DIM > 0) Base::dim_ = DIM;
2317 Base::leaf_max_size_ = params.leaf_max_size;
2318 if (params.n_thread_build > 0)
2319 {
2320 Base::n_thread_build_ = params.n_thread_build;
2321 }
2322 else
2323 {
2324 Base::n_thread_build_ = std::max(std::thread::hardware_concurrency(), 1u);
2325 }
2326 }
2327
2330
2333 {
2334 if (this == &rhs) return *this;
2336 std::swap(Base::vAcc_, tmp.Base::vAcc_);
2337 std::swap(Base::leaf_max_size_, tmp.Base::leaf_max_size_);
2338 std::swap(index_params_, tmp.index_params_);
2339 // treeIndex_ is a reference member and cannot be rebound; do not swap.
2340 std::swap(Base::size_, tmp.Base::size_);
2341 std::swap(Base::size_at_index_build_, tmp.Base::size_at_index_build_);
2342 std::swap(Base::root_node_, tmp.Base::root_node_);
2343 std::swap(Base::root_bbox_, tmp.Base::root_bbox_);
2344 std::swap(Base::pool_, tmp.Base::pool_);
2345 return *this;
2346 }
2347
2352 {
2353 Base::size_ = Base::vAcc_.size();
2354 this->freeIndex(*this);
2355 Base::size_at_index_build_ = Base::size_;
2356 if (Base::size_ == 0) return;
2357 this->computeBoundingBox(Base::root_bbox_);
2358 // construct the tree
2359 if (Base::n_thread_build_ == 1)
2360 {
2361 Base::root_node_ = this->divideTree(*this, 0, Base::size_, Base::root_bbox_);
2362 }
2363 else
2364 {
2365#ifndef NANOFLANN_NO_THREADS
2366 std::atomic<unsigned int> thread_count(0u);
2367 std::mutex mutex;
2368 Base::root_node_ = this->divideTreeConcurrent(
2369 *this, 0, Base::size_, Base::root_bbox_, thread_count, mutex);
2370#else /* NANOFLANN_NO_THREADS */
2371 throw std::runtime_error("Multithreading is disabled");
2372#endif /* NANOFLANN_NO_THREADS */
2373 }
2374 }
2375
2378
2399 template <typename RESULTSET>
2401 RESULTSET& result, const ElementType* vec, const SearchParameters& searchParams = {}) const
2402 {
2403 assert(vec);
2404 if (this->size(*this) == 0) return false;
2405 if (!Base::root_node_) return false;
2406 DistanceType epsError = 1 + static_cast<DistanceType>(searchParams.eps);
2407
2408 // fixed or variable-sized container (depending on DIM)
2409 distance_vector_t dists;
2410 // Fill it with zeros.
2411 assign(dists, this->veclen(*this), static_cast<typename distance_vector_t::value_type>(0));
2412 DistanceType dist = this->computeInitialDistances(*this, vec, dists);
2413 this->searchLevel(result, vec, Base::root_node_, dist, dists, epsError);
2414
2415 if (searchParams.sorted) result.sort();
2416
2417 return result.full();
2418 }
2419
2434 NANOFLANN_NODISCARD Size knnSearch(
2435 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
2436 DistanceType* out_distances, const SearchParameters& searchParams = {}) const
2437 {
2439 resultSet.init(out_indices, out_distances);
2440 findNeighbors(resultSet, query_point, searchParams);
2441 return resultSet.size();
2442 }
2443
2463 NANOFLANN_NODISCARD Size radiusSearch(
2464 const ElementType* query_point, const DistanceType& radius,
2465 std::vector<ResultItem<IndexType, DistanceType>>& IndicesDists,
2466 const SearchParameters& searchParams = {}) const
2467 {
2468 RadiusResultSet<DistanceType, IndexType> resultSet(radius, IndicesDists);
2469 const Size nFound = radiusSearchCustomCallback(query_point, resultSet, searchParams);
2470 return nFound;
2471 }
2472
2478 template <class SEARCH_CALLBACK>
2479 NANOFLANN_NODISCARD Size radiusSearchCustomCallback(
2480 const ElementType* query_point, SEARCH_CALLBACK& resultSet,
2481 const SearchParameters& searchParams = {}) const
2482 {
2483 findNeighbors(resultSet, query_point, searchParams);
2484 return resultSet.size();
2485 }
2486
2488
2489 public:
2490 public:
2496 void saveIndex(std::ostream& stream) { Base::saveIndex(*this, stream); }
2497
2503 void loadIndex(std::istream& stream) { Base::loadIndex(*this, stream); }
2504};
2505
2520template <typename Distance, class DatasetAdaptor, int32_t DIM = -1, typename IndexType = uint32_t>
2522{
2523 public:
2524 using ElementType = typename Distance::ElementType;
2525 using DistanceType = typename Distance::DistanceType;
2526
2527 using Offset = typename KDTreeSingleIndexDynamicAdaptor_<Distance, DatasetAdaptor, DIM>::Offset;
2528 using Size = typename KDTreeSingleIndexDynamicAdaptor_<Distance, DatasetAdaptor, DIM>::Size;
2529 using Dimension =
2530 typename KDTreeSingleIndexDynamicAdaptor_<Distance, DatasetAdaptor, DIM>::Dimension;
2531
2532 protected:
2533 Size leaf_max_size_;
2534 Size treeCount_;
2535 Size pointCount_;
2536
2540 const DatasetAdaptor& dataset_;
2541
2544 std::vector<int> treeIndex_;
2548 std::unordered_map<IndexType, int> removedPoints_;
2549
2550 KDTreeSingleIndexAdaptorParams index_params_;
2551
2552 Dimension dim_;
2553
2554 using index_container_t =
2556 std::vector<index_container_t> index_;
2557
2558 public:
2561 const std::vector<index_container_t>& getAllIndices() const { return index_; }
2562
2563 private:
2565 int First0Bit(Size num)
2566 {
2567 int pos = 0;
2568 while (num & 1)
2569 {
2570 num = num >> 1;
2571 pos++;
2572 }
2573 return pos;
2574 }
2575
2577 void init()
2578 {
2579 using my_kd_tree_t =
2580 KDTreeSingleIndexDynamicAdaptor_<Distance, DatasetAdaptor, DIM, IndexType>;
2581 std::vector<my_kd_tree_t> index(
2582 treeCount_, my_kd_tree_t(dim_ /*dim*/, dataset_, treeIndex_, index_params_));
2583 index_ = index;
2584 }
2585
2586 public:
2587 Distance distance_;
2588
2605 const int dimensionality, const DatasetAdaptor& inputData,
2607 const size_t maximumPointCount = 1000000000U)
2608 : dataset_(inputData), index_params_(params), distance_(inputData)
2609 {
2610 treeCount_ = static_cast<size_t>(std::log2(maximumPointCount)) + 1;
2611 pointCount_ = 0U;
2612 dim_ = dimensionality;
2613 treeIndex_.clear();
2614 if (DIM > 0) dim_ = DIM;
2615 leaf_max_size_ = params.leaf_max_size;
2616 init();
2617 const size_t num_initial_points = dataset_.kdtree_get_point_count();
2618 if (num_initial_points > 0)
2619 {
2620 addPoints(0, static_cast<IndexType>(num_initial_points - 1));
2621 }
2622 }
2623
2627
2629 void addPoints(IndexType start, IndexType end)
2630 {
2631 int maxIndex = 0;
2632 for (IndexType idx = start; idx <= end; idx++)
2633 {
2634 // If this index was previously removed, its point is still
2635 // physically present in its sub-tree (removal is lazy and never
2636 // deletes from vAcc_). Just clear the "removed" mark and restore its
2637 // tree index. Re-inserting it would create a duplicate entry that
2638 // grows the trees without bound and yields duplicate search results.
2639 const auto it = removedPoints_.find(idx);
2640 if (it != removedPoints_.end())
2641 {
2642 treeIndex_[idx] = it->second;
2643 removedPoints_.erase(it);
2644 continue;
2645 }
2646
2647 const int pos = First0Bit(pointCount_);
2648 maxIndex = std::max(pos, maxIndex);
2649 if (treeIndex_.size() <= static_cast<size_t>(pointCount_))
2650 treeIndex_.resize(static_cast<size_t>(pointCount_) + 1);
2651 treeIndex_[pointCount_] = pos;
2652
2653 for (int i = 0; i < pos; i++)
2654 {
2655 for (size_t j = 0; j < index_[i].vAcc_.size(); j++)
2656 {
2657 const IndexType e = index_[i].vAcc_[j];
2658 index_[pos].vAcc_.push_back(e);
2659 if (treeIndex_[e] != -1)
2660 treeIndex_[e] = pos;
2661 else
2662 removedPoints_[e] = pos; // keep tombstone's tree index current
2663 }
2664 index_[i].vAcc_.clear();
2665 }
2666 index_[pos].vAcc_.push_back(idx);
2667 pointCount_++;
2668 }
2669
2670 for (int i = 0; i <= maxIndex; ++i)
2671 {
2672 index_[i].freeIndex(index_[i]);
2673 if (!index_[i].vAcc_.empty()) index_[i].buildIndex();
2674 }
2675 }
2676
2678 void removePoint(size_t idx)
2679 {
2680 if (idx >= pointCount_) return;
2681 if (treeIndex_[idx] == -1) return; // already removed
2682 // Remember which sub-tree still physically holds this point, so it can
2683 // be reactivated in place if re-added later (see addPoints).
2684 removedPoints_[static_cast<IndexType>(idx)] = treeIndex_[idx];
2685 treeIndex_[idx] = -1;
2686 }
2687
2704 template <typename RESULTSET>
2706 RESULTSET& result, const ElementType* vec, const SearchParameters& searchParams = {}) const
2707 {
2708 for (size_t i = 0; i < treeCount_; i++)
2709 {
2710 index_[i].findNeighbors(result, &vec[0], searchParams);
2711 }
2712 return result.full();
2713 }
2714};
2715
2728struct KDTreeIncrementalIndexParams
2729{
2730 KDTreeIncrementalIndexParams(float alpha_balance_ = 0.75f, float alpha_deleted_ = 0.5f)
2731 : alpha_balance(alpha_balance_), alpha_deleted(alpha_deleted_)
2732 {
2733 }
2734
2735 float alpha_balance;
2736 float alpha_deleted;
2737};
2738
2773template <typename Distance, class DatasetAdaptor, int32_t DIM = -1, typename IndexType = uint32_t>
2775 : public KDTreeBaseClass<
2776 KDTreeSingleIndexIncrementalAdaptor<Distance, DatasetAdaptor, DIM, IndexType>, Distance,
2777 DatasetAdaptor, DIM, IndexType>
2778{
2779 public:
2780 using Base = typename nanoflann::KDTreeBaseClass<
2782 DatasetAdaptor, DIM, IndexType>;
2783
2784 using ElementType = typename Base::ElementType;
2785 using DistanceType = typename Base::DistanceType;
2786
2787 using Offset = typename Base::Offset;
2788 using Size = typename Base::Size;
2789 using Dimension = typename Base::Dimension;
2790
2791 using Interval = typename Base::Interval;
2792 using BoundingBox = typename Base::BoundingBox;
2793 using distance_vector_t = typename Base::distance_vector_t;
2794
2796 const DatasetAdaptor& dataset_;
2797
2798 Distance distance_;
2799
2802 struct INode
2803 {
2804 IndexType ptIdx = 0;
2805 Dimension divfeat = 0;
2806 bool deleted = false;
2807 bool treeDeleted = false;
2808 INode* child1 = nullptr;
2809 INode* child2 = nullptr;
2810 INode* parent = nullptr;
2811 Size subtree_size = 0;
2812 Size invalid_count = 0;
2813 BoundingBox box;
2818 typename array_or_vector<DIM, ElementType>::type pcoord;
2819 };
2820
2825#if defined(NANOFLANN_INCREMENTAL_NO_COORD_CACHE)
2826 static constexpr bool kCacheCoords = false;
2827#else
2828 static constexpr bool kCacheCoords = (DIM > 0);
2829#endif
2830
2831 private:
2832 INode* iroot_ = nullptr;
2833 INode* freeList_ = nullptr;
2834
2835 Size liveCount_ = 0;
2836 Size totalCount_ = 0;
2837
2838 float alphaBal_ = 0.75f;
2839 float alphaDel_ = 0.5f;
2841 static constexpr Size kMinBalanceRebuild = 4;
2844 static constexpr double kBulkInsertFraction = 0.5;
2845
2847 INode* pendingRebuild_ = nullptr;
2848
2853 bool inlineRebuild_ = true;
2854
2856 std::vector<INode*> nodeOfPoint_;
2857
2859 std::vector<IndexType> buildBuf_;
2860
2862 bool collectRemoved_ = false;
2863 std::vector<IndexType> removedSink_;
2864
2865 public:
2875 const Dimension dimensionality, const DatasetAdaptor& inputData,
2876 const KDTreeIncrementalIndexParams& params = {})
2877 : dataset_(inputData), distance_(inputData)
2878 {
2879 Base::dim_ = dimensionality;
2880 if (DIM > 0) Base::dim_ = DIM;
2881 alphaBal_ = params.alpha_balance;
2882 alphaDel_ = params.alpha_deleted;
2883 resize(Base::root_bbox_, static_cast<Dimension>(this->veclen(*this)));
2884 }
2885
2889 delete;
2890
2891 ~KDTreeSingleIndexIncrementalAdaptor() { destroyNodeObjects(); }
2892
2895
2897 void addPoint(IndexType idx)
2898 {
2899 ensureNodeMap(idx);
2900 insertOne(idx);
2901 syncRootBox();
2902 }
2903
2912 void addPoints(IndexType start, IndexType end)
2913 {
2914 if (end < start) return;
2915 ensureNodeMap(end);
2916 const Size batch = static_cast<Size>(end - start) + 1;
2917 if (!iroot_ ||
2918 static_cast<double>(batch) >= kBulkInsertFraction * static_cast<double>(liveCount_))
2919 {
2920 buildBuf_.clear();
2921 if (iroot_) collectLiveAndFree(iroot_, buildBuf_); // keep existing live points
2922 for (IndexType idx = start; idx <= end; ++idx) buildBuf_.push_back(idx);
2923 iroot_ = buildBalanced(buildBuf_, 0, buildBuf_.size(), 0, nullptr);
2924 liveCount_ = buildBuf_.size();
2925 totalCount_ = liveCount_;
2926 }
2927 else
2928 {
2929 for (IndexType idx = start; idx <= end; ++idx) insertOne(idx);
2930 }
2931 syncRootBox();
2932 }
2933
2935 void removePoint(IndexType idx)
2936 {
2937 if (idx >= nodeOfPoint_.size()) return;
2938 INode* n = nodeOfPoint_[idx];
2939 if (!n || n->deleted) return;
2940 // A point can also be logically dead via a treeDeleted ancestor (a
2941 // lazily-killed box region). Detect that and treat it as already gone.
2942 for (INode* p = n->parent; p; p = p->parent)
2943 if (p->treeDeleted) return;
2944
2945 n->deleted = true;
2946 for (INode* p = n; p; p = p->parent) ++p->invalid_count;
2947 --liveCount_;
2948 maybeRebuildForDeletion();
2949 syncRootBox();
2950 }
2951
2953 void removeBox(const BoundingBox& box)
2954 {
2955 if (iroot_) removeBoxRec(iroot_, box);
2956 maybeRebuildForDeletion();
2957 syncRootBox();
2958 }
2959
2962 void removeOutsideBox(const BoundingBox& keep)
2963 {
2964 if (iroot_) removeOutsideBoxRec(iroot_, keep);
2965 maybeRebuildForDeletion();
2966 syncRootBox();
2967 }
2968
2971 void setCollectRemovedPoints(bool enable)
2972 {
2973 collectRemoved_ = enable;
2974 if (!enable) std::vector<IndexType>().swap(removedSink_);
2975 }
2976
2979 std::vector<IndexType> acquireRemovedPoints()
2980 {
2981 std::vector<IndexType> out;
2982 out.swap(removedSink_);
2983 return out;
2984 }
2985
2989 void setInlineRebuild(bool enable) { inlineRebuild_ = enable; }
2990
2993 void snapshotLiveIndices(std::vector<IndexType>& out) const { snapshotRec(iroot_, out); }
2994
2998 void collectPhysicalIndices(std::vector<IndexType>& out) const { collectAllRec(iroot_, out); }
2999
3002 NANOFLANN_NODISCARD bool referencesIndex(IndexType idx) const
3003 {
3004 return idx < nodeOfPoint_.size() && nodeOfPoint_[idx] != nullptr;
3005 }
3006
3009 void buildFromIndices(const std::vector<IndexType>& idxs)
3010 {
3011 // Validate (and grow the point->node map) before touching the current
3012 // tree, so a rejected index list leaves the index untouched.
3013 IndexType maxIdx = 0;
3014 for (IndexType v : idxs) maxIdx = std::max(maxIdx, v);
3015 if (!idxs.empty()) ensureNodeMap(maxIdx);
3016
3017 if (iroot_)
3018 {
3019 buildBuf_.clear();
3020 collectLiveAndFree(iroot_, buildBuf_); // recycle existing nodes
3021 iroot_ = nullptr;
3022 }
3023 buildBuf_.assign(idxs.begin(), idxs.end());
3024 iroot_ = buildBalanced(buildBuf_, 0, buildBuf_.size(), 0, nullptr);
3025 liveCount_ = buildBuf_.size();
3026 totalCount_ = liveCount_;
3027 syncRootBox();
3028 }
3029
3031
3034
3036 NANOFLANN_NODISCARD Size size() const noexcept { return liveCount_; }
3037 NANOFLANN_NODISCARD bool empty() const noexcept { return liveCount_ == 0; }
3038
3040 NANOFLANN_NODISCARD Size physicalSize() const noexcept { return totalCount_; }
3041
3043 NANOFLANN_NODISCARD Size usedMemory() const
3044 {
3045 return Base::pool_.usedMemory + Base::pool_.wastedMemory +
3046 nodeOfPoint_.capacity() * sizeof(INode*);
3047 }
3048
3052 NANOFLANN_NODISCARD BoundingBox boundingBox() const { return Base::root_bbox_; }
3053
3056 void reserve(Size n)
3057 {
3058 nodeOfPoint_.reserve(n);
3059 buildBuf_.reserve(n);
3060 }
3061
3063
3066
3068 template <typename RESULTSET>
3070 RESULTSET& result, const ElementType* vec, const SearchParameters& searchParams = {}) const
3071 {
3072 assert(vec);
3073 if (!iroot_ || liveCount_ == 0) return false;
3074 const DistanceType epsError = 1 + static_cast<DistanceType>(searchParams.eps);
3075
3076 distance_vector_t dists;
3077 assign(dists, this->veclen(*this), static_cast<typename distance_vector_t::value_type>(0));
3078 const DistanceType dist = this->computeInitialDistances(*this, vec, dists);
3079 searchLevelInc(result, vec, iroot_, dist, dists, epsError, this->veclen(*this));
3080 if (searchParams.sorted) result.sort();
3081 return result.full();
3082 }
3083
3085 NANOFLANN_NODISCARD Size knnSearch(
3086 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
3087 DistanceType* out_distances, const SearchParameters& searchParams = {}) const
3088 {
3090 resultSet.init(out_indices, out_distances);
3091 findNeighbors(resultSet, query_point, searchParams);
3092 return resultSet.size();
3093 }
3094
3096 NANOFLANN_NODISCARD Size radiusSearch(
3097 const ElementType* query_point, const DistanceType& radius,
3098 std::vector<ResultItem<IndexType, DistanceType>>& IndicesDists,
3099 const SearchParameters& searchParams = {}) const
3100 {
3101 RadiusResultSet<DistanceType, IndexType> resultSet(radius, IndicesDists);
3102 findNeighbors(resultSet, query_point, searchParams);
3103 return resultSet.size();
3104 }
3105
3107 template <class SEARCH_CALLBACK>
3108 NANOFLANN_NODISCARD Size radiusSearchCustomCallback(
3109 const ElementType* query_point, SEARCH_CALLBACK& resultSet,
3110 const SearchParameters& searchParams = {}) const
3111 {
3112 findNeighbors(resultSet, query_point, searchParams);
3113 return resultSet.size();
3114 }
3115
3117 NANOFLANN_NODISCARD Size rknnSearch(
3118 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
3119 DistanceType* out_distances, const DistanceType& radius) const
3120 {
3121 nanoflann::RKNNResultSet<DistanceType, IndexType> resultSet(num_closest, radius);
3122 resultSet.init(out_indices, out_distances);
3123 findNeighbors(resultSet, query_point);
3124 return resultSet.size();
3125 }
3126
3128 template <typename RESULTSET>
3129 NANOFLANN_NODISCARD Size findWithinBox(RESULTSET& result, const BoundingBox& bbox) const
3130 {
3131 if (iroot_) findWithinBoxRec(result, iroot_, bbox);
3132 return result.size();
3133 }
3134
3136
3139
3145 static constexpr uint32_t INCREMENTAL_SAVE_MAGIC = 0x4E464C49;
3146
3166 void saveIndex(std::ostream& stream) const
3167 {
3168 const uint32_t hdr_magic = INCREMENTAL_SAVE_MAGIC;
3169 const uint32_t hdr_version = static_cast<uint32_t>(NANOFLANN_VERSION);
3170 const uint8_t hdr_sz_st = static_cast<uint8_t>(sizeof(size_t));
3171 const uint8_t hdr_sz_idx = static_cast<uint8_t>(sizeof(IndexType));
3172 const uint8_t hdr_sz_elem = static_cast<uint8_t>(sizeof(ElementType));
3173 const uint8_t hdr_sz_dist = static_cast<uint8_t>(sizeof(DistanceType));
3174 save_value(stream, hdr_magic);
3175 save_value(stream, hdr_version);
3176 save_value(stream, hdr_sz_st);
3177 save_value(stream, hdr_sz_idx);
3178 save_value(stream, hdr_sz_elem);
3179 save_value(stream, hdr_sz_dist);
3180
3181 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3182 save_value(stream, dims);
3183
3184 const uint8_t hasRoot = iroot_ ? 1 : 0;
3185 save_value(stream, hasRoot);
3186 if (iroot_) saveNode(stream, iroot_);
3187 }
3188
3202 void loadIndex(std::istream& stream)
3203 {
3204 uint32_t magic = 0;
3205 load_value(stream, magic);
3206 if (stream.fail() || magic != INCREMENTAL_SAVE_MAGIC)
3207 {
3208 throw std::runtime_error(
3209 "KDTreeSingleIndexIncrementalAdaptor::loadIndex: invalid file (wrong magic "
3210 "number). The stream was not written by this class' saveIndex(), or was written "
3211 "by the static KDTreeSingleIndexAdaptor instead.");
3212 }
3213
3214 uint32_t file_version = 0;
3215 load_value(stream, file_version);
3216 if (file_version != static_cast<uint32_t>(NANOFLANN_VERSION))
3217 {
3218 char msg[200];
3219 snprintf(
3220 msg, sizeof(msg),
3221 "KDTreeSingleIndexIncrementalAdaptor::loadIndex: version mismatch "
3222 "(file=0x%03X, library=0x%03X). Rebuild the index.",
3223 file_version, static_cast<unsigned>(NANOFLANN_VERSION));
3224 throw std::runtime_error(msg);
3225 }
3226
3227 uint8_t sz_size_t = 0;
3228 uint8_t sz_idx = 0;
3229 uint8_t sz_elem = 0;
3230 uint8_t sz_dist = 0;
3231 load_value(stream, sz_size_t);
3232 load_value(stream, sz_idx);
3233 load_value(stream, sz_elem);
3234 load_value(stream, sz_dist);
3235 if (sz_size_t != static_cast<uint8_t>(sizeof(size_t)) ||
3236 sz_idx != static_cast<uint8_t>(sizeof(IndexType)) ||
3237 sz_elem != static_cast<uint8_t>(sizeof(ElementType)) ||
3238 sz_dist != static_cast<uint8_t>(sizeof(DistanceType)))
3239 {
3240 throw std::runtime_error(
3241 "KDTreeSingleIndexIncrementalAdaptor::loadIndex: type-size mismatch between "
3242 "saved index and current template instantiation (sizeof size_t / IndexType / "
3243 "ElementType / DistanceType differ). Rebuild the index.");
3244 }
3245
3246 Dimension dims = 0;
3247 load_value(stream, dims);
3248 if (dims != static_cast<Dimension>(this->veclen(*this)))
3249 {
3250 throw std::runtime_error(
3251 "KDTreeSingleIndexIncrementalAdaptor::loadIndex: dimensionality mismatch "
3252 "between the saved index and this object's dataset.");
3253 }
3254
3255 uint8_t hasRoot = 0;
3256 load_value(stream, hasRoot);
3257 iroot_ = hasRoot ? loadNode(stream, nullptr) : nullptr;
3258
3259 if (stream.fail())
3260 {
3261 throw std::runtime_error(
3262 "KDTreeSingleIndexIncrementalAdaptor::loadIndex: unexpected end of stream or "
3263 "read error.");
3264 }
3265
3266 totalCount_ = iroot_ ? iroot_->subtree_size : 0;
3267 liveCount_ = iroot_ ? iroot_->subtree_size - iroot_->invalid_count : 0;
3268 syncRootBox();
3269 }
3270
3272
3273 private:
3274 // --------------------------------------------------------------------
3275 // Node allocation (bump-allocate from the pool, recycle via free-list)
3276 // --------------------------------------------------------------------
3277 INode* allocNode()
3278 {
3279 if (freeList_)
3280 {
3281 INode* n = freeList_;
3282 freeList_ = n->child1;
3283 return n; // already constructed; box storage reused
3284 }
3285 INode* n = Base::pool_.template allocate<INode>();
3286 // Placement-new so that, for DIM=-1, the std::vector box is constructed.
3287 ::new (static_cast<void*>(n)) INode();
3288 resize(n->box, static_cast<Dimension>(this->veclen(*this)));
3289 if (kCacheCoords) resize(n->pcoord, static_cast<Dimension>(this->veclen(*this)));
3290 return n;
3291 }
3292
3294 void cacheCoords(INode* n)
3295 {
3296 if (!kCacheCoords) return;
3297 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3298 for (Dimension i = 0; i < dims; ++i) n->pcoord[i] = pt(n->ptIdx, i);
3299 }
3300
3302 ElementType nodeCoord(const INode* n, Dimension d) const
3303 {
3304 return kCacheCoords ? n->pcoord[d] : pt(n->ptIdx, d);
3305 }
3306
3308 bool nodeInBox(const INode* n, const BoundingBox& b) const
3309 {
3310 if (!kCacheCoords) return pointInBox(n->ptIdx, b);
3311 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3312 for (Dimension i = 0; i < dims; ++i)
3313 if (n->pcoord[i] < b[i].low || n->pcoord[i] > b[i].high) return false;
3314 return true;
3315 }
3316
3317 void recycleNode(INode* n)
3318 {
3319 n->child1 = freeList_;
3320 freeList_ = n;
3321 }
3322
3325 void destroyNodeObjects()
3326 {
3327 if (std::is_trivially_destructible<INode>::value) return;
3328 destroySubtree(iroot_);
3329 iroot_ = nullptr;
3330 while (freeList_)
3331 {
3332 INode* n = freeList_;
3333 freeList_ = n->child1;
3334 n->~INode();
3335 }
3336 }
3337
3338 void destroySubtree(INode* n)
3339 {
3340 if (!n) return;
3341 destroySubtree(n->child1);
3342 destroySubtree(n->child2);
3343 n->~INode();
3344 }
3345
3346 // --------------------------------------------------------------------
3347 // Helpers
3348 // --------------------------------------------------------------------
3349 ElementType pt(IndexType idx, Dimension d) const { return dataset_.kdtree_get_pt(idx, d); }
3350
3351 void ensureNodeMap(IndexType idx)
3352 {
3353 // The point->node map is grown to hold `idx`, so a bogus index is not a
3354 // wrong result but an out-of-memory: the maximum IndexType value alone
3355 // asks for a 2^32-entry (32 GB) map on the default uint32_t. That value
3356 // is what `static_cast<IndexType>(n - 1)` produces when a caller forgets
3357 // to special-case an empty (n == 0) dataset, and it would additionally
3358 // make the [start,end] loops of addPoints() wrap around forever, so
3359 // reject it here, where every index-taking entry point passes through.
3360 if (idx == (std::numeric_limits<IndexType>::max)())
3361 {
3362 throw std::invalid_argument(
3363 "[nanoflann] KDTreeSingleIndexIncrementalAdaptor: point index equal to the "
3364 "maximum IndexType value; this is almost certainly an underflowed 'size - 1' "
3365 "on an empty dataset.");
3366 }
3367 if (idx >= nodeOfPoint_.size()) nodeOfPoint_.resize(static_cast<size_t>(idx) + 1, nullptr);
3368 }
3369
3370 void syncRootBox()
3371 {
3372 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3373 if (iroot_)
3374 for (Dimension i = 0; i < dims; ++i) Base::root_bbox_[i] = iroot_->box[i];
3375 else
3376 for (Dimension i = 0; i < dims; ++i) Base::root_bbox_[i] = Interval{0, 0};
3377 }
3378
3379 void initBoxToPoint(INode* n)
3380 {
3381 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3382 for (Dimension i = 0; i < dims; ++i)
3383 {
3384 const ElementType v = pt(n->ptIdx, i);
3385 n->box[i].low = n->box[i].high = v;
3386 }
3387 }
3388
3389 void expandBoxToPoint(INode* n, IndexType idx)
3390 {
3391 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3392 for (Dimension i = 0; i < dims; ++i)
3393 {
3394 const ElementType v = pt(idx, i);
3395 if (v < n->box[i].low) n->box[i].low = v;
3396 if (v > n->box[i].high) n->box[i].high = v;
3397 }
3398 }
3399
3400 void unionBox(INode* n, const INode* c)
3401 {
3402 if (!c) return;
3403 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3404 for (Dimension i = 0; i < dims; ++i)
3405 {
3406 if (c->box[i].low < n->box[i].low) n->box[i].low = c->box[i].low;
3407 if (c->box[i].high > n->box[i].high) n->box[i].high = c->box[i].high;
3408 }
3409 }
3410
3411 bool pointInBox(IndexType idx, const BoundingBox& b) const
3412 {
3413 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3414 for (Dimension i = 0; i < dims; ++i)
3415 {
3416 const ElementType v = pt(idx, i);
3417 if (v < b[i].low || v > b[i].high) return false;
3418 }
3419 return true;
3420 }
3421
3422 bool boxFullyInside(const BoundingBox& inner, const BoundingBox& outer) const
3423 {
3424 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3425 for (Dimension i = 0; i < dims; ++i)
3426 if (inner[i].low < outer[i].low || inner[i].high > outer[i].high) return false;
3427 return true;
3428 }
3429
3430 bool boxDisjoint(const BoundingBox& a, const BoundingBox& b) const
3431 {
3432 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3433 for (Dimension i = 0; i < dims; ++i)
3434 if (a[i].high < b[i].low || a[i].low > b[i].high) return true;
3435 return false;
3436 }
3437
3438 // --------------------------------------------------------------------
3439 // Insertion
3440 // --------------------------------------------------------------------
3441 INode* makeLeaf(IndexType idx, Dimension depth, INode* parent)
3442 {
3443 INode* n = allocNode();
3444 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3445 n->ptIdx = idx;
3446 n->divfeat = static_cast<Dimension>(depth % dims);
3447 n->deleted = false;
3448 n->treeDeleted = false;
3449 n->child1 = n->child2 = nullptr;
3450 n->parent = parent;
3451 n->subtree_size = 1;
3452 n->invalid_count = 0;
3453 initBoxToPoint(n);
3454 cacheCoords(n);
3455 nodeOfPoint_[idx] = n;
3456 return n;
3457 }
3458
3461 void insertOne(IndexType idx)
3462 {
3463 pendingRebuild_ = nullptr;
3464 iroot_ = insertRec(iroot_, idx, 0, nullptr);
3465 ++liveCount_;
3466 ++totalCount_;
3467 if (pendingRebuild_ && inlineRebuild_) rebuildAt(pendingRebuild_);
3468 }
3469
3470 INode* insertRec(INode* node, IndexType idx, Dimension depth, INode* parent)
3471 {
3472 if (!node) return makeLeaf(idx, depth, parent);
3473 if (node->treeDeleted) pushDownDelete(node);
3474
3475 ++node->subtree_size;
3476 expandBoxToPoint(node, idx);
3477
3478 const Dimension axis = node->divfeat;
3479 if (pt(idx, axis) < nodeCoord(node, axis))
3480 node->child1 = insertRec(node->child1, idx, static_cast<Dimension>(depth + 1), node);
3481 else
3482 node->child2 = insertRec(node->child2, idx, static_cast<Dimension>(depth + 1), node);
3483
3484 // On the unwind, remember the *highest* unbalanced node seen on the
3485 // path (ancestors are visited after descendants, so the last write
3486 // wins). insertOne() rebuilds it once, avoiding a second descent.
3487 if (isBalanceScapegoat(node)) pendingRebuild_ = node;
3488 return node;
3489 }
3490
3493 void pushDownDelete(INode* node)
3494 {
3495 node->deleted = true;
3496 if (node->child1)
3497 {
3498 node->child1->treeDeleted = true;
3499 node->child1->invalid_count = node->child1->subtree_size;
3500 }
3501 if (node->child2)
3502 {
3503 node->child2->treeDeleted = true;
3504 node->child2->invalid_count = node->child2->subtree_size;
3505 }
3506 node->treeDeleted = false; // invalid_count already == subtree_size
3507 }
3508
3509 Size maxChildSize(const INode* node) const
3510 {
3511 const Size l = node->child1 ? node->child1->subtree_size : 0;
3512 const Size r = node->child2 ? node->child2->subtree_size : 0;
3513 return l > r ? l : r;
3514 }
3515
3516 bool isBalanceScapegoat(const INode* node) const
3517 {
3518 if (node->subtree_size < kMinBalanceRebuild) return false;
3519 return static_cast<float>(maxChildSize(node)) >
3520 alphaBal_ * static_cast<float>(node->subtree_size);
3521 }
3522
3523 // --------------------------------------------------------------------
3524 // Deletion (lazy) + box-region deletion
3525 // --------------------------------------------------------------------
3527 void killSubtree(INode* node)
3528 {
3529 node->treeDeleted = true;
3530 node->invalid_count = node->subtree_size;
3531 }
3532
3534 Size removeOutsideBoxRec(INode* node, const BoundingBox& keep)
3535 {
3536 if (!node) return 0;
3537 if (node->invalid_count == node->subtree_size) return 0; // already all dead
3538 if (boxFullyInside(node->box, keep)) return 0; // keep entire subtree
3539 if (boxDisjoint(node->box, keep))
3540 {
3541 const Size newly = node->subtree_size - node->invalid_count;
3542 killSubtree(node);
3543 liveCount_ -= newly;
3544 return newly;
3545 }
3546 Size newly = 0;
3547 if (!node->deleted && !nodeInBox(node, keep))
3548 {
3549 node->deleted = true;
3550 ++newly;
3551 --liveCount_;
3552 }
3553 newly += removeOutsideBoxRec(node->child1, keep);
3554 newly += removeOutsideBoxRec(node->child2, keep);
3555 node->invalid_count += newly;
3556 return newly;
3557 }
3558
3560 Size removeBoxRec(INode* node, const BoundingBox& box)
3561 {
3562 if (!node) return 0;
3563 if (node->invalid_count == node->subtree_size) return 0;
3564 if (boxDisjoint(node->box, box)) return 0; // nothing inside
3565 if (boxFullyInside(node->box, box))
3566 {
3567 const Size newly = node->subtree_size - node->invalid_count;
3568 killSubtree(node);
3569 liveCount_ -= newly;
3570 return newly;
3571 }
3572 Size newly = 0;
3573 if (!node->deleted && nodeInBox(node, box))
3574 {
3575 node->deleted = true;
3576 ++newly;
3577 --liveCount_;
3578 }
3579 newly += removeBoxRec(node->child1, box);
3580 newly += removeBoxRec(node->child2, box);
3581 node->invalid_count += newly;
3582 return newly;
3583 }
3584
3585 bool isDeletionScapegoat(const INode* node) const
3586 {
3587 if (node->subtree_size == 0) return false;
3588 return static_cast<float>(node->invalid_count) >
3589 alphaDel_ * static_cast<float>(node->subtree_size);
3590 }
3591
3592 INode* findDeletionScapegoat(INode* node) const
3593 {
3594 if (!node) return nullptr;
3595 if (isDeletionScapegoat(node)) return node; // highest wins
3596 if (INode* l = findDeletionScapegoat(node->child1)) return l;
3597 return findDeletionScapegoat(node->child2);
3598 }
3599
3600 void maybeRebuildForDeletion()
3601 {
3602 if (!iroot_ || !inlineRebuild_) return;
3603 if (INode* sg = findDeletionScapegoat(iroot_)) rebuildAt(sg);
3604 }
3605
3606 // --------------------------------------------------------------------
3607 // Partial rebuild (scapegoat): flatten live points, rebuild balanced
3608 // --------------------------------------------------------------------
3609 void rebuildAt(INode* node)
3610 {
3611 INode* par = node->parent;
3612 INode** link = par ? (par->child1 == node ? &par->child1 : &par->child2) : &iroot_;
3613
3614 const Size oldSize = node->subtree_size;
3615 const Size oldInvalid = node->invalid_count;
3616
3617 buildBuf_.clear();
3618 collectLiveAndFree(node, buildBuf_);
3619
3620 INode* nb = buildBalanced(buildBuf_, 0, buildBuf_.size(), 0, par);
3621 *link = nb;
3622
3623 const Size newSize = nb ? nb->subtree_size : 0; // == number of live pts
3624 // Propagate the change in (size, invalid) up to the ancestors.
3625 for (INode* p = par; p; p = p->parent)
3626 {
3627 p->subtree_size = p->subtree_size - oldSize + newSize;
3628 p->invalid_count = p->invalid_count - oldInvalid;
3629 }
3630 totalCount_ = totalCount_ - oldSize + newSize;
3631 }
3632
3636 void collectLiveAndFree(INode* node, std::vector<IndexType>& out)
3637 {
3638 if (!node) return;
3639 if (node->treeDeleted)
3640 {
3641 freeDeadSubtree(node);
3642 return;
3643 }
3644 if (!node->deleted)
3645 out.push_back(node->ptIdx);
3646 else
3647 dropDeadPoint(node->ptIdx);
3648 collectLiveAndFree(node->child1, out);
3649 collectLiveAndFree(node->child2, out);
3650 recycleNode(node);
3651 }
3652
3653 void freeDeadSubtree(INode* node)
3654 {
3655 if (!node) return;
3656 dropDeadPoint(node->ptIdx);
3657 freeDeadSubtree(node->child1);
3658 freeDeadSubtree(node->child2);
3659 recycleNode(node);
3660 }
3661
3662 void dropDeadPoint(IndexType idx)
3663 {
3664 if (idx < nodeOfPoint_.size()) nodeOfPoint_[idx] = nullptr;
3665 if (collectRemoved_) removedSink_.push_back(idx);
3666 }
3667
3669 void snapshotRec(const INode* node, std::vector<IndexType>& out) const
3670 {
3671 if (!node) return;
3672 if (node->invalid_count == node->subtree_size) return; // whole subtree dead
3673 if (!node->deleted) out.push_back(node->ptIdx);
3674 snapshotRec(node->child1, out);
3675 snapshotRec(node->child2, out);
3676 }
3677
3679 void collectAllRec(const INode* node, std::vector<IndexType>& out) const
3680 {
3681 if (!node) return;
3682 out.push_back(node->ptIdx);
3683 collectAllRec(node->child1, out);
3684 collectAllRec(node->child2, out);
3685 }
3686
3687 // --------------------------------------------------------------------
3688 // Persistence (see saveIndex()/loadIndex())
3689 // --------------------------------------------------------------------
3692 void saveNode(std::ostream& stream, const INode* n) const
3693 {
3694 save_value(stream, n->ptIdx);
3695 save_value(stream, n->divfeat);
3696 save_value(stream, n->deleted);
3697 save_value(stream, n->treeDeleted);
3698
3699 const uint8_t hasChild1 = n->child1 ? 1 : 0;
3700 save_value(stream, hasChild1);
3701 if (n->child1) saveNode(stream, n->child1);
3702
3703 const uint8_t hasChild2 = n->child2 ? 1 : 0;
3704 save_value(stream, hasChild2);
3705 if (n->child2) saveNode(stream, n->child2);
3706 }
3707
3718 INode* loadNode(std::istream& stream, INode* parent)
3719 {
3720 INode* n = allocNode();
3721 load_value(stream, n->ptIdx);
3722 load_value(stream, n->divfeat);
3723 load_value(stream, n->deleted);
3724 load_value(stream, n->treeDeleted);
3725 n->parent = parent;
3726
3727 uint8_t hasChild1 = 0;
3728 load_value(stream, hasChild1);
3729 n->child1 = hasChild1 ? loadNode(stream, n) : nullptr;
3730
3731 uint8_t hasChild2 = 0;
3732 load_value(stream, hasChild2);
3733 n->child2 = hasChild2 ? loadNode(stream, n) : nullptr;
3734
3735 n->subtree_size = 1 + (n->child1 ? n->child1->subtree_size : 0) +
3736 (n->child2 ? n->child2->subtree_size : 0);
3737 n->invalid_count = n->treeDeleted ? n->subtree_size
3738 : static_cast<Size>(n->deleted ? 1 : 0) +
3739 (n->child1 ? n->child1->invalid_count : 0) +
3740 (n->child2 ? n->child2->invalid_count : 0);
3741
3742 initBoxToPoint(n);
3743 unionBox(n, n->child1);
3744 unionBox(n, n->child2);
3745 cacheCoords(n);
3746
3747 ensureNodeMap(n->ptIdx);
3748 nodeOfPoint_[n->ptIdx] = n;
3749
3750 return n;
3751 }
3752
3754 INode* buildBalanced(
3755 std::vector<IndexType>& buf, size_t lo, size_t hi, Dimension depth, INode* parent)
3756 {
3757 if (lo >= hi) return nullptr;
3758 const Dimension dims = static_cast<Dimension>(this->veclen(*this));
3759
3760 // Widest-spread axis over buf[lo,hi).
3761 Dimension axis = static_cast<Dimension>(depth % dims);
3762 ElementType bestSpan = -1;
3763 for (Dimension d = 0; d < dims; ++d)
3764 {
3765 ElementType mn = pt(buf[lo], d), mx = mn;
3766 for (size_t k = lo + 1; k < hi; ++k)
3767 {
3768 const ElementType v = pt(buf[k], d);
3769 if (v < mn) mn = v;
3770 if (v > mx) mx = v;
3771 }
3772 const ElementType span = mx - mn;
3773 if (span > bestSpan)
3774 {
3775 bestSpan = span;
3776 axis = d;
3777 }
3778 }
3779
3780 const size_t mid = lo + (hi - lo) / 2;
3781 std::nth_element(
3782 buf.begin() + lo, buf.begin() + mid, buf.begin() + hi,
3783 [this, axis](IndexType a, IndexType b) { return pt(a, axis) < pt(b, axis); });
3784
3785 INode* node = allocNode();
3786 node->ptIdx = buf[mid];
3787 node->divfeat = axis;
3788 node->deleted = false;
3789 node->treeDeleted = false;
3790 node->parent = parent;
3791 cacheCoords(node);
3792 nodeOfPoint_[buf[mid]] = node;
3793
3794 node->child1 = buildBalanced(buf, lo, mid, static_cast<Dimension>(depth + 1), node);
3795 node->child2 = buildBalanced(buf, mid + 1, hi, static_cast<Dimension>(depth + 1), node);
3796
3797 node->subtree_size = hi - lo;
3798 node->invalid_count = 0;
3799 initBoxToPoint(node);
3800 unionBox(node, node->child1);
3801 unionBox(node, node->child2);
3802 return node;
3803 }
3804
3805 // --------------------------------------------------------------------
3806 // Search
3807 // --------------------------------------------------------------------
3808 template <class RESULTSET>
3809 void searchLevelInc(
3810 RESULTSET& rs, const ElementType* vec, const INode* node, DistanceType mindist,
3811 distance_vector_t& dists, const DistanceType epsError, const Size dim) const
3812 {
3813 if (!node) return;
3814 if (node->invalid_count == node->subtree_size) return; // whole subtree dead
3815
3816 if (!node->deleted)
3817 {
3818#if defined(NANOFLANN_INCREMENTAL_INNODE_DISTANCE)
3819 // Opt-in: compute the node distance from the in-node coordinate cache
3820 // as a sum of per-axis accum_dist contributions. This avoids the
3821 // dataset_get() indirection and is ~12% faster on KNN, but is only
3822 // valid for *additive* (axis-decomposable) metrics — L1, L2,
3823 // L2_Simple. Do NOT enable it for SO2/SO3.
3824 DistanceType d = DistanceType();
3825 if (kCacheCoords)
3826 for (Size i = 0; i < dim; ++i)
3827 d += distance_.accum_dist(
3828 vec[i], node->pcoord[static_cast<Dimension>(i)], static_cast<Dimension>(i));
3829 else
3830 d = distance_.evalMetric(vec, node->ptIdx, dim);
3831#else
3832 const DistanceType d = distance_.evalMetric(vec, node->ptIdx, dim);
3833#endif
3834 if (d < rs.worstDist())
3835 rs.addPoint(
3836 static_cast<typename RESULTSET::DistanceType>(d),
3837 static_cast<typename RESULTSET::IndexType>(node->ptIdx));
3838 }
3839
3840 const Dimension axis = node->divfeat;
3841 const ElementType splitval = nodeCoord(node, axis);
3842 const ElementType val = vec[axis];
3843 const DistanceType cut = distance_.accum_dist(val, splitval, axis);
3844
3845 const INode* nearChild;
3846 const INode* farChild;
3847 if (val < splitval)
3848 {
3849 nearChild = node->child1;
3850 farChild = node->child2;
3851 }
3852 else
3853 {
3854 nearChild = node->child2;
3855 farChild = node->child1;
3856 }
3857
3858 searchLevelInc(rs, vec, nearChild, mindist, dists, epsError, dim);
3859
3860 const DistanceType dst = dists[axis];
3861 const DistanceType newmin = mindist + cut - dst;
3862 dists[axis] = cut;
3863 if (newmin * epsError <= rs.worstDist())
3864 searchLevelInc(rs, vec, farChild, newmin, dists, epsError, dim);
3865 dists[axis] = dst;
3866 }
3867
3868 template <typename RESULTSET>
3869 void findWithinBoxRec(RESULTSET& result, const INode* node, const BoundingBox& bbox) const
3870 {
3871 if (!node) return;
3872 if (node->invalid_count == node->subtree_size) return;
3873 if (boxDisjoint(node->box, bbox)) return;
3874 if (!node->deleted && nodeInBox(node, bbox)) result.addPoint(0, node->ptIdx);
3875 findWithinBoxRec(result, node->child1, bbox);
3876 findWithinBoxRec(result, node->child2, bbox);
3877 }
3878};
3879
3880#ifndef NANOFLANN_NO_THREADS
3915template <typename Distance, class DatasetAdaptor, int32_t DIM = -1, typename IndexType = uint32_t>
3917{
3918 public:
3920 using ElementType = typename Inner::ElementType;
3921 using DistanceType = typename Inner::DistanceType;
3922 using Size = typename Inner::Size;
3923 using Dimension = typename Inner::Dimension;
3924 using BoundingBox = typename Inner::BoundingBox;
3925
3932 const Dimension dimensionality, const DatasetAdaptor& inputData,
3933 const KDTreeIncrementalIndexParams& params = {}, double rebuild_growth = 1.3,
3934 Size min_rebuild_size = 10000)
3935 : dataset_(inputData),
3936 dim_(dimensionality),
3937 params_(params),
3938 rebuildGrowth_(rebuild_growth),
3939 minRebuildSize_(min_rebuild_size)
3940 {
3941 active_.reset(new Inner(dimensionality, inputData, params));
3942 active_->setInlineRebuild(false);
3943 }
3944
3945 KDTreeSingleIndexIncrementalAdaptorMT(const KDTreeSingleIndexIncrementalAdaptorMT&) = delete;
3946 KDTreeSingleIndexIncrementalAdaptorMT& operator=(const KDTreeSingleIndexIncrementalAdaptorMT&) =
3947 delete;
3948
3949 ~KDTreeSingleIndexIncrementalAdaptorMT()
3950 {
3951 // Lets any in-flight build finish before teardown: the worker reads the
3952 // caller's dataset, which is typically freed right after this returns.
3953 stopWorker();
3954 }
3955
3957 void addPoints(IndexType start, IndexType end)
3958 {
3959 integrateIfReady();
3960 active_->addPoints(start, end);
3961 if (building_) log_.push_back({OpKind::Add, start, end, {}});
3962 maybeTriggerRebuild();
3963 }
3964 void addPoint(IndexType idx) { addPoints(idx, idx); }
3965
3966 void removePoint(IndexType idx)
3967 {
3968 integrateIfReady();
3969 active_->removePoint(idx);
3970 if (building_) log_.push_back({OpKind::Remove, idx, idx, {}});
3971 maybeTriggerRebuild();
3972 }
3973 void removeBox(const BoundingBox& box)
3974 {
3975 integrateIfReady();
3976 active_->removeBox(box);
3977 if (building_) log_.push_back({OpKind::RemoveBox, 0, 0, box});
3978 maybeTriggerRebuild();
3979 }
3980 void removeOutsideBox(const BoundingBox& keep)
3981 {
3982 integrateIfReady();
3983 active_->removeOutsideBox(keep);
3984 if (building_) log_.push_back({OpKind::RemoveOutsideBox, 0, 0, keep});
3985 maybeTriggerRebuild();
3986 }
3988
3990 template <typename RESULTSET>
3991 bool findNeighbors(
3992 RESULTSET& result, const ElementType* vec, const SearchParameters& sp = {}) const
3993 {
3994 return active_->findNeighbors(result, vec, sp);
3995 }
3996 Size knnSearch(
3997 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
3998 DistanceType* out_distances, const SearchParameters& sp = {}) const
3999 {
4000 return active_->knnSearch(query_point, num_closest, out_indices, out_distances, sp);
4001 }
4002 Size radiusSearch(
4003 const ElementType* query_point, const DistanceType& radius,
4004 std::vector<ResultItem<IndexType, DistanceType>>& IndicesDists,
4005 const SearchParameters& sp = {}) const
4006 {
4007 return active_->radiusSearch(query_point, radius, IndicesDists, sp);
4008 }
4009 Size rknnSearch(
4010 const ElementType* query_point, const Size num_closest, IndexType* out_indices,
4011 DistanceType* out_distances, const DistanceType& radius) const
4012 {
4013 return active_->rknnSearch(query_point, num_closest, out_indices, out_distances, radius);
4014 }
4015 template <typename RESULTSET>
4016 Size findWithinBox(RESULTSET& result, const BoundingBox& bbox) const
4017 {
4018 return active_->findWithinBox(result, bbox);
4019 }
4021
4023 Size size() const noexcept { return active_->size(); }
4024 bool empty() const noexcept { return active_->empty(); }
4025 Size physicalSize() const noexcept { return active_->physicalSize(); }
4026 bool isRebuilding() const noexcept { return building_; }
4027
4029 NANOFLANN_NODISCARD BoundingBox boundingBox() const { return active_->boundingBox(); }
4030
4032 void snapshotLiveIndices(std::vector<IndexType>& out) const
4033 {
4034 active_->snapshotLiveIndices(out);
4035 }
4036
4038 void reserve(Size n) { active_->reserve(n); }
4039
4041 void sync()
4042 {
4043 if (building_)
4044 {
4045 std::unique_lock<std::mutex> lk(workerMtx_);
4046 workerCvDone_.wait(lk, [this] { return resultReady_; });
4047 }
4048 integrateIfReady();
4049 }
4050
4052 const Inner& activeIndex() const { return *active_; }
4054
4057
4060 void saveIndex(std::ostream& stream)
4061 {
4062 sync();
4063 active_->saveIndex(stream);
4064 }
4065
4070 void loadIndex(std::istream& stream)
4071 {
4072 sync();
4073 active_->loadIndex(stream);
4074 lastBuildLive_ = active_->size();
4075 }
4076
4077
4085 void setRebuildCallback(std::function<void(Inner&)> cb) { rebuildCallback_ = std::move(cb); }
4086
4088
4092 void setCollectRemovedPoints(bool enable)
4093 {
4094 collectRemoved_ = enable;
4095 if (!enable) std::vector<IndexType>().swap(removedSink_);
4096 }
4097
4101 std::vector<IndexType> acquireRemovedPoints()
4102 {
4103 std::vector<IndexType> out;
4104 out.swap(removedSink_);
4105 return out;
4106 }
4107
4108
4109 private:
4110 enum class OpKind
4111 {
4112 Add,
4113 Remove,
4114 RemoveBox,
4115 RemoveOutsideBox
4116 };
4117 struct LoggedOp
4118 {
4119 OpKind kind;
4120 IndexType a, b;
4121 BoundingBox box;
4122 };
4123
4124 void maybeTriggerRebuild()
4125 {
4126 if (building_) return;
4127 const Size phys = active_->physicalSize();
4128 if (phys < minRebuildSize_) return;
4129 const Size base = lastBuildLive_ ? lastBuildLive_ : Size(1);
4130 if (static_cast<double>(phys) < rebuildGrowth_ * static_cast<double>(base)) return;
4131
4132 // Snapshot the live indices on the foreground thread, then hand them to
4133 // the background worker, which bulk-builds a fresh balanced tree.
4134 auto snapshot = std::make_shared<std::vector<IndexType>>();
4135 active_->snapshotLiveIndices(*snapshot);
4136
4137 // Started on the first rebuild only, so an index that never rebuilds
4138 // costs no thread at all:
4139 if (!workerThread_.joinable())
4140 {
4141 workerThread_ = std::thread(&KDTreeSingleIndexIncrementalAdaptorMT::workerLoop, this);
4142 }
4143
4144 {
4145 std::lock_guard<std::mutex> lk(workerMtx_);
4146 pendingJob_ = std::move(snapshot);
4147 // Copied per job, so the worker never reads rebuildCallback_ while
4148 // setRebuildCallback() writes it:
4149 pendingCallback_ = rebuildCallback_;
4150 builtTree_.reset();
4151 buildError_ = nullptr;
4152 resultReady_ = false;
4153 }
4154 workerCvJob_.notify_one();
4155
4156 building_ = true;
4157 log_.clear();
4158 }
4159
4162 void workerLoop()
4163 {
4164 for (;;)
4165 {
4166 std::shared_ptr<std::vector<IndexType>> job;
4167 std::function<void(Inner&)> cb;
4168 {
4169 std::unique_lock<std::mutex> lk(workerMtx_);
4170 workerCvJob_.wait(lk, [this] { return workerStop_ || pendingJob_ != nullptr; });
4171 // A job already handed over is always finished before stopping:
4172 // the destructor relies on that to keep the caller's dataset
4173 // alive for as long as the build reads it.
4174 if (workerStop_ && !pendingJob_) return;
4175 job = std::move(pendingJob_);
4176 cb = std::move(pendingCallback_);
4177 }
4178
4179 // dim_, dataset_ and params_ are set at construction and never
4180 // mutated, so the worker reads them without synchronization.
4181 std::unique_ptr<Inner> t;
4182 std::exception_ptr err;
4183 try
4184 {
4185 t.reset(new Inner(dim_, dataset_, params_));
4186 t->setInlineRebuild(false);
4187 t->buildFromIndices(*job);
4188 if (cb) cb(*t); // background post-rebuild hook (e.g. recompute covariances)
4189 }
4190 catch (...)
4191 {
4192 // Handed to the foreground thread instead of terminating the
4193 // process; see integrateIfReady().
4194 err = std::current_exception();
4195 t.reset();
4196 }
4197
4198 {
4199 std::lock_guard<std::mutex> lk(workerMtx_);
4200 builtTree_ = std::move(t);
4201 buildError_ = err;
4202 resultReady_ = true;
4203 }
4204 workerCvDone_.notify_all();
4205 }
4206 }
4207
4209 void stopWorker()
4210 {
4211 if (!workerThread_.joinable()) return;
4212 {
4213 std::lock_guard<std::mutex> lk(workerMtx_);
4214 workerStop_ = true;
4215 }
4216 workerCvJob_.notify_all();
4217 workerThread_.join();
4218 }
4219
4220 void integrateIfReady()
4221 {
4222 if (!building_) return;
4223
4224 std::unique_ptr<Inner> fresh;
4225 std::exception_ptr err;
4226 {
4227 std::lock_guard<std::mutex> lk(workerMtx_);
4228 if (!resultReady_) return; // the rebuild is still running
4229 resultReady_ = false;
4230 fresh = std::move(builtTree_);
4231 err = buildError_;
4232 buildError_ = nullptr;
4233 }
4234
4235 // However this attempt ends (integrated, failed build, or a failed
4236 // replay below), it is over: return to the "not rebuilding" state so a
4237 // later rebuild can be triggered again. Latching `building_` would
4238 // silently disable rebuilding for good, and with it the reclaiming of
4239 // tombstoned nodes and of the dataset slots reported by
4240 // acquireRemovedPoints() (i.e. unbounded growth), and it would make a
4241 // later sync() wait forever for a result nobody is producing.
4242 struct EndOfRebuild
4243 {
4244 KDTreeSingleIndexIncrementalAdaptorMT& self;
4245 ~EndOfRebuild()
4246 {
4247 self.log_.clear();
4248 self.building_ = false;
4249 }
4250 } endOfRebuild{*this};
4251
4252 // The build failed (e.g. std::bad_alloc). The active tree was never
4253 // touched by it, so it is still correct: just drop the attempt.
4254 if (err) std::rethrow_exception(err);
4255
4256 fresh->setInlineRebuild(false);
4257 // Replay the operations buffered while the background build was running.
4258 for (const auto& op : log_)
4259 {
4260 switch (op.kind)
4261 {
4262 case OpKind::Add:
4263 fresh->addPoints(op.a, op.b);
4264 break;
4265 case OpKind::Remove:
4266 fresh->removePoint(op.a);
4267 break;
4268 case OpKind::RemoveBox:
4269 fresh->removeBox(op.box);
4270 break;
4271 case OpKind::RemoveOutsideBox:
4272 fresh->removeOutsideBox(op.box);
4273 break;
4274 }
4275 }
4276 // Dataset slots referenced by the OLD tree but not by the fresh one are
4277 // now free for the caller to recycle (no node references them anymore).
4278 if (collectRemoved_)
4279 {
4280 std::vector<IndexType> oldPhysical;
4281 active_->collectPhysicalIndices(oldPhysical);
4282 for (IndexType idx : oldPhysical)
4283 if (!fresh->referencesIndex(idx)) removedSink_.push_back(idx);
4284 }
4285 active_ = std::move(fresh);
4286 lastBuildLive_ = active_->size();
4287 }
4288
4289 const DatasetAdaptor& dataset_;
4290 Dimension dim_;
4291 KDTreeIncrementalIndexParams params_;
4292 double rebuildGrowth_;
4293 Size minRebuildSize_;
4294
4295 std::unique_ptr<Inner> active_;
4298 bool building_ = false;
4299 Size lastBuildLive_ = 0;
4300 std::vector<LoggedOp> log_;
4301
4308 std::thread workerThread_;
4309 std::mutex workerMtx_;
4310 std::condition_variable workerCvJob_; // foreground -> worker
4311 std::condition_variable workerCvDone_; // worker -> foreground
4312 std::shared_ptr<std::vector<IndexType>> pendingJob_; // live indices to build from
4313 std::function<void(Inner&)> pendingCallback_;
4314 std::unique_ptr<Inner> builtTree_; // result of the last build
4315 std::exception_ptr buildError_; // ...or how it failed
4316 bool resultReady_ = false;
4317 bool workerStop_ = false;
4319
4320 bool collectRemoved_ = false;
4321 std::vector<IndexType> removedSink_;
4322 std::function<void(Inner&)> rebuildCallback_;
4323};
4324#endif // NANOFLANN_NO_THREADS
4325
4351template <
4352 class MatrixType, int32_t DIM = -1, class Distance = nanoflann::metric_L2,
4353 bool row_major = true>
4355{
4357 using num_t = typename MatrixType::Scalar;
4358 using IndexType = typename MatrixType::Index;
4359 using metric_t = typename Distance::template traits<num_t, self_t, IndexType>::distance_t;
4360
4361 using index_t = KDTreeSingleIndexAdaptor<
4362 metric_t, self_t, row_major ? MatrixType::ColsAtCompileTime : MatrixType::RowsAtCompileTime,
4363 IndexType>;
4364
4365 index_t* index_;
4367
4368 using Offset = typename index_t::Offset;
4369 using Size = typename index_t::Size;
4370 using Dimension = typename index_t::Dimension;
4371
4374 const Dimension dimensionality, const std::reference_wrapper<const MatrixType>& mat,
4375 const int leaf_max_size = 10, const unsigned int n_thread_build = 1)
4376 : m_data_matrix(mat)
4377 {
4378 const auto dims = row_major ? mat.get().cols() : mat.get().rows();
4379 if (static_cast<Dimension>(dims) != dimensionality)
4380 throw std::runtime_error(
4381 "Error: 'dimensionality' must match column count in data "
4382 "matrix");
4383 if (DIM > 0 && static_cast<int32_t>(dims) != DIM)
4384 throw std::runtime_error(
4385 "Data set dimensionality does not match the 'DIM' template "
4386 "argument");
4387 index_ = new index_t(
4388 static_cast<Dimension>(dims), *this /* adaptor */,
4390 leaf_max_size, nanoflann::KDTreeSingleIndexAdaptorFlags::None, n_thread_build));
4391 }
4392
4393 public:
4395 KDTreeEigenMatrixAdaptor(const self_t&) = delete;
4396 self_t& operator=(const self_t&) = delete;
4397
4402 KDTreeEigenMatrixAdaptor(self_t&&) = delete;
4403 self_t& operator=(self_t&&) = delete;
4404
4405 ~KDTreeEigenMatrixAdaptor() { delete index_; }
4406
4407 const std::reference_wrapper<const MatrixType> m_data_matrix;
4408
4417 void query(
4418 const num_t* query_point, const Size num_closest, IndexType* out_indices,
4419 num_t* out_distances) const
4420 {
4421 nanoflann::KNNResultSet<num_t, IndexType> resultSet(num_closest);
4422 resultSet.init(out_indices, out_distances);
4423 index_->findNeighbors(resultSet, query_point);
4424 }
4425
4428
4429 inline const self_t& derived() const noexcept { return *this; }
4430 inline self_t& derived() noexcept { return *this; }
4431
4432 // Must return the number of data points
4433 inline Size kdtree_get_point_count() const
4434 {
4435 if (row_major)
4436 return m_data_matrix.get().rows();
4437 else
4438 return m_data_matrix.get().cols();
4439 }
4440
4441 // Returns the dim'th component of the idx'th point in the class:
4442 inline num_t kdtree_get_pt(const IndexType idx, size_t dim) const
4443 {
4444 if (row_major)
4445 return m_data_matrix.get().coeff(idx, IndexType(dim));
4446 else
4447 return m_data_matrix.get().coeff(IndexType(dim), idx);
4448 }
4449
4450 // Optional bounding-box computation: return false to default to a standard
4451 // bbox computation loop.
4452 // Return true if the BBOX was already computed by the class and returned
4453 // in "bb" so it can be avoided to redo it again. Look at bb.size() to
4454 // find out the expected dimensionality (e.g. 2 or 3 for point clouds)
4455 template <class BBOX>
4456 inline bool kdtree_get_bbox(BBOX& /*bb*/) const
4457 {
4458 return false;
4459 }
4460
4462
4463}; // end of KDTreeEigenMatrixAdaptor
4464
4465 // end of grouping
4467} // namespace nanoflann
4468
4469#undef NANOFLANN_RESTRICT
bool addPoint(DistanceType, IndexType index)
Definition nanoflann.hpp:491
Definition nanoflann.hpp:1052
NANOFLANN_NODISCARD bool isActive(IndexType) const
Definition nanoflann.hpp:1210
void freeIndex(Derived &obj)
Definition nanoflann.hpp:1056
NANOFLANN_NODISCARD Size veclen(const Derived &obj) const noexcept
Definition nanoflann.hpp:1157
void computeMinMax(const Derived &obj, Offset ind, Size count, Dimension element, ElementType &min_elem, ElementType &max_elem) const
Definition nanoflann.hpp:1193
BoundingBox root_bbox_
Definition nanoflann.hpp:1139
void saveIndex(const Derived &obj, std::ostream &stream) const
Definition nanoflann.hpp:1671
void computeBoundingBox(BoundingBox &bbox)
Definition nanoflann.hpp:1215
NANOFLANN_NODISCARD Size usedMemory(const Derived &obj) const
Definition nanoflann.hpp:1183
Dimension dim_
Dimensionality of each data point.
Definition nanoflann.hpp:1128
typename array_or_vector< DIM, DistanceType >::type distance_vector_t
Definition nanoflann.hpp:1136
void planeSplit(const Derived &obj, const Offset ind, const Size count, const Dimension cutfeat, const DistanceType &cutval, Offset &lim1, Offset &lim2)
Definition nanoflann.hpp:1565
Size n_thread_build_
Number of thread for concurrent tree build.
Definition nanoflann.hpp:1123
NANOFLANN_NODISCARD Size size(const Derived &obj) const noexcept
Definition nanoflann.hpp:1151
std::vector< IndexType > vAcc_
Definition nanoflann.hpp:1070
bool makeNode(Derived &obj, NodePtr node, const Offset left, const Offset right, BoundingBox &bbox, Offset &idx, Dimension &cutfeat, DistanceType &cutval)
Definition nanoflann.hpp:1328
bool searchLevel(RESULTSET &result_set, const ElementType *vec, const NodePtr node, DistanceType mindist, distance_vector_t &dists, const DistanceType epsError) const
Definition nanoflann.hpp:1245
Size size_at_index_build_
Number of points in the dataset when the index was built.
Definition nanoflann.hpp:1127
NodePtr divideTreeConcurrent(Derived &obj, const Offset left, const Offset right, BoundingBox &bbox, std::atomic< unsigned int > &thread_count, std::mutex &mutex)
Definition nanoflann.hpp:1422
Size size_
Number of current points in the dataset.
Definition nanoflann.hpp:1125
void finalizeSplitNode(Derived &obj, NodePtr node, const Dimension cutfeat, const BoundingBox &left_bbox, const BoundingBox &right_bbox, BoundingBox &bbox)
Definition nanoflann.hpp:1370
void loadIndex(Derived &obj, std::istream &stream)
Definition nanoflann.hpp:1717
PooledAllocator pool_
Definition nanoflann.hpp:1148
ElementType dataset_get(const Derived &obj, IndexType element, Dimension component) const
Helper accessor to the dataset points:
Definition nanoflann.hpp:1174
typename array_or_vector< DIM, Interval >::type BoundingBox
Definition nanoflann.hpp:1132
static constexpr uint32_t SAVE_MAGIC
Definition nanoflann.hpp:1651
Definition nanoflann.hpp:1838
void saveIndex(std::ostream &stream) const
Definition nanoflann.hpp:2199
NANOFLANN_NODISCARD Size findWithinBox(RESULTSET &result, const BoundingBox &bbox) const
Definition nanoflann.hpp:2030
void buildIndex()
Definition nanoflann.hpp:1943
NANOFLANN_NODISCARD Size radiusSearch(const ElementType *query_point, const DistanceType &radius, std::vector< ResultItem< IndexType, DistanceType > > &IndicesDists, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2120
NANOFLANN_NODISCARD Size radiusSearchCustomCallback(const ElementType *query_point, SEARCH_CALLBACK &resultSet, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2136
NANOFLANN_NODISCARD Size knnSearch(const ElementType *query_point, const Size num_closest, IndexType *out_indices, DistanceType *out_distances) const
Definition nanoflann.hpp:2091
KDTreeSingleIndexAdaptor(const KDTreeSingleIndexAdaptor< Distance, DatasetAdaptor, DIM, index_t > &)=delete
bool findNeighbors(RESULTSET &result, const ElementType *vec, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:1990
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:2159
typename Base::distance_vector_t distance_vector_t
Definition nanoflann.hpp:1874
void loadIndex(std::istream &stream)
Definition nanoflann.hpp:2206
typename Base::BoundingBox BoundingBox
Definition nanoflann.hpp:1870
KDTreeSingleIndexAdaptor(const Dimension dimensionality, const DatasetAdaptor &inputData, const KDTreeSingleIndexAdaptorParams &params, Args &&... args)
Definition nanoflann.hpp:1897
Definition nanoflann.hpp:2252
KDTreeSingleIndexDynamicAdaptor_(const Dimension dimensionality, const DatasetAdaptor &inputData, std::vector< int > &treeIndex, const KDTreeSingleIndexAdaptorParams &params=KDTreeSingleIndexAdaptorParams())
Definition nanoflann.hpp:2306
typename Base::BoundingBox BoundingBox
Definition nanoflann.hpp:2282
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:2434
KDTreeSingleIndexDynamicAdaptor_(const KDTreeSingleIndexDynamicAdaptor_ &rhs)=default
void buildIndex()
Definition nanoflann.hpp:2351
void saveIndex(std::ostream &stream)
Definition nanoflann.hpp:2496
NANOFLANN_NODISCARD bool isActive(IndexType idx) const
Definition nanoflann.hpp:2289
NANOFLANN_NODISCARD Size radiusSearchCustomCallback(const ElementType *query_point, SEARCH_CALLBACK &resultSet, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2479
typename Base::distance_vector_t distance_vector_t
Definition nanoflann.hpp:2286
void loadIndex(std::istream &stream)
Definition nanoflann.hpp:2503
KDTreeSingleIndexDynamicAdaptor_ & operator=(const KDTreeSingleIndexDynamicAdaptor_ &rhs)
Definition nanoflann.hpp:2332
NANOFLANN_NODISCARD Size radiusSearch(const ElementType *query_point, const DistanceType &radius, std::vector< ResultItem< IndexType, DistanceType > > &IndicesDists, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2463
bool findNeighbors(RESULTSET &result, const ElementType *vec, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2400
bool findNeighbors(RESULTSET &result, const ElementType *vec, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:2705
const DatasetAdaptor & dataset_
The source of our data.
Definition nanoflann.hpp:2540
void removePoint(size_t idx)
Definition nanoflann.hpp:2678
std::unordered_map< IndexType, int > removedPoints_
Definition nanoflann.hpp:2548
void addPoints(IndexType start, IndexType end)
Definition nanoflann.hpp:2629
KDTreeSingleIndexDynamicAdaptor(const int dimensionality, const DatasetAdaptor &inputData, const KDTreeSingleIndexAdaptorParams &params=KDTreeSingleIndexAdaptorParams(), const size_t maximumPointCount=1000000000U)
Definition nanoflann.hpp:2604
std::vector< int > treeIndex_
Definition nanoflann.hpp:2544
const std::vector< index_container_t > & getAllIndices() const
Definition nanoflann.hpp:2561
Dimension dim_
Dimensionality of each data point.
Definition nanoflann.hpp:2552
KDTreeSingleIndexDynamicAdaptor(const KDTreeSingleIndexDynamicAdaptor< Distance, DatasetAdaptor, DIM, IndexType > &)=delete
void snapshotLiveIndices(std::vector< IndexType > &out) const
Definition nanoflann.hpp:4032
KDTreeSingleIndexIncrementalAdaptorMT(const Dimension dimensionality, const DatasetAdaptor &inputData, const KDTreeIncrementalIndexParams &params={}, double rebuild_growth=1.3, Size min_rebuild_size=10000)
Definition nanoflann.hpp:3931
void setCollectRemovedPoints(bool enable)
Definition nanoflann.hpp:4092
void sync()
Definition nanoflann.hpp:4041
const Inner & activeIndex() const
Definition nanoflann.hpp:4052
void loadIndex(std::istream &stream)
Definition nanoflann.hpp:4070
void reserve(Size n)
Definition nanoflann.hpp:4038
std::vector< IndexType > acquireRemovedPoints()
Definition nanoflann.hpp:4101
void setRebuildCallback(std::function< void(Inner &)> cb)
Definition nanoflann.hpp:4085
NANOFLANN_NODISCARD BoundingBox boundingBox() const
Definition nanoflann.hpp:4029
void saveIndex(std::ostream &stream)
Definition nanoflann.hpp:4060
NANOFLANN_NODISCARD Size radiusSearch(const ElementType *query_point, const DistanceType &radius, std::vector< ResultItem< IndexType, DistanceType > > &IndicesDists, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:3096
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:3117
void removeBox(const BoundingBox &box)
Definition nanoflann.hpp:2953
void setCollectRemovedPoints(bool enable)
Definition nanoflann.hpp:2971
void saveIndex(std::ostream &stream) const
Definition nanoflann.hpp:3166
NANOFLANN_NODISCARD Size radiusSearchCustomCallback(const ElementType *query_point, SEARCH_CALLBACK &resultSet, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:3108
void addPoint(IndexType idx)
Definition nanoflann.hpp:2897
NANOFLANN_NODISCARD BoundingBox boundingBox() const
Definition nanoflann.hpp:3052
void setInlineRebuild(bool enable)
Definition nanoflann.hpp:2989
KDTreeSingleIndexIncrementalAdaptor(const Dimension dimensionality, const DatasetAdaptor &inputData, const KDTreeIncrementalIndexParams &params={})
Definition nanoflann.hpp:2874
void snapshotLiveIndices(std::vector< IndexType > &out) const
Definition nanoflann.hpp:2993
NANOFLANN_NODISCARD Size usedMemory() const
Definition nanoflann.hpp:3043
void removePoint(IndexType idx)
Definition nanoflann.hpp:2935
static constexpr uint32_t INCREMENTAL_SAVE_MAGIC
Definition nanoflann.hpp:3145
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:3085
void reserve(Size n)
Definition nanoflann.hpp:3056
NANOFLANN_NODISCARD Size size() const noexcept
Definition nanoflann.hpp:3036
NANOFLANN_NODISCARD Size physicalSize() const noexcept
Definition nanoflann.hpp:3040
bool findNeighbors(RESULTSET &result, const ElementType *vec, const SearchParameters &searchParams={}) const
Definition nanoflann.hpp:3069
void collectPhysicalIndices(std::vector< IndexType > &out) const
Definition nanoflann.hpp:2998
KDTreeSingleIndexIncrementalAdaptor(const KDTreeSingleIndexIncrementalAdaptor &)=delete
static constexpr bool kCacheCoords
Definition nanoflann.hpp:2828
void removeOutsideBox(const BoundingBox &keep)
Definition nanoflann.hpp:2962
NANOFLANN_NODISCARD Size findWithinBox(RESULTSET &result, const BoundingBox &bbox) const
Definition nanoflann.hpp:3129
void addPoints(IndexType start, IndexType end)
Definition nanoflann.hpp:2912
void buildFromIndices(const std::vector< IndexType > &idxs)
Definition nanoflann.hpp:3009
NANOFLANN_NODISCARD bool referencesIndex(IndexType idx) const
Definition nanoflann.hpp:3002
void loadIndex(std::istream &stream)
Definition nanoflann.hpp:3202
std::vector< IndexType > acquireRemovedPoints()
Definition nanoflann.hpp:2979
Definition nanoflann.hpp:292
bool addPoint(DistanceType dist, IndexType index)
Definition nanoflann.hpp:326
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:333
Definition nanoflann.hpp:902
~PooledAllocator()
Definition nanoflann.hpp:938
void free_all()
Definition nanoflann.hpp:941
void * allocateBytes(const size_t req_size)
Definition nanoflann.hpp:957
T * allocate(const size_t count=1)
Definition nanoflann.hpp:1006
PooledAllocator()
Definition nanoflann.hpp:933
Definition nanoflann.hpp:348
bool addPoint(DistanceType dist, IndexType index)
Definition nanoflann.hpp:388
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:395
Definition nanoflann.hpp:411
ResultItem< IndexType, DistanceType > worst_item() const
Definition nanoflann.hpp:452
bool addPoint(DistanceType dist, IndexType index)
Definition nanoflann.hpp:440
std::enable_if< has_assign< Container >::value, void >::type assign(Container &c, const size_t nElements, const T &value)
Definition nanoflann.hpp:199
std::enable_if< has_resize< Container >::value, void >::type resize(Container &c, const size_t nElements)
Definition nanoflann.hpp:178
constexpr T pi_const()
Definition nanoflann.hpp:145
bool has_flag(KDTreeSingleIndexAdaptorFlags f, KDTreeSingleIndexAdaptorFlags flag)
Definition nanoflann.hpp:852
Definition nanoflann.hpp:217
bool operator()(const PairType &p1, const PairType &p2) const
Definition nanoflann.hpp:220
Definition nanoflann.hpp:1114
Definition nanoflann.hpp:1089
Offset right
Indices of points in leaf node.
Definition nanoflann.hpp:1096
Dimension divfeat
Dimension used for subdivision. The values used for subdivision.
Definition nanoflann.hpp:1100
Node * child1
Definition nanoflann.hpp:1107
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:4417
KDTreeEigenMatrixAdaptor(const self_t &)=delete
KDTreeEigenMatrixAdaptor(self_t &&)=delete
typename index_t::Offset Offset
Definition nanoflann.hpp:4368
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:4373
Definition nanoflann.hpp:2729
Definition nanoflann.hpp:859
Size invalid_count
number of tombstoned nodes in subtree
Definition nanoflann.hpp:2812
Dimension divfeat
splitting axis at this node
Definition nanoflann.hpp:2805
bool treeDeleted
whole subtree lazily tombstoned
Definition nanoflann.hpp:2807
IndexType ptIdx
index of the stored data point
Definition nanoflann.hpp:2804
INode * parent
parent (nullptr at the root)
Definition nanoflann.hpp:2810
Size subtree_size
number of nodes in this subtree
Definition nanoflann.hpp:2811
INode * child1
"< split" child (also free-list link)
Definition nanoflann.hpp:2808
INode * child2
">= split" child
Definition nanoflann.hpp:2809
BoundingBox box
AABB of all points (live+dead) in this subtree Cache of this node's own point coordinates,...
Definition nanoflann.hpp:2813
bool deleted
this node's point is tombstoned
Definition nanoflann.hpp:2806
Definition nanoflann.hpp:553
Definition nanoflann.hpp:615
Definition nanoflann.hpp:682
Definition nanoflann.hpp:538
Definition nanoflann.hpp:236
DistanceType second
Distance from sample to query point.
Definition nanoflann.hpp:243
IndexType first
Index of the sample in the dataset.
Definition nanoflann.hpp:242
Definition nanoflann.hpp:721
DistanceType accum_dist(const U a, const V b, const size_t) const
Definition nanoflann.hpp:740
Definition nanoflann.hpp:764
Definition nanoflann.hpp:875
bool sorted
only for radius search, require neighbors sorted by distance (default: true)
Definition nanoflann.hpp:879
float eps
search for eps-approximate neighbors (default: 0)
Definition nanoflann.hpp:878
Definition nanoflann.hpp:1022
Definition nanoflann.hpp:166
Definition nanoflann.hpp:156
Definition nanoflann.hpp:789
Definition nanoflann.hpp:786
Definition nanoflann.hpp:799
Definition nanoflann.hpp:809
Definition nanoflann.hpp:806
Definition nanoflann.hpp:796
Definition nanoflann.hpp:818
Definition nanoflann.hpp:815
Definition nanoflann.hpp:827
Definition nanoflann.hpp:824