9#include <Common/assertion.h>
24template<
typename Storage,
typename Item>
26 std::default_initializable<Storage> &&
27 std::copyable<Storage> &&
28 requires (Storage storage)
30 { storage[std::size_t{}] } -> std::convertible_to<Item>;
31 { storage.size() } -> std::convertible_to<std::size_t>;
38 typename PointCalculator,
59 , m_maxNodeItems (maxNodeItems)
61 , m_pointCalculator(pointCalculator)
63 PH_ASSERT_GT(maxNodeItems, 0);
72 build(std::move(items), buildCache);
84 m_items = std::move(items);
86 m_indexBuffer.clear();
87 if(m_items.size() == 0)
93 itemPoints.resize(m_items.size());
94 for(std::size_t i = 0; i < m_items.size(); ++i)
96 const auto& item = m_items[i];
99 itemPoints[i] = center;
102 PH_ASSERT_LE(m_items.size() - 1, std::numeric_limits<Index>::max());
104 itemIndices.resize(m_items.size());
105 for(std::size_t i = 0; i < m_items.size(); ++i)
107 itemIndices[i] =
static_cast<Index
>(i);
110 m_rootAABB = calcPointsAABB(itemIndices, itemPoints);
122 const real searchRadius,
123 std::vector<Item>& results)
const
125 PH_ASSERT_GT(m_numNodes, 0);
127 const real searchRadius2 = searchRadius * searchRadius;
132 [
this, location, searchRadius2, &results](
const Item& item)
135 const real dist2 = (itemPoint - location).lengthSquared();
136 if(dist2 < searchRadius2)
138 results.push_back(item);
145 const std::size_t maxItems,
146 std::vector<Item>& results)
const
148 PH_ASSERT_GT(m_numNodes, 0);
149 PH_ASSERT_GT(maxItems, 0);
153 auto isACloserThanB =
154 [
this, location](
const Item& itemA,
const Item& itemB) ->
bool
156 return (m_pointCalculator(itemA) - location).lengthSquared() <
157 (m_pointCalculator(itemB) - location).lengthSquared();
160 real searchRadius2 = std::numeric_limits<real>::max();
161 std::size_t numFoundItems = 0;
163 [
this, location, maxItems,
164 &searchRadius2, &numFoundItems, &isACloserThanB, &results]
174 if(numFoundItems < maxItems)
176 results.push_back(item);
180 if(numFoundItems == maxItems)
184 results.end() - maxItems,
189 const Item& furthestItem = results[results.size() - maxItems];
192 searchRadius2 = (m_pointCalculator(furthestItem) - location).lengthSquared();
199 const Item& furthestItem = results[results.size() - maxItems];
201 if(isACloserThanB(item, furthestItem))
205 results.end() - maxItems,
209 results.back() = item;
213 results.end() - maxItems,
218 searchRadius2 = (m_pointCalculator(furthestItem) - location).lengthSquared();
222 return searchRadius2;
231 template<
typename ItemHandler>
234 const real squaredSearchRadius,
235 ItemHandler itemHandler)
const
237 static_assert(std::is_invocable_v<ItemHandler, Item>,
238 "ItemHandler must accept an item as input.");
240 PH_ASSERT_GT(m_numNodes, 0);
241 PH_ASSERT_LE(squaredSearchRadius, std::numeric_limits<real>::max());
243 constexpr std::size_t MAX_STACK_HEIGHT = 64;
244 std::array<const Node*, MAX_STACK_HEIGHT> nodeStack;
246 const Node* currentNode = &(m_nodeBuffer[0]);
247 std::size_t stackHeight = 0;
250 PH_ASSERT(currentNode);
251 if(!currentNode->
isLeaf())
255 const real splitPlaneDiff = location[splitAxis] - splitPos;
257 const Node* nearNode;
259 if(splitPlaneDiff < 0)
261 nearNode = currentNode + 1;
267 farNode = currentNode + 1;
270 currentNode = nearNode;
271 if(squaredSearchRadius >= splitPlaneDiff * splitPlaneDiff)
273 PH_ASSERT(stackHeight < MAX_STACK_HEIGHT);
274 nodeStack[stackHeight++] = farNode;
282 for(std::size_t i = 0; i <
numItems; ++i)
284 const Index itemIndex = m_indexBuffer[indexBufferOffset + i];
285 const Item& item = m_items[itemIndex];
292 currentNode = nodeStack[--stackHeight];
302 template<
typename ItemHandler>
305 const real initialSquaredSearchRadius,
306 ItemHandler itemHandler)
const
308 static_assert(std::is_invocable_v<ItemHandler, Item>,
309 "ItemHandler must accept an item as input.");
311 using Return =
decltype(itemHandler(std::declval<Item>()));
312 static_assert(std::is_same_v<Return, real>,
313 "ItemHandler must return an potentially shrunk squared search radius.");
315 PH_ASSERT_GT(m_numNodes, 0);
316 PH_ASSERT_LE(initialSquaredSearchRadius, std::numeric_limits<real>::max());
323 real parentSplitPlaneDiff2;
326 constexpr std::size_t MAX_STACK_HEIGHT = 64;
327 std::array<NodeRecord, MAX_STACK_HEIGHT> nodeStack;
329 NodeRecord currentNode = {&(m_nodeBuffer[0]), 0};
330 std::size_t stackHeight = 0;
331 real currentRadius2 = initialSquaredSearchRadius;
334 PH_ASSERT(currentNode.node);
335 if(!currentNode.node->isLeaf())
337 const auto splitAxis = currentNode.node->getSplitAxis();
338 const real splitPos = currentNode.node->getSplitPos();
339 const real splitPlaneDiff = location[splitAxis] - splitPos;
341 const Node* nearNode;
343 if(splitPlaneDiff < 0)
345 nearNode = currentNode.node + 1;
346 farNode = &(m_nodeBuffer[currentNode.node->getPositiveChildIndex()]);
350 nearNode = &(m_nodeBuffer[currentNode.node->getPositiveChildIndex()]);
351 farNode = currentNode.node + 1;
354 const real splitPlaneDiff2 = splitPlaneDiff * splitPlaneDiff;
356 currentNode = {nearNode, 0};
357 if(currentRadius2 >= splitPlaneDiff2)
359 PH_ASSERT(stackHeight < MAX_STACK_HEIGHT);
360 nodeStack[stackHeight++] = {farNode, splitPlaneDiff2};
368 if(currentRadius2 >= currentNode.parentSplitPlaneDiff2)
370 const std::size_t
numItems = currentNode.node->numItems();
371 const std::size_t indexBufferOffset = currentNode.node->getIndexBufferOffset();
372 for(std::size_t i = 0; i <
numItems; ++i)
374 const Index itemIndex = m_indexBuffer[indexBufferOffset + i];
375 const Item& item = m_items[itemIndex];
378 const real shrunkRadius2 = itemHandler(item);
379 PH_ASSERT_LE(shrunkRadius2, currentRadius2);
380 currentRadius2 = shrunkRadius2;
386 currentNode = nodeStack[--stackHeight];
398 return m_items.size();
404 void buildNodeRecursive(
405 const std::size_t nodeIndex,
409 const std::size_t currentNodeDepth)
412 if(m_numNodes > m_nodeBuffer.size())
414 m_nodeBuffer.resize(m_numNodes * 2);
416 PH_ASSERT_LT(nodeIndex, m_nodeBuffer.size());
418 if(nodeItemIndices.size() <= m_maxNodeItems)
420 m_nodeBuffer[nodeIndex] =
Node::makeLeaf(nodeItemIndices, m_indexBuffer);
427 const std::size_t midIndicesIndex = nodeItemIndices.size() / 2;
429 nodeItemIndices.begin(),
430 nodeItemIndices.begin() + midIndicesIndex,
431 nodeItemIndices.end(),
432 [itemPoints, splitAxis](
const Index& a,
const Index& b) ->
bool
434 return itemPoints[a][splitAxis] < itemPoints[b][splitAxis];
437 const real splitPos = itemPoints[nodeItemIndices[midIndicesIndex]][splitAxis];
441 splitPosMinVertex[splitAxis] = splitPos;
442 splitPosMaxVertex[splitAxis] = splitPos;
449 nodeItemIndices.subspan(0, midIndicesIndex),
451 currentNodeDepth + 1);
453 const std::size_t positiveChildIndex = m_numNodes;
454 m_nodeBuffer[nodeIndex] =
Node::makeInner(splitPos, splitAxis, positiveChildIndex);
459 nodeItemIndices.subspan(midIndicesIndex),
461 currentNodeDepth + 1);
470 PH_ASSERT_GT(pointIndices.size(), 0);
473 for(std::size_t i = 1; i < pointIndices.size(); ++i)
475 pointsAABB.unionWith(points[pointIndices[i]]);
480 std::vector<Node> m_nodeBuffer;
481 std::size_t m_numNodes;
484 std::size_t m_maxNodeItems;
485 std::vector<Index> m_indexBuffer;
486 PointCalculator m_pointCalculator;
const TVector3< T > & getMaxVertex() const
Get the corner vertex of the maximum (+++) octant.
Definition TAABB3D.ipp:152
TVector3< T > getExtents() const
Get the side lengths of the bound.
Definition TAABB3D.ipp:171
const TVector3< T > & getMinVertex() const
Get the corner vertex of the minimum (—) octant.
Definition TAABB3D.ipp:146
An indexed kD-tree node with compacted memory layout.
Definition TIndexedKdtreeNode.h:23
bool isLeaf() const
Definition TIndexedKdtreeNode.h:181
std::size_t getSplitAxis() const
Definition TIndexedKdtreeNode.h:219
static TIndexedKdtreeNode makeInner(real splitPos, std::size_t splitAxisIndex, std::size_t positiveChildIndex)
Definition TIndexedKdtreeNode.h:105
std::size_t numItems() const
Definition TIndexedKdtreeNode.h:199
static TIndexedKdtreeNode makeLeaf(Index indexBufferOffset, std::size_t numItems)
Definition TIndexedKdtreeNode.h:128
std::size_t getIndexBufferOffset() const
Definition TIndexedKdtreeNode.h:248
std::size_t getPositiveChildIndex() const
Definition TIndexedKdtreeNode.h:189
real getSplitPos() const
Definition TIndexedKdtreeNode.h:209
Definition TIndexedPointKdtree.h:42
TIndexedPointKdtree(const std::size_t maxNodeItems, const PointCalculator &pointCalculator)
Creates empty tree. Call build() to populate the tree.
Definition TIndexedPointKdtree.h:54
void findWithinRange(const math::Vector3R &location, const real searchRadius, std::vector< Item > &results) const
Definition TIndexedPointKdtree.h:120
void build(ItemStorage items, BuildCache &buildCache)
Populate the tree. Better for multiple builds.
Definition TIndexedPointKdtree.h:80
std::size_t numItems() const
Definition TIndexedPointKdtree.h:396
void rangeTraversal(const math::Vector3R &location, const real squaredSearchRadius, ItemHandler itemHandler) const
Definition TIndexedPointKdtree.h:232
void findNearest(const math::Vector3R &location, const std::size_t maxItems, std::vector< Item > &results) const
Definition TIndexedPointKdtree.h:143
void build(ItemStorage items)
Populate the tree. Better for build once then read only.
Definition TIndexedPointKdtree.h:69
void nearestTraversal(const math::Vector3R &location, const real initialSquaredSearchRadius, ItemHandler itemHandler) const
Definition TIndexedPointKdtree.h:303
std::size_t maxDimension() const
Definition TVectorNBase.ipp:81
Definition TIndexedPointKdtree.h:25
Math functions and utilities.
Definition TransformInfo.h:10
TAABB3D< real > AABB3D
Definition TAABB3D.h:21
TVector3< real > Vector3R
Definition math_fwd.h:52
std::span< const T, EXTENT > TSpanView
Same as TSpan, except that the objects are const-qualified. Note that for pointer types,...
Definition TSpan.h:19
T & ref_access(T &ref)
Definition utility.h:97
std::span< T, EXTENT > TSpan
A contiguous sequence of objects of type T. Effectively the same as std::span.
Definition TSpan.h:12
Definition TIndexedPointKdtree.h:47
std::vector< math::Vector3R > itemPoints
Definition TIndexedPointKdtree.h:49
std::vector< Index > itemIndices
Definition TIndexedPointKdtree.h:48