Library of Assembled Shared Sources
quad_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/** @class lass::spat::QuadTree
46 * A spatial container for generic objects. The object needs a traits class which
47 * contains the necessary functions to perform the quad tree management for the
48 * particular ObjectType. The traits class needs as a basis the following interface:
49 * <tt>
50 * static TAabb aabb(const TSimplePolygon3D& iP);
51 * static bool contains( const TSimplePolygon3D& iP, const TPoint& point)
52 * </tt>
53 * The above functions are only examples. The dimensionality of the primitives must
54 * match but can be of any order. So the quad tree can be used to classify in
55 * 2 and 3 dimensions. In three dimensions the more common name is OctTree.
56 *
57 * Higher level divisions can in theory be supported but the dimensional specific
58 * part must be reimplemented. Altough this is only 2 functions and could be written
59 * generally this is not yet available.
60 *
61 * @brief a Quad tree for general objects
62 * @author Tom De Muer [TDM]
63 */
64
65#ifndef LASS_GUARDIAN_OF_INCLUSION_SPAT_QUAD_TREE_INL
66#define LASS_GUARDIAN_OF_INCLUSION_SPAT_QUAD_TREE_INL
67
68#include "spat_common.h"
69#include "quad_tree.h"
70
71namespace lass
72{
73namespace spat
74{
75
76// --- public --------------------------------------------------------------------------------------
77
78template <typename O, typename OT, typename SH>
79QuadTree<O, OT, SH>::QuadTree(const TSplitHeuristics& heuristics):
80 SH(heuristics),
81 aabb_(),
82 root_(0),
83 end_(),
84 nodesAllocator_(new TNodesAllocator(sizeof(QuadNode) * numChildren)),
85 numObjects_(0)
86{
87}
88
89
90
91 /** empty quadtree with fixed bounding box
92 */
93template <typename O, typename OT, typename SH>
94QuadTree<O, OT, SH>::QuadTree(const TAabb& aabb, const TSplitHeuristics& heuristics):
95 SH(heuristics),
96 aabb_(aabb),
97 root_(0),
98 end_(),
99 nodesAllocator_(new TNodesAllocator(sizeof(QuadNode) * numChildren)),
100 numObjects_(0)
101{
102 root_ = new QuadNode(aabb, *nodesAllocator_);
103}
104
105
106
107/** empty quadtree with fixed bounding box and end iterator.
108 */
109template <typename O, typename OT, typename SH>
110QuadTree<O, OT, SH>::QuadTree(const TAabb& aabb, const TObjectIterator end, const TSplitHeuristics& heuristics):
111 SH(heuristics),
112 aabb_(aabb),
113 root_(0),
114 end_(end),
115 nodesAllocator_(new TNodesAllocator(sizeof(QuadNode) * numChildren)),
116 numObjects_(0)
117{
118 root_ = new QuadNode(aabb, *nodesAllocator_);
119}
120
121
122
123/** quadtree from objects, with computed bounding box
124 */
125template <typename O, typename OT, typename SH>
126QuadTree<O, OT, SH>::QuadTree(TObjectIterator first, TObjectIterator last, const TSplitHeuristics& heuristics):
127 SH(heuristics),
128 root_(0),
129 end_(last),
130 nodesAllocator_(new TNodesAllocator(sizeof(QuadNode) * numChildren)),
131 numObjects_(0)
133 aabb_ = TObjectTraits::aabbEmpty();
134 for (TObjectIterator i = first; i != last; ++i)
136 aabb_ = TObjectTraits::aabbJoin(aabb_, TObjectTraits::objectAabb(i));
137 }
138
139 // growth hack! Slightly grow the box with a fraction of the diagonal, so that we don't have any sides of zero length.
140 //*
141 TPoint min = TObjectTraits::aabbMin(aabb_);
142 TPoint max = TObjectTraits::aabbMax(aabb_);
143 const TValue growth = num::sqrt(squaredDistance(min, max)) / 1000;
144 for (size_t k = 0; k < dimension; ++k)
146 TObjectTraits::coord(min, k, TObjectTraits::coord(min, k) - growth);
147 TObjectTraits::coord(max, k, TObjectTraits::coord(max, k) + growth);
148 }
149 aabb_ = TObjectTraits::aabbMake(min, max);
150 /**/
151
152 root_ = new QuadNode(aabb_, *nodesAllocator_);
153 while (first != last)
154 {
155 add(first++);
157}
158
160/** move constructor
161 */
162template <typename O, typename OT, typename SH>
163QuadTree<O, OT, SH>::QuadTree(TSelf&& other) noexcept:
164 SH(std::forward<SH>(other)),
165 aabb_(other.aabb_),
166 root_(other.root_),
167 end_(other.end_),
168 nodesAllocator_(std::move(other.nodesAllocator_)),
169 numObjects_(other.numObjects_)
170{
171 other.root_ = nullptr;
172}
173
174
175template <typename O, typename OT, typename SH>
176QuadTree<O, OT, SH>::~QuadTree()
177{
178 delete root_;
179}
180
181
182/** move constructor
183 */
184template <typename O, typename OT, typename SH>
185QuadTree<O, OT, SH>& QuadTree<O, OT, SH>::operator=(TSelf&& other) noexcept
186{
187 SH::operator=(std::forward<TSelf>(other));
188 aabb_ = other.aabb_;
189 root_ = other.root_;
190 other.root_ = nullptr;
191 end_ = other.end_;
192 nodesAllocator_ = std::move(other.nodesAllocator_);
193 numObjects_ = other.numObjects_;
194 return *this;
195}
196
197
198
199template <typename O, typename OT, typename SH>
200void QuadTree<O, OT, SH>::reset()
201{
202 QuadTree temp(aabb_, end_, static_cast<const SH&>(*this));
203 swap(temp);
204}
205
206
207
208template <typename O, typename OT, typename SH>
209void QuadTree<O, OT, SH>::reset(TObjectIterator first, TObjectIterator last)
210{
211 QuadTree temp(first, last, static_cast<const SH&>(*this));
212 swap(temp);
213}
214
215
216
217template <typename O, typename OT, typename SH>
218void QuadTree<O, OT, SH>::add(TObjectIterator object)
219{
220 LASS_ASSERT(root_);
221 if (!TObjectTraits::aabbContains(aabb_, TObjectTraits::objectAabb(object)))
222 {
223 LASS_THROW("object not within bounding box of tree");
224 }
225 root_->add(object, *this);
226 ++numObjects_;
227}
228
229
230
231template <typename O, typename OT, typename SH>
232void QuadTree<O, OT, SH>::remove(TObjectIterator object)
233{
234 LASS_ASSERT(root_);
235 if (root_->remove(object))
236 {
237 --numObjects_;
238 }
239}
240
241
242
243template <typename O, typename OT, typename SH>
244bool QuadTree<O, OT, SH>::contains(const TPoint& point, const TInfo* info) const
245{
246 LASS_ASSERT(root_);
247
248 if (!TObjectTraits::aabbContains(aabb_, point))
249 {
250 return false;
251 }
252
253 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
254 QuadNode* node = root_;
255 while (!node->isLeaf())
256 {
257 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_NODE;
258 node = &node->children[this->findSubNode(node->center(), point)];
259 LASS_ASSERT(node); // if it's not a leaf, there should be a child node
260 }
261
262 for (typename TObjectIterators::const_iterator i = node->data.begin(); i != node->data.end(); ++i)
263 {
264 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
265 if (TObjectTraits::objectContains(*i, point, info))
266 {
267 return true;
268 }
269 }
270 return false;
271}
272
273
274
275template <typename O, typename OT, typename SH>
276template <typename OutputIterator>
277OutputIterator QuadTree<O, OT, SH>::find(const TPoint& point, OutputIterator result, const TInfo* info) const
278{
279 LASS_ASSERT(root_);
280
281 if (!TObjectTraits::aabbContains(aabb_, point))
282 {
283 return result;
284 }
285
286 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
287 QuadNode* node = root_;
288 while (!node->isLeaf())
289 {
290 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_NODE;
291 node = &node->children[this->findSubNode(node->center(), point)];
292 LASS_ASSERT(node); // if it's not a leaf, there should be a child node
293 }
294
295 for (typename TObjectIterators::const_iterator i = node->data.begin(); i != node->data.end(); ++i)
296 {
297 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
298 if (TObjectTraits::objectContains(*i, point, info))
299 {
300 *result++ = *i;
301 }
302 }
303 return result;
304}
305
306
307
308/** @warning As this algorithm visits multiple leaf nodes, it may find duplicate objects.
309 * This algorithm doesn't care. But if you do, you can use insert_iterators to a set.
310 */
311template <typename O, typename OT, typename SH>
312template <typename OutputIterator>
313OutputIterator QuadTree<O, OT, SH>::find(const TAabb& box, OutputIterator result, const TInfo* info) const
314{
315 LASS_ASSERT(root_);
316 if (!TObjectTraits::aabbIntersects(aabb_, box))
317 {
318 return result;
319 }
320 return doFind(*root_, box, result, info);
321}
322
323
324
325/** @warning As this algorithm visits multiple leaf nodes, it may find duplicate objects.
326 * This algorithm doesn't care. But if you do, you can use insert_iterators to a set.
327 */
328template <typename O, typename OT, typename SH>
329template <typename OutputIterator>
330OutputIterator QuadTree<O, OT, SH>::find(const TRay& ray, TParam tMin, TParam tMax, OutputIterator result, const TInfo* info) const
331{
332 LASS_ASSERT(root_);
333
334 const TPoint min = TObjectTraits::aabbMin(aabb_);
335 const TPoint max = TObjectTraits::aabbMax(aabb_);
336 const TPoint support = TObjectTraits::raySupport(ray);
337 const TVector direction = TObjectTraits::rayDirection(ray);
338 const TVector invDirection = TObjectTraits::vectorReciprocal(direction);
339
340 TVector tNear = invDirection * (min - support);
341 TVector tFar = invDirection * (max - support);
342 const size_t flipMask = this->forceMinToMax(tNear, tFar);
343
344 return doFind(*root_, ray, tMin, tMax, result, info, tNear, tFar, support, invDirection, flipMask);
345}
346
347
348template <typename O, typename OT, typename SH>
349const typename QuadTree<O, OT, SH>::TObjectIterator
350QuadTree<O, OT, SH>::intersect(
351 const TRay& ray, TReference t, TParam tMin, const TInfo* info) const
352{
353 LASS_ASSERT(root_);
354
355 const TPoint min = TObjectTraits::aabbMin(aabb_);
356 const TPoint max = TObjectTraits::aabbMax(aabb_);
357 const TPoint support = TObjectTraits::raySupport(ray);
358 const TVector direction = TObjectTraits::rayDirection(ray);
359 const TVector invDirection = TObjectTraits::vectorReciprocal(direction);
360
361 TVector tNear = invDirection * (min - support);
362 TVector tFar = invDirection * (max - support);
363 const size_t flipMask = this->forceMinToMax(tNear, tFar);
364
365 return doIntersect(*root_, ray, t, tMin, info, tNear, tFar, support, invDirection, flipMask);
366}
367
368
369
370template <typename O, typename OT, typename SH>
371bool QuadTree<O, OT, SH>::intersects(
372 const TRay& ray, TParam tMin, TParam tMax, const TInfo* info) const
373{
374 LASS_ASSERT(root_);
375
376 const TPoint min = TObjectTraits::aabbMin(aabb_);
377 const TPoint max = TObjectTraits::aabbMax(aabb_);
378 const TPoint support = TObjectTraits::raySupport(ray);
379 const TVector direction = TObjectTraits::rayDirection(ray);
380 const TVector invDirection = TObjectTraits::vectorReciprocal(direction);
381
382 TVector tNear = invDirection * (min - support);
383 TVector tFar = invDirection * (max - support);
384 const size_t flipMask = this->forceMinToMax(tNear, tFar);
385
386 return doIntersects(*root_, ray, tMin, tMax, info, tNear, tFar, support, invDirection, flipMask);
387}
388
389
390
391template <typename O, typename OT, typename SH>
392const typename QuadTree<O, OT, SH>::Neighbour
393QuadTree<O, OT, SH>::nearestNeighbour(const TPoint& point, const TInfo* info) const
394{
395 LASS_ASSERT(root_);
396 Neighbour nearest(end_, std::numeric_limits<TValue>::infinity());
397 doNearestNeighbour(*root_, point, info, nearest);
398 return nearest;
399}
400
401
402
403template <typename O, typename OT, typename SH>
404template <typename RandomAccessIterator>
405RandomAccessIterator
406QuadTree<O, OT, SH>::rangeSearch(
407 const TPoint& target, TParam maxRadius, size_t maxCount, RandomAccessIterator first,
408 const TInfo* info) const
409{
410 LASS_ASSERT(root_);
411 if (maxRadius == 0)
412 {
413 return first;
414 }
415 TValue squaredRadius = maxRadius * maxRadius;
416 return doRangeSearch(*root_, target, squaredRadius, maxCount, first, first, info);
417}
418
419
420
421template <typename O, typename OT, typename SH>
422size_t QuadTree<O, OT, SH>::objectCount() const
423{
424 LASS_ASSERT(root_);
425 return root_->objectCount();
426}
427
428
429
430template <typename O, typename OT, typename SH>
431const typename QuadTree<O, OT, SH>::TAabb&
432QuadTree<O, OT, SH>::aabb() const
433{
434 return aabb_;
435}
436
437
438
439template <typename O, typename OT, typename SH>
441{
442 LASS_ASSERT(root_);
443 return root_->depth();
444}
445
446
447
448
449template <typename O, typename OT, typename SH>
450const typename QuadTree<O, OT, SH>::TValue
451QuadTree<O, OT, SH>::averageDepth() const
452{
453 LASS_ASSERT(root_);
454 return root_->averageDepth();
455}
456
457
458
459template <typename O, typename OT, typename SH>
460bool QuadTree<O, OT, SH>::isEmpty() const
461{
462 return numObjects_ == 0;
463}
464
465
466
467template <typename O, typename OT, typename SH>
468void QuadTree<O, OT, SH>::swap(QuadTree& other)
469{
470 SH::swap(other);
471 nodesAllocator_.swap(other.nodesAllocator_);
472 std::swap(aabb_, other.aabb_);
473 std::swap(root_, other.root_);
474 std::swap(end_, other.end_);
475 std::swap(numObjects_, other.numObjects_);
476}
477
478
479
480template <typename O, typename OT, typename SH>
481const typename QuadTree<O, OT, SH>::TObjectIterator
482QuadTree<O, OT, SH>::end() const
483{
484 return end_;
485}
486
487
488
489// --- private -------------------------------------------------------------------------------------
490
491
492template <typename O, typename OT, typename SH>
493template <typename OutputIterator>
494OutputIterator QuadTree<O, OT, SH>::doFind(
495 const QuadNode& node, const TAabb& box, OutputIterator output,
496 const TInfo* info) const
497{
498 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
499
500 if (node.isLeaf())
501 {
502 const typename TObjectIterators::const_iterator end = node.data.end();
503 for (typename TObjectIterators::const_iterator i = node.data.begin(); i != end; ++i)
504 {
505 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
506 if (TObjectTraits::objectIntersects(*i, box, info))
507 {
508 *output++ = *i;
509 }
510 }
511 return output;
512 }
513 for (size_t i = 0; i < numChildren; ++i)
514 {
515 if (node.children[i].bounds.intersects(box))
516 {
517 output = doFind(node.children[i], box, output, info);
518 }
519 }
520 return output;
521}
522
523
524
525template <typename O, typename OT, typename SH>
526template <typename OutputIterator>
527OutputIterator QuadTree<O, OT, SH>::doFind(
528 const QuadNode& node,
529 const TRay& ray, TParam tMin, TParam tMax, OutputIterator output, const TInfo* info,
530 const TVector& tNear, const TVector& tFar, const TPoint& support, const TVector& invDir, size_t flipMask) const
531{
532 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
533
534 const TValue tNearMax = this->maxComponent(tNear);
535 const TValue tFarMin = this->minComponent(tFar);
536 if (tNearMax > tFarMin * (1 + num::sign(tFarMin) * 1e-3f))
537 {
538 return output;
539 }
540
541 if (node.isLeaf())
542 {
543 const typename TObjectIterators::const_iterator end = node.data.end();
544 for (typename TObjectIterators::const_iterator i = node.data.begin(); i != end; ++i)
545 {
546 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
547 if (TObjectTraits::objectIntersects(*i, ray, tMin, tMax, info))
548 {
549 *output++ = *i;
550 }
551 }
552 return output;
553 }
554
555 const TVector tMiddle = invDir * (node.center() - support);
556#if !defined(NDEBUG)
557 for (size_t k = 0; k < dimension; ++k)
558 {
559 LASS_ASSERT((tNear[k] < tMiddle[k] && tMiddle[k] < tFar[k]) || (num::isInf(tNear[k]) && num::isInf(tMiddle[k]) && num::isInf(tFar[k])));
560 }
561#endif
562
563 size_t i = this->entryNode(tNear, tMiddle);
564 do
565 {
566 const QuadNode& child = node.children[i ^ flipMask];
567 TVector tChildNear = tNear;
568 TVector tChildFar = tFar;
569 this->childNearAndFar(tChildNear, tChildFar, tMiddle, i);
570 output = doFind(child, ray, tMin, tMax, output, info, tChildNear, tChildFar, support, invDir, flipMask);
571 i = this->nextNode(i, tChildFar);
572 }
573 while (i != size_t(-1));
574
575 return output;
576}
577
578
579
580/**
581 * Reference: J. Revelles, C. Urena, and M. Lastra. An efficient parametric algorithm for octree
582 * traversal. In Eighth International Conference in Central Europe on Computer Graphics,
583 * Visualization and Interactive Digital Media (WSCG 2000), pages 212--219, Plzen, Czech Republic,
584 * 2000.
585 */
586template <typename O, typename OT, typename SH>
587const typename QuadTree<O, OT, SH>::TObjectIterator
588QuadTree<O, OT, SH>::doIntersect(
589 const QuadNode& node,
590 const TRay& ray, TReference t, TParam tMin, const TInfo* info,
591 const TVector& tNear, const TVector& tFar, const TPoint& support, const TVector& invDir, size_t flipMask) const
592{
593 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
594
595 const TValue tNearMax = std::max(this->maxComponent(tNear), tMin);
596 const TValue tFarMin = this->minComponent(tFar);
597 if (tNearMax > tFarMin * (1 + num::sign(tFarMin) * 1e-3f))
598 {
599 return end_;
600 }
601
602 if (node.isLeaf())
603 {
604 const size_t n = node.data.size();
605 TValue tBest = 0;
606 TObjectIterator best = end_;
607 for (size_t i = 0; i < n; ++i)
608 {
609 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
610 TValue tCandidate = 0;
611 if (TObjectTraits::objectIntersect(node.data[i], ray, tCandidate, tMin, info))
612 {
613 LASS_ASSERT(tCandidate > tMin);
614 if (best == end_ || tCandidate < tBest)
615 {
616 LASS_ASSERT(tCandidate > tMin);
617 best = node.data[i];
618 tBest = tCandidate;
619 }
620 }
621 }
622
623 if (best != end_)
624 {
625 t = tBest;
626 }
627 return best;
628 }
629
630 const TVector tMiddle = invDir * (node.center() - support);
631#if !defined(NDEBUG)
632 for (size_t k = 0; k < dimension; ++k)
633 {
634 LASS_ASSERT((tNear[k] < tMiddle[k] && tMiddle[k] < tFar[k]) || (num::isInf(tNear[k]) && num::isInf(tMiddle[k]) && num::isInf(tFar[k])));
635 }
636#endif
637
638 TObjectIterator best = end_;
639 TValue tBest = 0;
640 size_t i = this->entryNode(tNear, tMiddle);
641 do
642 {
643 const QuadNode& child = node.children[i ^ flipMask];
644 TVector tChildNear = tNear;
645 TVector tChildFar = tFar;
646 this->childNearAndFar(tChildNear, tChildFar, tMiddle, i);
647
648 TValue tCandidate;
649 TObjectIterator candidate = doIntersect(
650 child, ray, tCandidate, tMin, info, tChildNear, tChildFar, support, invDir, flipMask);
651 if (candidate != end_)
652 {
653 if (best == end_ || tCandidate < tBest)
654 {
655 best = candidate;
656 tBest = tCandidate;
657 }
658 }
659
660 const TValue tChildFarMin = this->minComponent(tChildFar);
661 if (best != end_ && tBest < tChildFarMin * (1 - num::sign(tFarMin) * 1e-3f))
662 {
663 t = tBest;
664 return best;
665 }
666
667 i = this->nextNode(i, tChildFar);
668 }
669 while (i != size_t(-1));
670
671 if (best != end_)
672 {
673 t = tBest;
674 }
675 return best;
676}
677
678
679
680template <typename O, typename OT, typename SH>
681bool QuadTree<O, OT, SH>::doIntersects(
682 const QuadNode& node,
683 const TRay& ray, TParam tMin, TParam tMax, const TInfo* info,
684 const TVector& tNear, const TVector& tFar, const TPoint& support, const TVector& invDir, size_t flipMask) const
685{
686 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
687
688 const TValue tNearMax = std::max(this->maxComponent(tNear), tMin);
689 const TValue tFarMin = std::min(this->minComponent(tFar), tMax);
690 if (tNearMax > tFarMin * (1 + num::sign(tFarMin) * 1e-3f))
691 {
692 return false;
693 }
694
695 if (node.isLeaf())
696 {
697 const size_t n = node.data.size();
698 for (size_t i = 0; i < n; ++i)
699 {
700 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
701 if (TObjectTraits::objectIntersects(node.data[i], ray, tMin, tMax, info))
702 {
703 return true;
704 }
705 }
706
707 return false;
708 }
709
710 const TVector tMiddle = invDir * (node.center() - support);
711#if !defined(NDEBUG)
712 for (size_t k = 0; k < dimension; ++k)
713 {
714 LASS_ASSERT((tNear[k] < tMiddle[k] && tMiddle[k] < tFar[k]) || (num::isInf(tNear[k]) && num::isInf(tMiddle[k]) && num::isInf(tFar[k])));
715 }
716#endif
717
718 size_t i = this->entryNode(tNear, tMiddle);
719 do
720 {
721 const QuadNode& child = node.children[i ^ flipMask];
722 TVector tChildNear = tNear;
723 TVector tChildFar = tFar;
724 this->childNearAndFar(tChildNear, tChildFar, tMiddle, i);
725 if (doIntersects(child, ray, tMin, tMax, info, tChildNear, tChildFar, support, invDir, flipMask))
726 {
727 return true;
728 }
729 i = this->nextNode(i, tChildFar);
730 }
731 while (i != size_t(-1));
732
733 return false;
734}
735
736
737
738template <typename O, typename OT, typename SH>
739void QuadTree<O, OT, SH>::doNearestNeighbour(
740 const QuadNode& node, const TPoint& point, const TInfo* info, Neighbour& best) const
741{
742 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
743 if (node.isLeaf())
744 {
745 const size_t n = node.data.size();
746 for (size_t i = 0; i < n; ++i)
747 {
748 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
749 const TValue squaredDistance =
750 TObjectTraits::objectSquaredDistance(node.data[i], point, info);
751 if (squaredDistance < best.squaredDistance())
752 {
753 best = Neighbour(node.data[i], squaredDistance);
754 }
755 }
756 }
757 else
758 {
759 // first, determine squared distances to children and find closest one.
760 TValue sqrNodeDists[numChildren];
761 size_t nearestNode = 0;
762 for (size_t i = 0; i < numChildren; ++i)
763 {
764 sqrNodeDists[i] = node.children[i].sqrDistance(point);
765 if (sqrNodeDists[i] < sqrNodeDists[nearestNode])
766 {
767 nearestNode = i;
768 }
769 }
770
771 // visit closest node first, and then the others.
772 if (sqrNodeDists[nearestNode] < best.squaredDistance())
773 {
774 doNearestNeighbour(node.children[nearestNode], point, info, best);
775 }
776 for (size_t i = 0; i < numChildren; ++i)
777 {
778 if (sqrNodeDists[i] < best.squaredDistance() && i != nearestNode)
779 {
780 doNearestNeighbour(node.children[i], point, info, best);
781 }
782 }
783 }
784}
785
786
787
788template <typename O, typename OT, typename SH>
789template <typename RandomIterator>
790RandomIterator QuadTree<O, OT, SH>::doRangeSearch(
791 const QuadNode& node, const TPoint& target, TReference squaredRadius, size_t maxCount,
792 RandomIterator first, RandomIterator last, const TInfo* info) const
793{
794 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
795 LASS_ASSERT(squaredRadius >= 0);
796
797 if (node.isLeaf())
798 {
799 const size_t n = node.data.size();
800 for (size_t i = 0; i < n; ++i)
801 {
802 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
803 const TValue sqrDist = TObjectTraits::objectSquaredDistance(node.data[i], target, info);
804 if (sqrDist < squaredRadius)
805 {
806 Neighbour candidate(node.data[i], sqrDist);
807// TODO: use a push_heap_unique to avoid duplicates instead of naive search [Bramz]
808// https://sourceforge.net/tracker2/?func=detail&aid=2517748&group_id=118315&atid=680768
809 if (std::find(first, last, candidate) == last)
810 {
811 *last++ = candidate;
812 std::push_heap(first, last);
813 LASS_ASSERT(last >= first);
814 if (static_cast<size_t>(last - first) > maxCount)
815 {
816 std::pop_heap(first, last);
817 --last;
818 squaredRadius = first->squaredDistance();
819 }
820 }
821 }
822 }
823 return last;
824 }
825
826 // first, determine squared distances to children and find closest one.
827 TValue sqrNodeDists[numChildren];
828 size_t nearestNode = 0;
829 for (size_t i = 0; i < numChildren; ++i)
830 {
831 sqrNodeDists[i] = node.children[i].sqrDistance(target);
832 if (sqrNodeDists[i] < sqrNodeDists[nearestNode])
833 {
834 nearestNode = i;
835 }
836 }
837
838 if (sqrNodeDists[nearestNode] < squaredRadius)
839 {
840 last = doRangeSearch(node.children[nearestNode], target, squaredRadius, maxCount, first, last, info);
841 }
842 for (size_t i = 0; i < numChildren; ++i)
843 {
844 if (sqrNodeDists[i] < squaredRadius && i != nearestNode)
845 {
846 last = doRangeSearch(node.children[i], target, squaredRadius, maxCount, first, last, info);
847 }
848 }
849 return last;
850}
851
852
853
854// --- QuadNode ------------------------------------------------------------------------------------
855
856template <typename O, typename OT, typename SH>
857QuadTree<O, OT, SH>::QuadNode::QuadNode(const TAabb& bounds, TNodesAllocator& allocator):
858 bounds(bounds),
859 children(0),
860 allocator_(allocator)
861{
862}
863
864
865
866template <typename O, typename OT, typename SH>
867QuadTree<O, OT, SH>::QuadNode::~QuadNode()
868{
869 deleteChildren(numChildren);
870}
871
872
873
874template <typename O, typename OT, typename SH>
875const typename QuadTree<O, OT, SH>::TPoint
876QuadTree<O, OT, SH>::QuadNode::center() const
877{
878 return QuadTree::middle(TObjectTraits::aabbMin(bounds), TObjectTraits::aabbMax(bounds));
879}
880
881
882
883template <typename O, typename OT, typename SH>
884const typename QuadTree<O, OT, SH>::TValue
885QuadTree<O, OT, SH>::QuadNode::sqrDistance(const TPoint& point) const
886{
887 TValue sqrDist = 0;
888 const TPoint& min = TObjectTraits::aabbMin(bounds);
889 const TPoint& max = TObjectTraits::aabbMax(bounds);
890 for (size_t k = 0; k < dimension; ++k)
891 {
892 const TValue x = TObjectTraits::coord(point, k);
893 const TValue d = std::max(x - TObjectTraits::coord(max, k), TObjectTraits::coord(min, k) - x);
894 if (d > 0)
895 {
896 sqrDist += d * d;
897 }
898 }
899 return sqrDist;
900}
901
902
903
904template <typename O, typename OT, typename SH>
905size_t QuadTree<O, OT, SH>::QuadNode::objectCount() const
906{
907 size_t cumulCount = data.size();
908 if (!isLeaf())
909 {
910 for (size_t i=0;i<numChildren;++i)
911 {
912 cumulCount += children[i].objectCount();
913 }
914 }
915 return cumulCount;
916}
917
918
919
920template <typename O, typename OT, typename SH>
921void QuadTree<O, OT, SH>::QuadNode::add(
922 TObjectIterator object, const TSplitHeuristics& heuristics, size_t level, bool mayDecompose)
923{
924 if (isLeaf())
925 {
926 data.push_back(object);
927 if (mayDecompose && data.size() > heuristics.maxObjectsPerLeaf() && level < heuristics.maxDepth())
928 {
929 decompose(heuristics, level);
930 }
931 }
932 else
933 {
934 bool isAdded = false;
935 for (size_t i=0;i<numChildren;++i)
936 {
937 if (TObjectTraits::objectIntersects(object, children[i].bounds, 0))
938 {
939 children[i].add(object, heuristics, level + 1);
940 isAdded = true;
941 }
942 }
943 LASS_ENFORCE(isAdded)("didn't overlap with any of the child nodes");
944 }
945}
946
947
948
949/** @return true if object was indeed removed, false if it was never found i guess.
950 */
951template <typename O, typename OT, typename SH>
952bool QuadTree<O, OT, SH>::QuadNode::remove(
953 TObjectIterator object)
954{
955 bool isRemoved = false;
956 if (isLeaf())
957 {
958 typename TObjectIterators::iterator last = std::remove(data.begin(), data.end(), object);
959 isRemoved = last != data.end();
960 data.erase(last, data.end());
961 }
962 else
963 {
964 for (size_t i=0;i<numChildren;++i)
965 {
966 if (TObjectTraits::objectIntersects(object, children[i].bounds, 0))
967 {
968 isRemoved |= children[i].remove(object);
969 }
970 }
971 }
972 return isRemoved;
973}
974
975
976
977template <typename O, typename OT, typename SH>
978void QuadTree<O, OT, SH>::QuadNode::decompose(const TSplitHeuristics& heuristics, size_t level)
979{
980 if (!isLeaf())
981 {
982 return;
983 }
984
985 makeChildren();
986
987 //const size_t maxCopies = numChildren * data.size() * 2 / 3;
988 for (typename TObjectIterators::iterator vit = data.begin(); vit != data.end(); ++vit)
989 {
990 /* for each object we test wether it is contained in one of the
991 * subnodes of the quadnode. If it is even partially in one then move
992 * the object down the tree, but only one level
993 */
994 bool isAdded = false;
995 for (size_t i = 0; i < numChildren; ++i)
996 {
997 if (TObjectTraits::objectIntersects(*vit, children[i].bounds, 0))
998 {
999 children[i].add(*vit, heuristics, level + 1, false);
1000 isAdded = true;
1001 }
1002 }
1003 LASS_ENFORCE(isAdded)("object is not added to any of the sub nodes");
1004 }
1005
1006 data.clear();
1007}
1008
1009
1010
1011template <typename O, typename OT, typename SH>
1012void QuadTree<O, OT, SH>::QuadNode::absorb()
1013{
1014 if (isLeaf())
1015 {
1016 return;
1017 }
1018
1019 for (size_t i=0;i<numChildren;++i)
1020 {
1021 children[i].absorb();
1022 std::copy(children[i].data.begin(), children[i].data.end(), std::back_inserter(data));
1023 }
1024 deleteChildren(numChildren);
1025
1026 std::sort(data.begin(), data.end());
1027 typename TObjectIterators::iterator last = std::unique(data.begin(), data.end());
1028 data.erase(last, data.end());
1029}
1030
1031
1032
1033template <typename O, typename OT, typename SH>
1034size_t QuadTree<O, OT, SH>::QuadNode::depth() const
1035{
1036 if (isLeaf())
1037 return 1;
1038
1039 size_t depth=children[0].depth();
1040 for (size_t i=1;i<numChildren;++i)
1041 depth = std::max(depth,children[i].depth());
1042 return depth + 1;
1043}
1044
1045
1046
1047template <typename O, typename OT, typename SH>
1048const typename QuadTree<O, OT, SH>::TValue
1049QuadTree<O, OT, SH>::QuadNode::averageDepth() const
1050{
1051 if (isLeaf())
1052 return 1;
1053
1054 TValue depth = 0;
1055 for (size_t i = 1; i < numChildren; ++i)
1056 depth += children[i].averageDepth();
1057 return depth / numChildren + 1;
1058}
1059
1060
1061
1062template <typename O, typename OT, typename SH>
1063void QuadTree<O, OT, SH>::QuadNode::makeChildren()
1064{
1065 if (children)
1066 {
1067 return;
1068 }
1069
1070 const TPoint center = this->center();
1071 children = static_cast<QuadNode*>(allocator_.allocate());
1072 size_t i = 0;
1073 try
1074 {
1075 for (i = 0; i < numChildren; ++i)
1076 {
1077 TPoint min = TObjectTraits::aabbMin(bounds);
1078 TPoint max = TObjectTraits::aabbMax(bounds);
1079 for (size_t k = 0, mask = 1; k < dimension; ++k, mask *= 2)
1080 {
1081 TObjectTraits::coord(i & mask ? min : max, k, TObjectTraits::coord(center, k));
1082
1083 // // slightly grow the box ...
1084 // //*
1085 // const TValue dx = (TObjectTraits::coord(max, k) - TObjectTraits::coord(min, k)) / 1000;
1086 // TObjectTraits::coord(min, k, TObjectTraits::coord(min, k) - dx);
1087 // TObjectTraits::coord(max, k, TObjectTraits::coord(max, k) + dx);
1088 // /**/
1089 }
1090 new (&children[i]) QuadNode(TObjectTraits::aabbMake(min, max), allocator_);
1091 }
1092 }
1093 catch (...)
1094 {
1095 deleteChildren(i);
1096 throw;
1097 }
1098}
1099
1100
1101
1102template <typename O, typename OT, typename SH>
1103void QuadTree<O, OT, SH>::QuadNode::deleteChildren(size_t count)
1104{
1105 if (!children)
1106 {
1107 return;
1108 }
1109 while (count)
1110 {
1111 children[--count].~QuadNode();
1112 }
1113 allocator_.deallocate(children);
1114 children = 0;
1115}
1116
1117
1118}
1119}
1120
1121#endif
1122
1123// EOF
A spatial container for generic objects.
Definition quad_tree.h:87
OutputIterator find(const TPoint &p, OutputIterator result, const TInfo *info=0) const
find objects containing point and write them to the output iterator.
size_t depth() const
depth.
TSelf & operator=(TSelf &&other) noexcept
move constructor
bool contains(const TPoint &p, const TInfo *info=0) const
return true if any of the objects contains point.
T sign(const T &x)
if x < 0 return -1, else if x > 0 return 1, else return 0.
Definition basic_ops.h:153
bool isInf(const C &iV)
return true if iV equals minus or plus Infinity
Definition num_traits.h:132
spatial subdivisions, quadtrees, octrees, meshes in 2D and 3D, triangulators, ...
Definition aabb8_tree.h:80
Library for Assembled Shared Sources.
Definition config.h:53