Library of Assembled Shared Sources
aabb_tree.inl
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2024 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43
44
45#ifndef LASS_GUARDIAN_OF_INCLUSION_SPAT_AABB_TREE_INL
46#define LASS_GUARDIAN_OF_INCLUSION_SPAT_AABB_TREE_INL
47
48#include "spat_common.h"
49#include "aabb_tree.h"
50
51#include <cstddef>
52
53namespace lass
54{
55namespace spat
56{
57
58// --- public --------------------------------------------------------------------------------------
59
60template <typename O, typename OT, typename SH>
61AabbTree<O, OT, SH>::AabbTree(const TSplitHeuristics& heuristics):
62 SH(heuristics),
63 objects_(),
64 nodes_(),
65 end_(new TObjectIterator)
66{
67}
68
69
70
71template <typename O, typename OT, typename SH>
72AabbTree<O, OT, SH>::AabbTree(TObjectIterator first, TObjectIterator last, const TSplitHeuristics& heuristics):
73 SH(heuristics),
74 objects_(),
75 nodes_(),
76 end_(new TObjectIterator(last))
77{
78 if (first != last)
79 {
80 std::ptrdiff_t n = last - first;
81 if (n < 0)
82 {
83 LASS_THROW("AabbTree: invalid range");
84 }
85 if (static_cast<size_t>(n) > static_cast<size_t>(Node::sentinelInternal))
86 {
87 LASS_THROW("AabbTree: too many objects");
88 }
89 TInputs inputs;
90 inputs.reserve(static_cast<size_t>(n));
91 for (TObjectIterator i = first; i != last; ++i)
92 {
93 inputs.push_back(Input(TObjectTraits::objectAabb(i), i));
94 }
95 balance(inputs.begin(), inputs.end());
96 }
97}
98
99
100template <typename O, typename OT, typename SH>
101AabbTree<O, OT, SH>::AabbTree(TSelf&& other) noexcept:
102 SH(std::forward<TSplitHeuristics>(other)),
103 objects_(std::move(other.objects_)),
104 nodes_(std::move(other.nodes_)),
105 end_(std::move(other.end_))
106{
107}
108
109
110template <typename O, typename OT, typename SH>
111AabbTree<O, OT, SH>& AabbTree<O, OT, SH>::operator=(TSelf&& other) noexcept
112{
113 TSplitHeuristics::operator=(std::forward<TSplitHeuristics>(other));
114 objects_ = std::move(other.objects_);
115 nodes_ = std::move(other.nodes_);
116 end_ = std::move(other.end_);
117 return *this;
118}
119
120
121template <typename O, typename OT, typename SH>
122void AabbTree<O, OT, SH>::reset(TObjectIterator first, TObjectIterator last)
123{
124 TSelf temp(first, last, static_cast<const SH&>(*this));
125 swap(temp);
126}
127
128
129
130template <typename O, typename OT, typename SH> inline
131const typename AabbTree<O, OT, SH>::TAabb
132AabbTree<O, OT, SH>::aabb() const
133{
134 if (isEmpty())
136 return TObjectTraits::aabbEmpty();
137 }
138 return nodes_[0].aabb();
139}
141
143/** Check whether there's any object in the tree that contains @a point.
144 */
145template <typename O, typename OT, typename SH> inline
146bool AabbTree<O, OT, SH>::contains(const TPoint& point, const TInfo* info) const
147{
148 if (isEmpty())
149 {
150 return false;
151 }
152 return doContains(0, point, info);
153}
154
155
156
157/** Find all objects that contain @a point.
158 */
159template <typename O, typename OT, typename SH>
160template <typename OutputIterator> inline
161OutputIterator AabbTree<O, OT, SH>::find(const TPoint& point, OutputIterator result, const TInfo* info) const
162{
163 if (isEmpty())
164 {
165 return result;
166 }
167 return doFind(0, point, result, info);
168}
169
170
171
172/** Find all objects that intersect (overlap) with @a box.
173 */
174template <typename O, typename OT, typename SH>
175template <typename OutputIterator> inline
176OutputIterator AabbTree<O, OT, SH>::find(const TAabb& box, OutputIterator result, const TInfo* info) const
177{
178 if (isEmpty())
179 {
180 return result;
181 }
182 return doFind(0, box, result, info);
183}
184
185
186
187/** Find all objects that have an intersection with @a ray between @a tMin and @a tMax.
188 */
189template <typename O, typename OT, typename SH>
190template <typename OutputIterator> inline
191OutputIterator AabbTree<O, OT, SH>::find(const TRay& ray, TParam tMin, TParam tMax, OutputIterator result, const TInfo* info) const
192{
193 if (isEmpty())
194 {
195 return result;
196 }
197 const TVector& dir = ray.direction();
198 const TVector invDir = TObjectTraits::vectorReciprocal(dir);
199#if LASS_SPAT_AABB_TREE_STACK_SIZE
200 TIndex stack[LASS_SPAT_AABB_TREE_STACK_SIZE];
201 TIndex stackSize = 0;
202 stack[stackSize++] = 0;
203 while (stackSize > 0)
204 {
205 const TIndex index = stack[--stackSize];
206 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
207 LASS_ASSERT(index < nodes_.size());
208 const Node& node = nodes_[index];
209
210 if (!volumeIntersects(node.aabb(), ray, invDir, tMin, tMax))
211 {
212 continue;
213 }
214 if (node.isInternal())
215 {
216 if (stackSize + 2 >= LASS_SPAT_AABB_TREE_STACK_SIZE)
217 {
218 // fall back to recursive implementation
219 result = doFind(index + 1, ray, invDir, tMin, tMax, result, info);
220 result = doFind(node.right(), ray, invDir, tMin, tMax, result, info);
221 continue;
222 }
223 LASS_ASSERT(stackSize + 2 < LASS_SPAT_AABB_TREE_STACK_SIZE);
224 stack[stackSize++] = node.right();
225 stack[stackSize++] = index + 1;
226 continue;
227 }
228 for (TIndex i = node.first(); i != node.last(); ++i)
229 {
230 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
231 if (TObjectTraits::objectIntersects(objects_[i], ray, tMin, tMax, info))
232 {
233 *result++ = objects_[i];
234 }
235 }
236 }
237 return result;
238#else
239 return doFind(0, ray, invDir, tMin, tMax, result, info);
240#endif
241}
242
243
244
245template <typename O, typename OT, typename SH>
246typename AabbTree<O, OT, SH>::TObjectIterator inline
247AabbTree<O, OT, SH>::intersect(const TRay& ray, TReference t, TParam tMin, const TInfo* info) const
248{
249 const TVector& dir = ray.direction();
250 const TVector invDir = TObjectTraits::vectorReciprocal(dir);
251 TValue tRoot;
252 if (isEmpty() || !volumeIntersect(nodes_.front().aabb(), ray, invDir, tRoot, tMin))
253 {
254 return *end_;
255 }
256#if LASS_SPAT_AABB_TREE_STACK_SIZE
257 struct Visit
258 {
259 TIndex index;
260 TValue tNear;
261 Visit(TIndex index = 0, TValue tNear = 0) : index(index), tNear(tNear) {}
262 };
263 Visit stack[LASS_SPAT_AABB_TREE_STACK_SIZE];
264 TIndex stackSize = 0;
265 stack[stackSize++] = Visit(0, tRoot);
266 TValue tBest = 0;
267 TObjectIterator best = *end_;
268 while (stackSize > 0)
269 {
270 const Visit visit = stack[--stackSize];
271 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
272 LASS_ASSERT(visit.index < nodes_.size());
273 const Node& node = nodes_[visit.index];
274
275 if (best != *end_ && tBest < visit.tNear)
276 {
277 continue; // we already have a closer hit than what this node can offer
278 }
279
280 if (stackSize + 2 >= LASS_SPAT_AABB_TREE_STACK_SIZE)
281 {
282 // fall back to recursive implementation
283 TValue tb = 0;
284 TObjectIterator b = doIntersect(0, ray, invDir, tb, tMin, info);
285 if (best == *end_ || tb < tBest)
286 {
287 tBest = tb;
288 best = b;
289 }
290 continue;
291 }
292
293 if (node.isLeaf())
294 {
295 for (TIndex i = node.first(); i != node.last(); ++i)
296 {
297 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
298 TValue tCandidate = 0;
299 if (TObjectTraits::objectIntersect(objects_[i], ray, tCandidate, tMin, info))
300 {
301 if (best == *end_ || tCandidate < tBest)
302 {
303 tBest = tCandidate;
304 best = objects_[i];
305 }
306 }
307 }
308 continue;
309 }
310
311 TIndex left = visit.index + 1;
312 TIndex right = node.right();
313 TValue tLeftBox = 0, tRightBox = 0;
314 const bool hitsLeft = volumeIntersect(nodes_[left].aabb(), ray, invDir, tLeftBox, tMin);
315 const bool hitsRight = volumeIntersect(nodes_[right].aabb(), ray, invDir, tRightBox, tMin);
316
317 if (hitsLeft)
318 {
319 if (hitsRight)
320 {
321 // ok, we intersect both childs. Visit the box that is nearest first.
322 if (tRightBox < tLeftBox)
323 {
324 std::swap(tLeftBox, tRightBox);
325 std::swap(left, right);
326 }
327 // left is nearest, so push right first (it'll be visited as last)
328 stack[stackSize++] = Visit(right, tRightBox);
329 stack[stackSize++] = Visit(left, tLeftBox);
330 }
331 else
332 {
333 stack[stackSize++] = Visit(left, tLeftBox);
334 continue;
335 }
336 }
337 else if (hitsRight)
338 {
339 stack[stackSize++] = Visit(right, tRightBox);
340 }
341 }
342 if (best != *end_)
343 {
344 t = tBest;
345 }
346 return best;
347#else
348 return doIntersect(0, ray, invDir, t, tMin, info);
349#endif
350}
351
352
353
354template <typename O, typename OT, typename SH>
355bool inline AabbTree<O, OT, SH>::intersects(const TRay& ray, TParam tMin, TParam tMax, const TInfo* info) const
356{
357 if (isEmpty())
358 {
359 return false;
360 }
361 const TVector& dir = ray.direction();
362 const TVector invDir = TObjectTraits::vectorReciprocal(dir);
363#if LASS_SPAT_AABB_TREE_STACK_SIZE
364 TIndex stack[LASS_SPAT_AABB_TREE_STACK_SIZE];
365 TIndex stackSize = 0;
366 stack[stackSize++] = 0;
367 while (stackSize > 0)
368 {
369 const TIndex index = stack[--stackSize];
370
371 if (stackSize + 2 >= LASS_SPAT_AABB_TREE_STACK_SIZE)
372 {
373 // fall back to recursive implementation
374 if (doIntersects(index, ray, invDir, tMin, tMax, info))
375 {
376 return true;
377 }
378 continue;
379 }
380
381 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
382 LASS_ASSERT(index < nodes_.size());
383 const Node& node = nodes_[index];
384
385 if (!volumeIntersects(node.aabb(), ray, invDir, tMin, tMax))
386 {
387 continue;
388 }
389
390 if (node.isLeaf())
391 {
392 for (TIndex i = node.first(); i != node.last(); ++i)
393 {
394 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
395 if (TObjectTraits::objectIntersects(objects_[i], ray, tMin, tMax, info))
396 {
397 return true;
398 }
399 }
400 continue;
401 }
402
403 LASS_ASSERT(stackSize + 2 < LASS_SPAT_AABB_TREE_STACK_SIZE);
404 stack[stackSize++] = node.right();
405 stack[stackSize++] = index + 1;
406 }
407 return false;
408#else
409 return doIntersects(0, ray, invDir, tMin, tMax, info);
410#endif
411}
412
413
414
415template <typename O, typename OT, typename SH>
416const typename AabbTree<O, OT, SH>::Neighbour
417AabbTree<O, OT, SH>::nearestNeighbour(const TPoint& point, const TInfo* info) const
418{
419 Neighbour nearest(*end_, std::numeric_limits<TValue>::infinity());
420 if (isEmpty())
421 {
422 return nearest;
423 }
424#if LASS_SPAT_AABB_TREE_STACK_SIZE
425 struct Visit
426 {
427 TIndex index;
428 TValue squaredDistance;
429 Visit(TIndex index = 0, TValue squaredDistance = 0) : index(index), squaredDistance(squaredDistance) {}
430 };
431 Visit stack[LASS_SPAT_AABB_TREE_STACK_SIZE];
432 TIndex stackSize = 0;
433 stack[stackSize++] = Visit(0, 0);
434 while (stackSize > 0)
435 {
436 const Visit visit = stack[--stackSize];
437 if (visit.squaredDistance >= nearest.squaredDistance())
438 {
439 continue; // we already have a closer hit than what this node can offer
440 }
441
442 if (stackSize + 2 >= LASS_SPAT_AABB_TREE_STACK_SIZE)
443 {
444 // fall back to recursive implementation
445 doNearestNeighbour(visit.index, point, info, nearest);
446 continue;
447 }
448
449 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
450 LASS_ASSERT(nearest.squaredDistance() >= 0);
451 LASS_ASSERT(visit.index < nodes_.size());
452 const Node& node = nodes_[visit.index];
453
454 if (node.isLeaf())
455 {
456 for (TIndex i = node.first(); i != node.last(); ++i)
457 {
458 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
459 const TValue squaredDistance = TObjectTraits::objectSquaredDistance(objects_[i], point, info);
460 if (squaredDistance < nearest.squaredDistance())
461 {
462 nearest = Neighbour(objects_[i], squaredDistance);
463 }
464 }
465 continue;
466 }
467
468 TIndex children[2];
469 TValue squaredDistances[2];
470 getChildren(visit.index, point, children, squaredDistances);
471 // children[0] is the nearest child, so push it as last (it'll be visited first)
472 stack[stackSize++] = Visit(children[1], squaredDistances[1]);
473 stack[stackSize++] = Visit(children[0], squaredDistances[0]);
474 }
475#else
476 doNearestNeighbour(0, point, info, nearest);
477#endif
478 return nearest;
479}
480
481
482
483template <class O, class OT, typename SH>
484template <typename RandomAccessIterator>
485RandomAccessIterator
486AabbTree<O, OT, SH>::rangeSearch(
487 const TPoint& target, TParam maxRadius, size_t maxCount, RandomAccessIterator first,
488 const TInfo* info) const
489{
490 if (isEmpty() || maxRadius == 0)
491 {
492 return first;
493 }
494 TValue squaredRadius = maxRadius * maxRadius;
495#if LASS_SPAT_AABB_TREE_STACK_SIZE
496 RandomAccessIterator last = first;
497 struct Visit
498 {
499 TIndex index;
500 TValue squaredDistance;
501 Visit(TIndex index = 0, TValue squaredDistance = 0) : index(index), squaredDistance(squaredDistance) {}
502 };
503 Visit stack[LASS_SPAT_AABB_TREE_STACK_SIZE];
504 TIndex stackSize = 0;
505 stack[stackSize++] = Visit(0, 0);
506 while (stackSize > 0)
507 {
508 const Visit visit = stack[--stackSize];
509 if (visit.squaredDistance >= squaredRadius)
510 {
511 continue; // node is too far away
512 }
513
514 if (stackSize + 2 >= LASS_SPAT_AABB_TREE_STACK_SIZE)
515 {
516 // fall back to recursive implementation
517 last = doRangeSearch(visit.index, target, squaredRadius, maxCount, first, last, info);
518 continue;
519 }
520
521 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
522 LASS_ASSERT(visit.index < nodes_.size());
523 const Node& node = nodes_[visit.index];
524
525 if (node.isLeaf())
526 {
527 for (TIndex i = node.first(); i != node.last(); ++i)
528 {
529 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
530 const TValue sqrDist = TObjectTraits::objectSquaredDistance(objects_[i], target, info);
531 if (sqrDist < squaredRadius)
532 {
533 *last++ = Neighbour(objects_[i], sqrDist);
534 std::push_heap(first, last);
535 LASS_ASSERT(last >= first);
536 if (static_cast<size_t>(last - first) > maxCount)
537 {
538 std::pop_heap(first, last);
539 --last;
540 squaredRadius = first->squaredDistance();
541 }
542 }
543 }
544 continue;
545 }
546
547 TIndex children[2];
548 TValue squaredDistances[2];
549 getChildren(visit.index, target, children, squaredDistances);
550 // children[0] is the nearest child, so push it as last (it'll be visited first)
551 stack[stackSize++] = Visit(children[1], squaredDistances[1]);
552 stack[stackSize++] = Visit(children[0], squaredDistances[0]);
553 }
554 return last;
555#else
556 return doRangeSearch(0, target, squaredRadius, maxCount, first, first, info);
557#endif
558}
559
560
561
562template <typename O, typename OT, typename SH>
563void AabbTree<O, OT, SH>::swap(TSelf& other)
564{
565 SH::swap(other);
566 nodes_.swap(other.nodes_);
567 objects_.swap(other.objects_);
568 end_.swap(other.end_);
569}
570
571
572
573template <typename O, typename OT, typename SH>
574bool AabbTree<O, OT, SH>::isEmpty() const
575{
576 return objects_.empty();
577}
578
579
580
581template <typename O, typename OT, typename SH>
582const typename AabbTree<O, OT, SH>::TObjectIterator
583AabbTree<O, OT, SH>::end() const
584{
585 return *end_;
586}
587
588
589
590template <typename O, typename OT, typename SH>
591void AabbTree<O, OT, SH>::reset()
592{
593 TSelf temp(static_cast<const SH&>(*this));
594 swap(temp);
595}
596
597
598
599// --- protected -----------------------------------------------------------------------------------
600
601
602
603// --- private -------------------------------------------------------------------------------------
604
605template <typename O, typename OT, typename SH>
606typename AabbTree<O, OT, SH>::TIndex
607AabbTree<O, OT, SH>::balance(TInputIterator first, TInputIterator last)
608{
609 const SplitInfo<OT> split = TSplitHeuristics::template split<OT>(first, last);
610 if (split.isLeaf())
611 {
612 return addLeafNode(split.aabb, first, last);
613 }
614
615 TInputIterator middle = std::partition(first, last, impl::Splitter<TObjectTraits>(split));
616 if (middle == first || middle == last)
617 {
618 const std::ptrdiff_t halfSize = (last - first) / 2;
619 LASS_ASSERT(halfSize > 0);
620 middle = first + halfSize;
621 std::nth_element(first, middle, last, impl::LessAxis<TObjectTraits>(split.axis));
622 }
623 LASS_ASSERT(middle != first && middle != last);
624
625 const TIndex node = addInternalNode(split.aabb);
626 [[maybe_unused]] const TIndex left = balance(first, middle);
627 LASS_ASSERT(left == node + 1);
628 const TIndex right = balance(middle, last);
629 nodes_[node].setRight(right);
630 return node;
631}
632
633
634
635template <typename O, typename OT, typename SH>
636typename AabbTree<O, OT, SH>::TIndex
637AabbTree<O, OT, SH>::addLeafNode(
638 const TAabb& aabb, TInputIterator first, TInputIterator last)
639{
640 LASS_ASSERT(objects_.size() <= Node::sentinelInternal);
641 const TIndex begin = static_cast<TIndex>(objects_.size());
642 while (first != last)
643 {
644 objects_.push_back((first++)->object);
645 }
646 LASS_ASSERT(objects_.size() <= Node::sentinelInternal);
647 const TIndex end = static_cast<TIndex>(objects_.size());
648 nodes_.push_back(Node(aabb, begin, end));
649
650 LASS_ASSERT(nodes_.size() > 0);
651 return static_cast<TIndex>(nodes_.size() - 1);
652}
653
654
655
656template <typename O, typename OT, typename SH>
657typename AabbTree<O, OT, SH>::TIndex
658AabbTree<O, OT, SH>::addInternalNode(const TAabb& aabb)
659{
660 nodes_.push_back(Node(aabb));
661 LASS_ASSERT(objects_.size() <= Node::sentinelInternal);
662 return static_cast<TIndex>(nodes_.size() - 1);
663}
664
665
666
667template <typename O, typename OT, typename SH>
668bool AabbTree<O, OT, SH>::doContains(TIndex rootIndex, const TPoint& point, const TInfo* info) const
669{
670#if LASS_SPAT_AABB_TREE_STACK_SIZE
671 TIndex stack[LASS_SPAT_AABB_TREE_STACK_SIZE];
672 TIndex stackSize = 0;
673 stack[stackSize++] = rootIndex;
674 while (stackSize > 0)
675 {
676 const TIndex index = stack[--stackSize];
677 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
678 LASS_ASSERT(index < nodes_.size());
679 const Node& node = nodes_[index];
680
681 if (!TObjectTraits::aabbContains(node.aabb(), point))
682 {
683 continue;
684 }
685 if (node.isInternal())
686 {
687 if (stackSize + 2 >= LASS_SPAT_AABB_TREE_STACK_SIZE)
688 {
689 // fall back to recursive implementation
690 if (doContains(index + 1, point, info) || doContains(node.right(), point, info))
691 {
692 return true;
693 }
694 continue;
695 }
696 LASS_ASSERT(stackSize + 2 < LASS_SPAT_AABB_TREE_STACK_SIZE);
697 stack[stackSize++] = node.right();
698 stack[stackSize++] = index + 1;
699 continue;
700 }
701 for (TIndex i = node.first(); i != node.last(); ++i)
702 {
703 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
704 if (TObjectTraits::objectContains(objects_[i], point, info))
705 {
706 return true;
707 }
708 }
709 }
710#else
711 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
712 LASS_ASSERT(rootIndex < nodes_.size());
713 const Node& node = nodes_[rootIndex];
714
715 if (!TObjectTraits::aabbContains(node.aabb(), point))
716 {
717 return false;
718 }
719 if (node.isInternal())
720 {
721 return doContains(rootIndex + 1, point, info) || doContains(node.right(), point, info);
722 }
723 for (TIndex i = node.first(); i != node.last(); ++i)
724 {
725 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
726 if (TObjectTraits::objectContains(objects_[i], point, info))
727 {
728 return true;
729 }
730 }
731#endif
732 return false;
733}
734
735
736
737template <typename O, typename OT, typename SH>
738template <typename OutputIterator>
739OutputIterator AabbTree<O, OT, SH>::doFind(TIndex rootIndex, const TPoint& point, OutputIterator result, const TInfo* info) const
740{
741#if LASS_SPAT_AABB_TREE_STACK_SIZE
742 TIndex stack[LASS_SPAT_AABB_TREE_STACK_SIZE];
743 TIndex stackSize = 0;
744 stack[stackSize++] = rootIndex;
745 while (stackSize > 0)
746 {
747 const TIndex index = stack[--stackSize];
748 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
749 LASS_ASSERT(index < nodes_.size());
750 const Node& node = nodes_[index];
751
752 if (!TObjectTraits::aabbContains(node.aabb(), point))
753 {
754 continue;
755 }
756 if (node.isInternal())
757 {
758 if (stackSize + 2 >= LASS_SPAT_AABB_TREE_STACK_SIZE)
759 {
760 // fall back to recursive implementation
761 result = doFind(index + 1, point, result, info);
762 result = doFind(node.right(), point, result, info);
763 continue;
764 }
765 LASS_ASSERT(stackSize + 2 < LASS_SPAT_AABB_TREE_STACK_SIZE);
766 stack[stackSize++] = node.right();
767 stack[stackSize++] = index + 1;
768 continue;
769 }
770 for (TIndex i = node.first(); i != node.last(); ++i)
771 {
772 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
773 if (TObjectTraits::objectContains(objects_[i], point, info))
774 {
775 *result++ = objects_[i];
776 }
777 }
778 }
779 return result;
780#else
781 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
782 LASS_ASSERT(rootIndex < nodes_.size());
783 const Node& node = nodes_[rootIndex];
784
785 if (!TObjectTraits::aabbContains(node.aabb(), point))
786 {
787 return result;
788 }
789 if (node.isInternal())
790 {
791 result = doFind(rootIndex + 1, point, result, info);
792 return doFind(node.right(), point, result, info);
793 }
794 for (TIndex i = node.first(); i != node.last(); ++i)
795 {
796 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
797 if (TObjectTraits::objectContains(objects_[i], point, info))
798 {
799 *result++ = objects_[i];
800 }
801 }
802 return result;
803#endif
804}
805
806
807
808template <typename O, typename OT, typename SH>
809template <typename OutputIterator>
810OutputIterator AabbTree<O, OT, SH>::doFind(TIndex rootIndex, const TAabb& box, OutputIterator result, const TInfo* info) const
811{
812#if LASS_SPAT_AABB_TREE_STACK_SIZE
813 TIndex stack[LASS_SPAT_AABB_TREE_STACK_SIZE];
814 TIndex stackSize = 0;
815 stack[stackSize++] = rootIndex;
816 while (stackSize > 0)
817 {
818 const TIndex index = stack[--stackSize];
819 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
820 LASS_ASSERT(index < nodes_.size());
821 const Node& node = nodes_[index];
822
823 if (!TObjectTraits::aabbIntersects(node.aabb(), box))
824 {
825 continue;
826 }
827 if (node.isInternal())
828 {
829 if (stackSize + 2 >= LASS_SPAT_AABB_TREE_STACK_SIZE)
830 {
831 // fall back to recursive implementation
832 result = doFind(index + 1, box, result, info);
833 result = doFind(node.right(), box, result, info);
834 continue;
835 }
836 LASS_ASSERT(stackSize + 2 < LASS_SPAT_AABB_TREE_STACK_SIZE);
837 stack[stackSize++] = node.right();
838 stack[stackSize++] = index + 1;
839 continue;
840 }
841 for (TIndex i = node.first(); i != node.last(); ++i)
842 {
843 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
844 if (TObjectTraits::objectIntersects(objects_[i], box, info))
845 {
846 *result++ = objects_[i];
847 }
848 }
849 }
850 return result;
851#else
852 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
853 LASS_ASSERT(rootIndex < nodes_.size());
854 const Node& node = nodes_[rootIndex];
855
856 if (!TObjectTraits::aabbIntersects(node.aabb(), box))
857 {
858 return result;
859 }
860 if (node.isInternal())
861 {
862 result = doFind(rootIndex + 1, box, result, info);
863 return doFind(node.right(), box, result, info);
864 }
865 for (TIndex i = node.first(); i != node.last(); ++i)
866 {
867 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
868 if (TObjectTraits::objectIntersects(objects_[i], box, info))
869 {
870 *result++ = objects_[i];
871 }
872 }
873 return result;
874#endif
875}
876
877
878
879template <typename O, typename OT, typename SH>
880template <typename OutputIterator>
881OutputIterator AabbTree<O, OT, SH>::doFind(TIndex index, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax, OutputIterator result, const TInfo* info) const
882{
883 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
884 LASS_ASSERT(index < nodes_.size());
885 const Node& node = nodes_[index];
886
887 if (!volumeIntersects(node.aabb(), ray, invDir, tMin, tMax))
888 {
889 return result;
890 }
891 if (node.isInternal())
892 {
893 result = doFind(index + 1, ray, invDir, tMin, tMax, result, info);
894 return doFind(node.right(), ray, invDir, tMin, tMax, result, info);
895 }
896 for (TIndex i = node.first(); i != node.last(); ++i)
897 {
898 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
899 if (TObjectTraits::objectIntersects(objects_[i], ray, tMin, tMax, info))
900 {
901 *result++ = objects_[i];
902 }
903 }
904 return result;
905}
906
907
908
909template <typename O, typename OT, typename SH>
910typename AabbTree<O, OT, SH>::TObjectIterator
911AabbTree<O, OT, SH>::doIntersect(TIndex index, const TRay& ray, const TVector& invDir, TReference t, TParam tMin, const TInfo* info) const
912{
913 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
914 LASS_ASSERT(index < nodes_.size());
915 const Node& node = nodes_[index];
916
917 if (node.isLeaf())
918 {
919 TValue tBest = 0;
920 TObjectIterator best = *end_;
921 for (TIndex i = node.first(); i != node.last(); ++i)
922 {
923 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
924 TValue tCandidate = 0;
925 if (TObjectTraits::objectIntersect(objects_[i], ray, tCandidate, tMin, info))
926 {
927 if (best == *end_ || tCandidate < tBest)
928 {
929 tBest = tCandidate;
930 best = objects_[i];
931 }
932 }
933 }
934 if (best != *end_)
935 {
936 t = tBest;
937 }
938 return best;
939 }
940
941 TIndex left = index + 1;
942 TIndex right = node.right();
943 TValue tLeftBox = 0, tRightBox = 0;
944 const bool hitsLeft = volumeIntersect(nodes_[left].aabb(), ray, invDir, tLeftBox, tMin);
945 const bool hitsRight = volumeIntersect(nodes_[right].aabb(), ray, invDir, tRightBox, tMin);
946
947 if (!hitsLeft)
948 {
949 return hitsRight ? doIntersect(right, ray, invDir, t, tMin, info) : *end_;
950 }
951 if (!hitsRight)
952 {
953 return doIntersect(left, ray, invDir, t, tMin, info);
954 }
955
956 // ok, we intersect both childs. Visit the box that is nearest first.
957 if (tRightBox < tLeftBox)
958 {
959 std::swap(tLeftBox, tRightBox);
960 std::swap(left, right);
961 }
962
963 TValue tLeft;
964 const TObjectIterator leftBest = doIntersect(left, ray, invDir, tLeft, tMin, info);
965 if (leftBest == *end_)
966 {
967 return doIntersect(right, ray, invDir, t, tMin, info);
968 }
969
970 if (tRightBox <= tLeft)
971 {
972 // right node might still have a closer hit.
973 TValue tRight;
974 const TObjectIterator rightBest = doIntersect(right, ray, invDir, tRight, tMin, info);
975 if (rightBest != *end_ && tRight < tLeft)
976 {
977 t = tRight;
978 return rightBest;
979 }
980 }
981
982 t = tLeft;
983 return leftBest;
984}
985
986
987
988template <typename O, typename OT, typename SH>
989bool AabbTree<O, OT, SH>::doIntersects(
990 TIndex index, const TRay& ray, const TVector& invDir, TParam tMin, const TParam tMax, const TInfo* info) const
991{
992 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
993 LASS_ASSERT(index < nodes_.size());
994 const Node& node = nodes_[index];
995
996 if (!volumeIntersects(node.aabb(), ray, invDir, tMin, tMax))
997 {
998 return false;
999 }
1000 if (node.isInternal())
1001 {
1002 return doIntersects(index + 1, ray, invDir, tMin, tMax, info)
1003 || doIntersects(node.right(), ray, invDir, tMin, tMax, info);
1004 }
1005 for (TIndex i = node.first(); i != node.last(); ++i)
1006 {
1007 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1008 if (TObjectTraits::objectIntersects(objects_[i], ray, tMin, tMax, info))
1009 {
1010 return true;
1011 }
1012 }
1013 return false;
1014}
1015
1016
1017
1018template <typename O, typename OT, typename SH>
1019void AabbTree<O, OT, SH>::doNearestNeighbour(
1020 TIndex index, const TPoint& target, const TInfo* info, Neighbour& best) const
1021{
1022 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1023 LASS_ASSERT(best.squaredDistance() >= 0);
1024
1025 const Node& node = nodes_[index];
1026
1027 if (node.isLeaf())
1028 {
1029 for (TIndex i = node.first(); i != node.last(); ++i)
1030 {
1031 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1032 const TValue squaredDistance = TObjectTraits::objectSquaredDistance(objects_[i], target, info);
1033 if (squaredDistance < best.squaredDistance())
1034 {
1035 best = Neighbour(objects_[i], squaredDistance);
1036 }
1037 }
1038 }
1039 else
1040 {
1041 TIndex children[2];
1042 TValue squaredDistances[2];
1043 getChildren(index, target, children, squaredDistances);
1044 if (squaredDistances[0] < best.squaredDistance())
1045 {
1046 doNearestNeighbour(children[0], target, info, best);
1047 if (squaredDistances[1] < best.squaredDistance())
1048 {
1049 doNearestNeighbour(children[1], target, info, best);
1050 }
1051 }
1052 }
1053}
1054
1055
1056
1057template <typename O, typename OT, typename SH>
1058template <typename RandomIterator>
1059RandomIterator AabbTree<O, OT, SH>::doRangeSearch(
1060 TIndex index, const TPoint& target, TReference squaredRadius, size_t maxCount,
1061 RandomIterator first, RandomIterator last, const TInfo* info) const
1062{
1063 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1064 LASS_ASSERT(squaredRadius >= 0);
1065
1066 const Node& node = nodes_[index];
1067
1068 if (node.isLeaf())
1069 {
1070 for (TIndex i = node.first(); i != node.last(); ++i)
1071 {
1072 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1073 const TValue sqrDist = TObjectTraits::objectSquaredDistance(objects_[i], target, info);
1074 if (sqrDist < squaredRadius)
1075 {
1076 *last++ = Neighbour(objects_[i], sqrDist);
1077 std::push_heap(first, last);
1078 LASS_ASSERT(last >= first);
1079 if (static_cast<size_t>(last - first) > maxCount)
1080 {
1081 std::pop_heap(first, last);
1082 --last;
1083 squaredRadius = first->squaredDistance();
1084 }
1085 }
1086 }
1087 return last;
1088 }
1089
1090 TIndex children[2];
1091 TValue squaredDistances[2];
1092 getChildren(index, target, children, squaredDistances);
1093 if (squaredDistances[0] < squaredRadius)
1094 {
1095 last = doRangeSearch(children[0], target, squaredRadius, maxCount, first, last, info);
1096 if (squaredDistances[1] < squaredRadius)
1097 {
1098 last = doRangeSearch(children[1], target, squaredRadius, maxCount, first, last, info);
1099 }
1100 }
1101
1102 return last;
1103}
1104
1105
1106
1107template <typename O, typename OT, typename SH>
1108void AabbTree<O, OT, SH>::getChildren(
1109 TIndex index, const TPoint& target, TIndex indices[2], TValue squaredDistances[2]) const
1110{
1111 const Node& node = nodes_[index];
1112 indices[0] = index + 1;
1113 indices[1] = node.right();
1114
1115 for (size_t i = 0; i < 2; ++i)
1116 {
1117 const Node& child = nodes_[indices[i]];
1118 squaredDistances[i] = 0;
1119 const TPoint& min = TObjectTraits::aabbMin(child.aabb());
1120 const TPoint& max = TObjectTraits::aabbMax(child.aabb());
1121 for (size_t k = 0; k < dimension; ++k)
1122 {
1123 const TValue x = TObjectTraits::coord(target, k);
1124 const TValue d = std::max(TObjectTraits::coord(min, k) - x, x - TObjectTraits::coord(max, k));
1125 if (d > 0)
1126 {
1127 squaredDistances[i] += d * d;
1128 }
1129 }
1130 }
1131
1132 if (squaredDistances[1] < squaredDistances[0])
1133 {
1134 std::swap(squaredDistances[0], squaredDistances[1]);
1135 std::swap(indices[0], indices[1]);
1136 }
1137}
1138
1139
1140
1141template <typename O, typename OT, typename SH>
1142bool AabbTree<O, OT, SH>::volumeIntersect(const TAabb& box, const TRay& ray, const TVector& invDir, TReference t, TParam tMin) const
1143{
1144 if (TObjectTraits::aabbContains(box, TObjectTraits::rayPoint(ray, tMin)))
1145 {
1146 t = tMin;
1147 return true;
1148 }
1149 return TObjectTraits::aabbIntersect(box, ray, invDir, t, tMin);
1150}
1151
1152
1153
1154template <typename O, typename OT, typename SH>
1155bool AabbTree<O, OT, SH>::volumeIntersects(const TAabb& box, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax) const
1156{
1157 TValue t = 0;
1158 return volumeIntersect(box, ray, invDir, t, tMin) && t <= tMax;
1159}
1160
1161
1162// --- free ----------------------------------------------------------------------------------------
1163
1164
1165
1166}
1167
1168}
1169
1170#endif
1171
1172// EOF
bool contains(const TPoint &point, const TInfo *info=0) const
Check whether there's any object in the tree that contains point.
OutputIterator find(const TPoint &point, OutputIterator result, const TInfo *info=0) const
Find all objects that contain point.
std::vector< std::basic_string< Char, Traits, Alloc > > split(const std::basic_string< Char, Traits, Alloc > &to_be_split)
Reflects the Python function split without seperator argument.
Definition basic_ops.h:211
spatial subdivisions, quadtrees, octrees, meshes in 2D and 3D, triangulators, ...
Definition aabb8_tree.h:80
Library for Assembled Shared Sources.
Definition config.h:53