Library of Assembled Shared Sources
aabb8_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) 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#pragma once
44
45#include "spat_common.h"
46#include "aabb8_tree.h"
47
48#include <cstddef>
49#include <cstring>
50
51namespace lass
52{
53namespace spat
54{
55namespace impl::aabb8
56{
57
58template <typename T, size_t D>
59void Aabb8<T, D>::set(size_t k, const Aabb<T, D>& aabb)
60{
61 for (size_t i = 0; i < D; ++i)
62 {
63 corners[k][i][0] = aabb.corners[i][0];
64 corners[k][i][1] = aabb.corners[i][1];
65 }
66}
67
68template <typename T, size_t D>
69int contains(const Aabb8<T, D>& bounds, const Point<T, D>& point, bool hits[4])
70{
71 int hitmask = 0;
72 for (size_t k = 0; k < 8; ++k)
73 {
74 int hit = 1;
75 for (size_t d = 0; d < D; ++d)
76 {
77 hit &= bounds.corners[k][d][0] <= point.x[d] && point.x[d] <= bounds.corners[k][d][1];
78 }
79 hitmask |= hit << k;
80 }
81 return hitmask;
82}
83
84template <typename T, size_t D>
85void overlaps(const Aabb8<T, D>& bounds, const Aabb<T, D>& box, bool hits[4])
86{
87 int hitmask = 0;
88 for (size_t k = 0; k < 8; ++k)
89 {
90 int hit = 1;
91 for (size_t d = 0; d < D; ++d)
92 {
93 hit &= bounds.corners[k][d][0] <= box.corners[d][1] && box.corners[d][0] <= bounds.corners[k][d][1];
94 }
95 hitmask |= hit << k;
96 }
97 return hitmask;
98}
99
100template <typename T, size_t D>
101int intersect(const Aabb8<T, D>& bounds, const Ray<T, D>& ray, T tMin, T tMax, T ts[8])
102{
103 int hitmask = 0;
104 for (size_t k = 0; k < 8; ++k)
105 {
106 T tmin = tMin;
107 T tmax = tMax;
108 for (size_t d = 0; d < D; ++d)
109 {
110 const bool sign = ray.sign[d];
111 const T bmin = bounds.corners[k][d][sign];
112 const T bmax = bounds.corners[k][d][!sign];
113 const T dmin = (bmin - ray.support[d]) * ray.invDir[d];
114 const T dmax = (bmax - ray.support[d]) * ray.invDir[d];
115 tmin = std::max(dmin, tmin);
116 tmax = std::min(dmax, tmax);
117 }
118 tmax *= 1 + 2 * num::NumTraits<T>::gamma(3);
119 hitmask |= tmin <= tmax ? 1 << k : 0;
120 ts[k] = tmin;
121 }
122 return hitmask;
123}
124
125
126template <typename T, size_t D>
127void squaredDistance(const Aabb8<T, D>& bounds, const Point<T, D>& point, T sds[8])
128{
129 for (size_t k = 0; k < 8; ++k)
130 {
131 T sd = 0;
132 for (size_t i = 0; i < D; ++i)
133 {
134 const T bmin = bounds.corners[k][i][0];
135 const T bmax = bounds.corners[k][i][1];
136 const T dmin = bmin - point.x[i];
137 const T dmax = point.x[i] - bmax;
138 const T d = std::max(dmin, dmax);
139 if (d > 0)
140 {
141 sd += d * d;
142 }
143 }
144 sds[k] = sd;
145 }
146}
147
148#if LASS_HAVE_AVX
149
150template <size_t D>
151void Aabb8<float, D>::set(size_t k, const Aabb<float, D>& aabb)
152{
153 for (size_t i = 0; i < D; ++i)
154 {
155#if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
156 corners[i][0].m256_f32[k] = aabb.corners[i][0];
157 corners[i][1].m256_f32[k] = aabb.corners[i][1];
158#else
159 corners[i][0][k] = aabb.corners[i][0];
160 corners[i][1][k] = aabb.corners[i][1];
161#endif
162 }
163}
164
165template <size_t D>
166void Aabb8<double, D>::set(size_t k, const Aabb<double, D>& aabb)
167{
168 const size_t k0 = k / 4;
169 const size_t dk = k % 4;
170 for (size_t i = 0; i < D; ++i)
171 {
172#if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
173 corners[i][0][k0].m256d_f64[dk] = aabb.corners[i][0];
174 corners[i][1][k0].m256d_f64[dk] = aabb.corners[i][1];
175#else
176 corners[i][0][k0][dk] = aabb.corners[i][0];
177 corners[i][1][k0][dk] = aabb.corners[i][1];
178#endif
179 }
180}
181
182template <size_t D>
183int contains(const Aabb8<float, D>& bounds, const Point<float, D>& point)
184{
185 __m256 h = _mm256_castsi256_ps(_mm256_set1_epi32(-1));
186 for (size_t d = 0; d < D; ++d)
187 {
188 const __m256 p = _mm256_set1_ps(point.x[d]);
189 const __m256 hmin = _mm256_cmp_ps(bounds.corners[d][0], p, _CMP_LE_OQ);
190 const __m256 hmax = _mm256_cmp_ps(p, bounds.corners[d][1], _CMP_LE_OQ);
191 h = _mm256_and_ps(h, hmin);
192 h = _mm256_and_ps(h, hmax);
193 }
194 const int hitmask = _mm256_movemask_ps(h);
195 return hitmask;
196}
198template <size_t D>
199int contains(const Aabb8<double, D>& bounds, const Point<double, D>& point)
200{
201 __m256d h0 = _mm256_castsi256_pd(_mm256_set1_epi32(-1));
202 __m256d h1 = h0;
203 for (size_t d = 0; d < D; ++d)
205 const __m256d p = _mm256_set1_pd(point.x[d]);
206 const __m256d hmin0 = _mm256_cmp_pd(bounds.corners[d][0][0], p, _CMP_LE_OQ);
207 const __m256d hmin1 = _mm256_cmp_pd(bounds.corners[d][0][1], p, _CMP_LE_OQ);
208 const __m256d hmax0 = _mm256_cmp_pd(p, bounds.corners[d][1][0], _CMP_LE_OQ);
209 const __m256d hmax1 = _mm256_cmp_pd(p, bounds.corners[d][1][1], _CMP_LE_OQ);
210 h0 = _mm256_and_pd(h0, hmin0);
211 h1 = _mm256_and_pd(h1, hmin1);
212 h0 = _mm256_and_pd(h0, hmax0);
213 h1 = _mm256_and_pd(h1, hmax1);
214 }
215 const int hitmask = _mm256_movemask_pd(h0) | (_mm256_movemask_pd(h1) << 4);
216 return hitmask;
217}
218
219
220template <size_t D>
221int overlaps(const Aabb8<float, D>& bounds, const Aabb<float, D>& box)
222{
223 __m256 h = _mm256_castsi256_ps(_mm256_set1_epi32(-1));
224 for (size_t d = 0; d < D; ++d)
225 {
226 const __m256 pmin = _mm256_set1_ps(box.corners[d][0]);
227 const __m256 pmax = _mm256_set1_ps(box.corners[d][1]);
228 const __m256 hmin = _mm256_cmp_ps(bounds.corners[d][0], pmax, _CMP_LE_OQ);
229 const __m256 hmax = _mm256_cmp_ps(pmin, bounds.corners[d][1], _CMP_LE_OQ);
230 h = _mm256_and_ps(h, hmin);
231 h = _mm256_and_ps(h, hmax);
232 }
233 const int hitmask = _mm256_movemask_ps(h);
234 return hitmask;
235}
236
237
238template <size_t D>
239int overlaps(const Aabb8<double, D>& bounds, const Aabb<double, D>& box)
240{
241 __m256d h0 = _mm256_castsi256_pd(_mm256_set1_epi32(-1));
242 __m256d h1 = h0;
243 for (size_t d = 0; d < D; ++d)
244 {
245 const __m256d pmin = _mm256_set1_pd(box.corners[d][0]);
246 const __m256d pmax = _mm256_set1_pd(box.corners[d][1]);
247 const __m256d hmin0 = _mm256_cmp_pd(bounds.corners[d][0][0], pmax, _CMP_LE_OQ);
248 const __m256d hmin1 = _mm256_cmp_pd(bounds.corners[d][0][1], pmax, _CMP_LE_OQ);
249 const __m256d hmax0 = _mm256_cmp_pd(pmin, bounds.corners[d][1][0], _CMP_LE_OQ);
250 const __m256d hmax1 = _mm256_cmp_pd(pmin, bounds.corners[d][1][1], _CMP_LE_OQ);
251 h0 = _mm256_and_pd(h0, hmin0);
252 h1 = _mm256_and_pd(h1, hmin1);
253 h0 = _mm256_and_pd(h0, hmax0);
254 h1 = _mm256_and_pd(h1, hmax1);
255 }
256 const int hitmask = _mm256_movemask_pd(h0) | (_mm256_movemask_pd(h1) << 4);
257 return hitmask;
258}
259
260template <size_t D>
261int intersect(const Aabb8<float, D>& bounds, const Ray<float, D>& ray, float tMin, float tMax, float ts[8])
262{
263 __m256 tmin = _mm256_set1_ps(tMin);
264 __m256 tmax = _mm256_set1_ps(tMax);
265 for (size_t d = 0; d < D; ++d)
266 {
267 const __m256 support = _mm256_set1_ps(ray.support[d]);
268 const __m256 invDir = _mm256_set1_ps(ray.invDir[d]);
269 const bool sign = ray.sign[d];
270 const __m256 bmin = bounds.corners[d][sign];
271 const __m256 bmax = bounds.corners[d][!sign];
272 const __m256 dmin = _mm256_mul_ps(_mm256_sub_ps(bmin, support), invDir);
273 const __m256 dmax = _mm256_mul_ps(_mm256_sub_ps(bmax, support), invDir);
274 tmin = _mm256_max_ps(dmin, tmin);
275 tmax = _mm256_min_ps(dmax, tmax);
276 }
277 tmax = _mm256_mul_ps(tmax, _mm256_set1_ps(1 + 2 * num::NumTraits<float>::gamma(3)));
278 _mm256_storeu_ps(ts, tmin);
279 __m256 h = _mm256_cmp_ps(tmin, tmax, _CMP_LE_OQ);
280 const int hitmask = _mm256_movemask_ps(h);
281 return hitmask;
282}
283
284template <size_t D>
285int intersect(const Aabb8<double, D>& bounds, const Ray<double, D>& ray, double tMin, double tMax, double ts[8])
286{
287 __m256d tmin0 = _mm256_set1_pd(tMin);
288 __m256d tmax0 = _mm256_set1_pd(tMax);
289 __m256d tmin1 = tmin0;
290 __m256d tmax1 = tmax0;
291 for (size_t d = 0; d < D; ++d)
292 {
293 const __m256d support = _mm256_set1_pd(ray.support[d]);
294 const __m256d invDir = _mm256_set1_pd(ray.invDir[d]);
295 const bool sign = ray.sign[d];
296 const __m256d bmin0 = bounds.corners[d][sign][0];
297 const __m256d bmin1 = bounds.corners[d][sign][1];
298 const __m256d bmax0 = bounds.corners[d][!sign][0];
299 const __m256d bmax1 = bounds.corners[d][!sign][1];
300 const __m256d dmin0 = _mm256_mul_pd(_mm256_sub_pd(bmin0, support), invDir);
301 const __m256d dmin1 = _mm256_mul_pd(_mm256_sub_pd(bmin1, support), invDir);
302 const __m256d dmax0 = _mm256_mul_pd(_mm256_sub_pd(bmax0, support), invDir);
303 const __m256d dmax1 = _mm256_mul_pd(_mm256_sub_pd(bmax1, support), invDir);
304 tmin0 = _mm256_max_pd(dmin0, tmin0);
305 tmin1 = _mm256_max_pd(dmin1, tmin1);
306 tmax0 = _mm256_min_pd(dmax0, tmax0);
307 tmax1 = _mm256_min_pd(dmax1, tmax1);
308 }
309 _mm256_storeu_pd(&ts[0], tmin0);
310 _mm256_storeu_pd(&ts[4], tmin1);
311 const __m256d f = _mm256_set1_pd(1 + 2 * num::NumTraits<double>::gamma(3));
312 tmax0 = _mm256_mul_pd(tmax0, f);
313 tmax1 = _mm256_mul_pd(tmax1, f);
314 const __m256d h0 = _mm256_cmp_pd(tmin0, tmax0, _CMP_LE_OQ);
315 const __m256d h1 = _mm256_cmp_pd(tmin1, tmax1, _CMP_LE_OQ);
316 const int hitmask = _mm256_movemask_pd(h0) | (_mm256_movemask_pd(h1) << 4);
317 return hitmask;
318}
319
320
321template <size_t D>
322void squaredDistance(const Aabb8<float, D>& bounds, const Point<float, D>& point, float sds[4])
323{
324 const __m256 zero = _mm256_setzero_ps();
325 __m256 sd = _mm256_setzero_ps();
326 for (size_t i = 0; i < D; ++i)
327 {
328 const __m256 p = _mm256_set1_ps(point.x[i]);
329 const __m256 dmin = _mm256_sub_ps(bounds.corners[i][0], p);
330 const __m256 dmax = _mm256_sub_ps(p, bounds.corners[i][1]);
331 const __m256 d = _mm256_max_ps(_mm256_max_ps(dmin, dmax), zero);
332 const __m256 d2 = _mm256_mul_ps(d, d);
333 sd = _mm256_add_ps(sd, d2);
334 }
335 _mm256_storeu_ps(sds, sd);
336}
337
338template <size_t D>
339void squaredDistance(const Aabb8<double, D>& bounds, const Point<double, D>& point, double sds[4])
340{
341 const __m256d zero = _mm256_setzero_pd();
342 __m256d sd0 = _mm256_setzero_pd();
343 __m256d sd1 = sd0;
344 for (size_t i = 0; i < D; ++i)
345 {
346 const __m256d p = _mm256_set1_pd(point.x[i]);
347 const __m256d dmin0 = _mm256_sub_pd(bounds.corners[i][0][0], p);
348 const __m256d dmin1 = _mm256_sub_pd(bounds.corners[i][0][1], p);
349 const __m256d dmax0 = _mm256_sub_pd(p, bounds.corners[i][1][0]);
350 const __m256d dmax1 = _mm256_sub_pd(p, bounds.corners[i][1][1]);
351 const __m256d d0 = _mm256_max_pd(_mm256_max_pd(dmin0, dmax0), zero);
352 const __m256d d1 = _mm256_max_pd(_mm256_max_pd(dmin1, dmax1), zero);
353 sd0 = _mm256_add_pd(sd0, _mm256_mul_pd(d0, d0));
354 sd1 = _mm256_add_pd(sd1, _mm256_mul_pd(d1, d1));
355 }
356 _mm256_storeu_pd(&sds[0], sd0);
357 _mm256_storeu_pd(&sds[4], sd1);
358}
359
360#endif
361
362
363}
364
365
366// --- public --------------------------------------------------------------------------------------
367
368
369
370template <typename O, typename OT, typename SH>
371Aabb8Tree<O, OT, SH>::Aabb8Tree(TSplitHeuristics heuristics) :
372 SH(std::move(heuristics)),
373 aabb_(TObjectTraits::aabbEmpty()),
374 nodes_(),
375 objects_(),
376 end_(new TObjectIterator),
377 root_()
378{
379}
380
381
382
383template <typename O, typename OT, typename SH>
384Aabb8Tree<O, OT, SH>::Aabb8Tree(TObjectIterator first, TObjectIterator last, TSplitHeuristics heuristics):
385 SH(std::move(heuristics)),
386 aabb_(TObjectTraits::aabbEmpty()),
387 nodes_(),
388 objects_(),
389 end_(new TObjectIterator(last)),
390 root_()
391{
392 if (first != last)
393 {
394 std::ptrdiff_t n = last - first;
395 if (n < 0)
396 {
397 LASS_THROW("Aabb8Tree: invalid range");
398 }
399 if (static_cast<size_t>(n) > static_cast<size_t>(Child::maxNode) + 1)
400 {
401 LASS_THROW("Aabb8Tree: too many objects");
402 }
403 TInputs inputs;
404 inputs.reserve(static_cast<size_t>(n));
405 for (TObjectIterator i = first; i != last; ++i)
406 {
407 inputs.emplace_back(TObjectTraits::objectAabb(i), i);
408 }
409 root_ = balance(inputs.begin(), inputs.end(), aabb_);
410 }
411}
412
413
414template <typename O, typename OT, typename SH>
415Aabb8Tree<O, OT, SH>::Aabb8Tree(TSelf&& other) noexcept:
416 SH(std::forward<TSplitHeuristics>(other)),
417 aabb_(std::move(other.aabb_)),
418 nodes_(std::move(other.nodes_)),
419 objects_(std::move(other.objects_)),
420 end_(std::move(other.end_)),
421 root_(std::move(other.root_))
422{
423}
424
425
426template <typename O, typename OT, typename SH>
427Aabb8Tree<O, OT, SH>& Aabb8Tree<O, OT, SH>::operator=(TSelf&& other) noexcept
428{
429 TSplitHeuristics::operator=(std::forward<TSplitHeuristics>(other));
430 aabb_ = std::move(other.aabb_);
431 nodes_ = std::move(other.nodes_);
432 objects_ = std::move(other.objects_);
433 end_ = std::move(other.end_);
434 root_ = std::move(other.root_);
435 return *this;
436}
437
438
439
440/** Reset the tree to an empty one.
441 *
442 * Is equivalent to:
443 * @code
444 * *this = TSelf();
445 * @endcode
446 */
447template <typename O, typename OT, typename SH>
449{
450 TSelf temp;
451 swap(temp);
452}
453
454
455
456/** Reset the tree to a new one with objects in the range [@a first, @a last)
457 *
458 * Is equivalent to:
459 * @code
460 * *this = TSelf(first, last);
461 * @endcode
462 */
463template <typename O, typename OT, typename SH>
464void Aabb8Tree<O, OT, SH>::reset(TObjectIterator first, TObjectIterator last)
465{
466 TSelf temp(first, last, static_cast<const SH&>(*this));
467 swap(temp);
468}
469
470
471
472/** Return the total bounding box of all objecs in the tree
473 */
474template <typename O, typename OT, typename SH> inline
475const typename Aabb8Tree<O, OT, SH>::TAabb
477{
478 return aabb_;
479}
480
481
482
483/** Check whether there's any object in the tree that contains @a point.
484 */
485template <typename O, typename OT, typename SH>
486bool Aabb8Tree<O, OT, SH>::contains(const TPoint& point, const TInfo* info) const
487{
488 if (isEmpty() || !TObjectTraits::aabbContains(aabb_, point))
489 {
490 return false;
491 }
492 LASS_ASSERT(!root_.isEmpty());
493 return doContains(root_, point, info);
494}
495
496
497
498/** Find all objects that contain @a point.
499 */
500template <typename O, typename OT, typename SH>
501template <typename OutputIterator> inline
502OutputIterator Aabb8Tree<O, OT, SH>::find(const TPoint& point, OutputIterator result, const TInfo* info) const
503{
504 if (isEmpty() || !TObjectTraits::aabbContains(aabb_, point))
505 {
506 return result;
507 }
508 LASS_ASSERT(!root_.isEmpty());
509 return doFind(root_, point, result, info);
510}
511
512
513
514/** Find all objects that intersect with @a box.
515 */
516template <typename O, typename OT, typename SH>
517template <typename OutputIterator> inline
518OutputIterator Aabb8Tree<O, OT, SH>::find(const TAabb& box, OutputIterator result, const TInfo* info) const
519{
520 if (isEmpty() || !TObjectTraits::aabbIntersects(aabb_, box))
521 {
522 return result;
523 }
524 LASS_ASSERT(!root_.isEmpty());
525 return doFind(root_, box, result, info);
526}
527
528
529
530/** Find all objects that have an intersection with @a ray in the interval [tMin, tMax].
531 */
532template <typename O, typename OT, typename SH>
533template <typename OutputIterator>
534OutputIterator Aabb8Tree<O, OT, SH>::find(const TRay& ray, TParam tMin, TParam tMax, OutputIterator result, const TInfo* info) const
535{
536 const TVector invDir = TObjectTraits::vectorReciprocal(TObjectTraits::rayDirection(ray));
537 if (isEmpty() || !volumeIntersects(aabb_, ray, invDir, tMin, tMax))
538 {
539 return result;
540 }
541 LASS_ASSERT(!root_.isEmpty());
542 return doFind(root_, ray, invDir, tMin, tMax, result, info);
543}
544
545
546
547/** Find the first object that is intersected by @a ray, so that t >= tMin and t is minimized.
548 */
549template <typename O, typename OT, typename SH>
550typename Aabb8Tree<O, OT, SH>::TObjectIterator
551Aabb8Tree<O, OT, SH>::intersect(const TRay& ray, TReference t, TParam tMin, const TInfo* info) const
552{
553 const TVector dir = TObjectTraits::rayDirection(ray);
554 const TVector invDir = TObjectTraits::vectorReciprocal(dir);
555 TValue tRoot;
556 if (isEmpty() || !volumeIntersect(aabb_, ray, invDir, tRoot, tMin))
557 {
558 return *end_;
559 }
560 LASS_ASSERT(!root_.isEmpty());
561 return doIntersect(root_, ray, invDir, t, tMin, info);
562}
563
564
565
566/** Check whether there's any object in the tree that is intersected by @a ray in the interval [tMin, tMax].
567 */
568template <typename O, typename OT, typename SH>
570 const TRay& ray, TParam tMin, TParam tMax, const TInfo* info) const
571{
572 LASS_ASSERT(tMax > tMin || (num::isInf(tMin) && num::isInf(tMax)));
573 const TVector dir = TObjectTraits::rayDirection(ray);
574 const TVector invDir = TObjectTraits::vectorReciprocal(dir);
575 if (isEmpty() || !volumeIntersects(aabb_, ray, invDir, tMin, tMax))
576 {
577 return false;
578 }
579 LASS_ASSERT(!root_.isEmpty());
580 return doIntersects(root_, ray, invDir, tMin, tMax, info);
581}
582
583
584
585/** Find the object that is closest to @a point.
586 */
587template <typename O, typename OT, typename SH>
588const typename Aabb8Tree<O, OT, SH>::Neighbour
589Aabb8Tree<O, OT, SH>::nearestNeighbour(const TPoint& point, const TInfo* info) const
590{
591 Neighbour nearest(*end_, std::numeric_limits<TValue>::infinity());
592 if (isEmpty())
593 {
594 return nearest;
595 }
596 LASS_ASSERT(!root_.isEmpty());
597 doNearestNeighbour(root_, point, info, nearest);
598 return nearest;
599}
600
601
602
603/** Find all objects that are within @a maxRadius from @a center, up to @a maxCount.
604 *
605 * If more than @a maxCount objects within @a maxRadius are found, then the closest
606 * @a maxCount objects are returned.
607 *
608 * @param center The center of the search sphere.
609 * @param maxRadius The maximum radius of the search sphere.
610 * @param maxCount The maximum number of objects to find.
611 * @param first An output iterator to a container of Neighbour objects that will be filled with the found objects.
612 * @param info Optional information to pass to the object traits.
613 *
614 * @return The output iterator @a last, pointing to the first element beyond the last found object.
615 *
616 * The range [@a first, @a last) will contain the found objects and form a heap, so that @a first points to the
617 * farthest object from @a center: its squared distance to @a center is the largest in the range, and gives you
618 * the effective maximum radius of the search sphere.
619 *
620 * @note The output iterator must be able to handle at least @a maxCount objects.
621 */
622template <class O, class OT, typename SH>
623template<typename RandomIterator>
624RandomIterator
626 const TPoint& center, TParam maxRadius, size_t maxCount, RandomIterator first, const TInfo* info) const
627{
628 if (isEmpty() || maxRadius == 0)
629 {
630 return first;
631 }
632 TValue squaredRadius = maxRadius * maxRadius;
633 LASS_ASSERT(!root_.isEmpty());
634 return doRangeSearch(root_, center, squaredRadius, maxCount, first, first, info);
635}
636
637
638
639/** Returns true if there are no objects in the tree.
640 */
641template <typename O, typename OT, typename SH>
643{
644 return objects_.empty();
645}
646
647
648
649/** Returns an iterator not pointing to any object, used to indicate when no object is found.
650 *
651 * This iterator can be used as the return value of the find functions when no object is found.
652 * You can compare those return values to the end() function to check if an object was found.
653 */
654template <typename O, typename OT, typename SH>
655const typename Aabb8Tree<O, OT, SH>::TObjectIterator
657{
658 return *end_;
659}
660
661
662
663/** Swap the contents of this tree with another.
664 *
665 * Is equivalent to:
666 * @code
667 * TSelf tmp = std::move(other);
668 * other = std::move(*this);
669 * *this = std::move(tmp);
670 * @endcode
671 */
672template <typename O, typename OT, typename SH>
674{
675 SH::swap(other);
676 std::swap(aabb_, other.aabb_);
677 nodes_.swap(other.nodes_);
678 objects_.swap(other.objects_);
679 end_.swap(other.end_);
680 std::swap(root_, other.root_);
681}
682
683
684
685// --- private -------------------------------------------------------------------------------------
686
687template <typename O, typename OT, typename SH>
688typename Aabb8Tree<O, OT, SH>::Child
689Aabb8Tree<O, OT, SH>::balance(TInputIterator first, TInputIterator last, TAabb& bounds)
690{
691 auto split = [this](TInputIterator first, TInputIterator last) -> TSplitInfo
692 {
693 auto s = TSplitHeuristics::template split<OT>(first, last);
694 if (s.isLeaf() && (last - first) > Child::maxCount)
695 {
696 // we have to many objects, but SAH didn't split them.
697 // force a median split
698 s = forceSplit(s.aabb);
699 }
700 return s;
701 };
702
703 auto partition = [](TInputIterator first, TInputIterator last, const TSplitInfo& s) -> TInputIterator
704 {
705 TInputIterator middle = std::partition(first, last, impl::Splitter<TObjectTraits>(s));
706 if (middle == first || middle == last)
707 {
708 const std::ptrdiff_t halfSize = (last - first) / 2;
709 LASS_ASSERT(halfSize > 0);
710 middle = first + halfSize;
711 std::nth_element(first, middle, last, impl::LessAxis<TObjectTraits>(s.axis));
712 }
713 return middle;
714 };
715
716 auto makeLeaf = [this](TInputIterator first, TInputIterator last) -> Child
717 {
718 const auto count = last - first;
719 LASS_ASSERT(count <= Child::maxCount);
720 const TIndex frst = static_cast<TIndex>(objects_.size());
721 LASS_ASSERT(frst >= 0 && frst <= Child::maxObject);
722 for (TInputIterator i = first; i != last; ++i)
723 {
724 objects_.push_back(i->object);
725 }
726 LASS_ASSERT(objects_.size() <= static_cast<size_t>(Child::maxObject) + 1);
727 return Child(frst, static_cast<TIndex>(count));
728 };
729
730 auto splitQuadrant = [this, makeLeaf, partition](TIndex index, size_t quadrant, TInputIterator first, TInputIterator last)
731 {
732 const size_t octant0 = 2 * quadrant;
733 const size_t octant1 = 2 * quadrant + 1;
734 auto s = TSplitHeuristics::template split<OT>(first, last);
735 if (s.isLeaf())
736 {
737 Node& node = nodes_[index];
738 node.children[octant0] = makeLeaf(first, last);
739 node.children[octant1] = Child();
740 node.bounds.set(octant0, makeAabb(s.aabb));
741 node.bounds.set(octant1, makeAabb(TObjectTraits::aabbEmpty()));
742 node.axis[quadrant + 3] = 0;
743 }
744 else
745 {
746 const TInputIterator middle = partition(first, last, s);
747 TAabb bounds0, bounds1;
748 const Child child0 = balance(first, middle, bounds0);
749 const Child child1 = balance(middle, last, bounds1);
750 Node& node = nodes_[index];
751 node.children[octant0] = child0;
752 node.children[octant1] = child1;
753 node.bounds.set(octant0, makeAabb(bounds0));
754 node.bounds.set(octant1, makeAabb(bounds1));
755 node.axis[quadrant + 3] = static_cast<TAxis>(s.axis);
756 }
757 };
758
759 auto splitHalf = [this, makeLeaf, partition, splitQuadrant](TIndex index, size_t half, TInputIterator first, TInputIterator last)
760 {
761 auto s = TSplitHeuristics::template split<OT>(first, last);
762 if (s.isLeaf())
763 {
764 Node& node = nodes_[index];
765 node.children[4 * half] = makeLeaf(first, last);
766 node.children[4 * half + 1] = Child();
767 node.children[4 * half + 2] = Child();
768 node.children[4 * half + 3] = Child();
769 node.bounds.set(4 * half, makeAabb(s.aabb));
770 node.bounds.set(4 * half + 1, makeAabb(TObjectTraits::aabbEmpty()));
771 node.bounds.set(4 * half + 2, makeAabb(TObjectTraits::aabbEmpty()));
772 node.bounds.set(4 * half + 3, makeAabb(TObjectTraits::aabbEmpty()));
773 node.axis[half + 1] = 0;
774 }
775 else
776 {
777 const TInputIterator middle = partition(first, last, s);
778 splitQuadrant(index, 2 * half, first, middle);
779 splitQuadrant(index, 2 * half + 1, middle, last);
780 nodes_[index].axis[half + 1] = static_cast<TAxis>(s.axis);
781 }
782 };
783
784 if (first == last)
785 {
786 bounds = TObjectTraits::aabbEmpty();
787 return Child();
788 }
789
790 auto s = split(first, last);
791 bounds = s.aabb;
792 if (s.isLeaf())
793 {
794 return makeLeaf(first, last);
795 }
796 LASS_ASSERT(!s.isLeaf());
797
798 LASS_ASSERT(nodes_.size() <= static_cast<size_t>(Child::maxNode));
799 const TIndex index = static_cast<TIndex>(nodes_.size());
800 nodes_.emplace_back();
801
802 nodes_[index].axis[0] = static_cast<TAxis>(s.axis);
803
804 const TInputIterator middle = partition(first, last, s);
805 splitHalf(index, 0, first, middle);
806 splitHalf(index, 1, middle, last);
807 return Child(index);
808}
809
810
811
812template <typename O, typename OT, typename SH>
813typename Aabb8Tree<O, OT, SH>::TSplitInfo
814Aabb8Tree<O, OT, SH>::forceSplit(const TAabb& bounds)
815{
816 const TPoint min = TObjectTraits::aabbMin(bounds);
817 const TPoint max = TObjectTraits::aabbMax(bounds);
818 size_t axis = 0;
819 TValue maxSize = TObjectTraits::coord(max, 0) - TObjectTraits::coord(min, 0);
820 for (size_t k = 1; k < TObjectTraits::dimension; ++k)
821 {
822 const TValue size = TObjectTraits::coord(max, k) - TObjectTraits::coord(min, k);
823 if (size > maxSize)
824 {
825 axis = k;
826 maxSize = size;
827 }
828 }
829 const TValue x = (TObjectTraits::coord(min, axis) + TObjectTraits::coord(max, axis)) / 2;
830 return TSplitInfo(bounds, x, axis);
831}
832
833
834
835template <typename O, typename OT, typename SH>
836bool Aabb8Tree<O, OT, SH>::doContains(Child root, const TPoint& point, const TInfo* info) const
837{
838 const auto pnt = makePoint(point);
839
840 Child stack[stackSize_];
841 TIndex stackSize = 0;
842 stack[stackSize++] = root;
843 while (stackSize > 0)
844 {
845 const Child index = stack[--stackSize];
846 LASS_ASSERT(!index.isEmpty());
847
848 if (index.isLeaf())
849 {
850 const TIndex first = index.first();
851 const TIndex last = first + index.count();
852 for (TIndex i = first; i != last; ++i)
853 {
854 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
855 if (TObjectTraits::objectContains(objects_[i], point, info))
856 {
857 return true;
858 }
859 }
860 continue;
861 }
862
863 LASS_ASSERT(index.isInternal());
864 LASS_ASSERT(index.node() < nodes_.size());
865 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
866 const Node& node = nodes_[index.node()];
867
868 const int hits = impl::aabb8::contains(node.bounds, pnt);
869 for (size_t k = 0; k < 8; ++k)
870 {
871 if (!(hits & (1 << k)))
872 {
873 continue;
874 }
875 const Child child = node.children[k];
876 if (child.isEmpty())
877 {
878 continue;
879 }
880 if (stackSize < stackSize_)
881 {
882 stack[stackSize++] = child;
883 }
884 else if (doContains(child, point, info))
885 {
886 return true;
887 }
888 }
889 }
890 return false;
891}
892
893
894
895template <typename O, typename OT, typename SH>
896template <typename OutputIterator>
897OutputIterator Aabb8Tree<O, OT, SH>::doFind(Child root, const TPoint& point, OutputIterator result, const TInfo* info) const
898{
899 const auto pnt = makePoint(point);
900
901 Child stack[stackSize_];
902 TIndex stackSize = 0;
903 stack[stackSize++] = root;
904 while (stackSize > 0)
905 {
906 const Child index = stack[--stackSize];
907 LASS_ASSERT(!index.isEmpty());
908
909 if (index.isLeaf())
910 {
911 const TIndex first = index.first();
912 const TIndex last = first + index.count();
913 for (TIndex i = first; i != last; ++i)
914 {
915 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
916 if (TObjectTraits::objectContains(objects_[i], point, info))
917 {
918 *result++ = objects_[i];
919 }
920 }
921 continue;
922 }
923
924 LASS_ASSERT(index.isInternal());
925 LASS_ASSERT(index.node() < nodes_.size());
926 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
927 const Node& node = nodes_[index.node()];
928
929 const int hits = impl::aabb8::contains(node.bounds, pnt);
930 for (size_t k = 0; k < 8; ++k)
931 {
932 if (!(hits & (1 << k)))
933 {
934 continue;
935 }
936 const Child child = node.children[k];
937 if (child.isEmpty())
938 {
939 continue;
940 }
941 if (stackSize < stackSize_)
942 {
943 stack[stackSize++] = child;
944 }
945 else
946 {
947 result = doFind(child, point, result, info);
948 }
949 }
950 }
951 return result;
952}
953
954
955template <typename O, typename OT, typename SH>
956template <typename OutputIterator>
957OutputIterator Aabb8Tree<O, OT, SH>::doFind(Child root, const TAabb& box, OutputIterator result, const TInfo* info) const
958{
959 const auto bx = makeAabb(box);
960
961 Child stack[stackSize_];
962 TIndex stackSize = 0;
963 stack[stackSize++] = root;
964 while (stackSize > 0)
965 {
966 const Child index = stack[--stackSize];
967 LASS_ASSERT(!index.isEmpty());
968
969 if (index.isLeaf())
970 {
971 const TIndex first = index.first();
972 const TIndex last = first + index.count();
973 for (TIndex i = first; i != last; ++i)
974 {
975 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
976 if (TObjectTraits::objectIntersects(objects_[i], box, info))
977 {
978 *result++ = objects_[i];
979 }
980 }
981 continue;
982 }
983
984 LASS_ASSERT(index.isInternal());
985 LASS_ASSERT(index.node() < nodes_.size());
986 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
987 const Node& node = nodes_[index.node()];
988
989 const int hits = impl::aabb8::overlaps(node.bounds, bx);
990 for (size_t k = 0; k < 8; ++k)
991 {
992 if (!(hits & (1 << k)))
993 {
994 continue;
995 }
996 const Child child = node.children[k];
997 if (child.isEmpty())
998 {
999 continue;
1000 }
1001 if (stackSize < stackSize_)
1002 {
1003 stack[stackSize++] = child;
1004 }
1005 else
1006 {
1007 result = doFind(child, box, result, info);
1008 }
1009 }
1010 }
1011 return result;
1012}
1013
1014
1015
1016template <typename O, typename OT, typename SH>
1017template <typename OutputIterator>
1018OutputIterator Aabb8Tree<O, OT, SH>::doFind(Child root, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax, OutputIterator result, const TInfo* info) const
1019{
1020 const auto r = makeRay(ray);
1021
1022 Child stack[stackSize_];
1023 TIndex stackSize = 0;
1024 stack[stackSize++] = root;
1025 while (stackSize > 0)
1026 {
1027 const Child index = stack[--stackSize];
1028 LASS_ASSERT(!index.isEmpty());
1029
1030 if (index.isLeaf())
1031 {
1032 const TIndex first = index.first();
1033 const TIndex last = first + index.count();
1034 for (TIndex i = first; i != last; ++i)
1035 {
1036 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1037 if (TObjectTraits::objectIntersects(objects_[i], ray, tMin, tMax, info))
1038 {
1039 *result++ = objects_[i];
1040 }
1041 }
1042 continue;
1043 }
1044
1045 LASS_ASSERT(index.isInternal());
1046 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1047 LASS_ASSERT(index.node() < nodes_.size());
1048 const Node& node = nodes_[index.node()];
1049
1050 TValue ts[8];
1051 const int hits = impl::aabb8::intersect(node.bounds, r, tMin, tMax, ts);
1052 for (size_t k = 0; k < 8; ++k)
1053 {
1054 const Child child = node.children[k];
1055 if (!(hits & (child.isEmpty() ? 0 : 1) << k))
1056 {
1057 continue;
1058 }
1059 if (stackSize < stackSize_)
1060 {
1061 stack[stackSize++] = child;
1062 }
1063 else
1064 {
1065 result = doFind(child, ray, invDir, tMin, tMax, result, info);
1066 }
1067 }
1068 }
1069 return result;
1070}
1071
1072
1073
1074
1075
1076template <typename O, typename OT, typename SH>
1077typename Aabb8Tree<O, OT, SH>::TObjectIterator
1078Aabb8Tree<O, OT, SH>::doIntersect(Child root, const TRay& ray, const TVector& invDir, TReference t, TParam tMin, const TInfo* info) const
1079{
1080 const auto r = makeRay(ray);
1081#if LASS_HAVE_AVX
1082 __m128i signs[dimension];
1083 for (size_t i = 0; i < dimension; ++i)
1084 {
1085 signs[i] = _mm_set1_epi32(r.sign[i] ? -1 : 0);
1086 }
1087#endif
1088
1089 struct Visit
1090 {
1091 TValue tNear;
1092 Child index;
1093 Visit() = default;
1094 Visit(Child index, TValue tNear): tNear(tNear), index(index) {}
1095 };
1096 Visit stack[stackSize_];
1097 size_t stackSize = 0;
1098 stack[stackSize++] = Visit(root, tMin);
1099 TValue tBest = TNumTraits::infinity;
1100 TObjectIterator best = *end_;
1101 while (stackSize > 0)
1102 {
1103 const Visit visit = stack[--stackSize];
1104 if (tBest <= visit.tNear)
1105 {
1106 continue; // we already have a closer hit than what this node can offer
1107 }
1108
1109 const Child index = visit.index;
1110 LASS_ASSERT(!index.isEmpty());
1111
1112 if (index.isLeaf())
1113 {
1114 const TIndex first = index.first();
1115 const TIndex last = first + index.count();
1116 for (TIndex i = first; i != last; ++i)
1117 {
1118 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1119 TValue tCandidate = 0;
1120 if (TObjectTraits::objectIntersect(objects_[i], ray, tCandidate, tMin, info))
1121 {
1122 if (best == *end_ || tCandidate < tBest)
1123 {
1124 tBest = tCandidate;
1125 best = objects_[i];
1126 }
1127 }
1128 }
1129 continue;
1130 }
1131
1132 LASS_ASSERT(index.isInternal());
1133 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1134 LASS_ASSERT(index.node() < nodes_.size());
1135 const Node& node = nodes_[index.node()];
1136
1137 TValue ts[8];
1138 const int hits = impl::aabb8::intersect(node.bounds, r, tMin, TNumTraits::infinity, ts);
1139
1140#if 0 && LASS_HAVE_AVX
1141 alignas(16) int order[4];
1142 {
1143 auto order_ = _mm_set_epi32(3, 2, 1, 0);
1144 const auto m0 = _mm_set_epi32(-1, -1, -1, -1);
1145 const auto m1 = _mm_set_epi32(0, 0, -1, -1);
1146 const auto m2 = _mm_set_epi32(-1, -1, 0, 0);
1147 const auto mask0 = _mm_and_si128(m0, signs[node.axis[0]]);
1148 const auto mask1 = _mm_and_si128(m1, signs[node.axis[1]]);
1149 const auto mask2 = _mm_and_si128(m2, signs[node.axis[2]]);
1150 const auto mask21 = _mm_or_si128(mask2, mask1);
1151 const auto order21 = _mm_shuffle_epi32(order_, 0xB1);
1152 order_ = _mm_blendv_epi8(order_, order21, mask21);
1153 const auto order0 = _mm_shuffle_epi32(order_, 0x4E);
1154 order_ = _mm_blendv_epi8(order_, order0, mask0);
1155 LASS_ASSERT(sizeof(order) == sizeof(order_));
1156 memcpy(order, &order_, sizeof(order_));
1157 }
1158#else
1159 int order[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
1160 if (r.sign[node.axis[3]])
1161 {
1162 std::swap(order[0], order[1]);
1163 }
1164 if (r.sign[node.axis[4]])
1165 {
1166 std::swap(order[2], order[3]);
1167 }
1168 if (r.sign[node.axis[5]])
1169 {
1170 std::swap(order[4], order[5]);
1171 }
1172 if (r.sign[node.axis[6]])
1173 {
1174 std::swap(order[6], order[7]);
1175 }
1176 if (r.sign[node.axis[1]])
1177 {
1178 std::swap(order[0], order[2]);
1179 std::swap(order[1], order[3]);
1180 }
1181 if (r.sign[node.axis[2]])
1182 {
1183 std::swap(order[4], order[6]);
1184 std::swap(order[5], order[7]);
1185 }
1186 if (r.sign[node.axis[0]])
1187 {
1188 std::swap(order[0], order[4]);
1189 std::swap(order[1], order[5]);
1190 std::swap(order[2], order[6]);
1191 std::swap(order[3], order[7]);
1192 }
1193#endif
1194
1195 // push in reverse order, so that the first child is on top of the stack
1196 for (int k = 7; k >= 0; --k)
1197 {
1198 const auto o = order[k];
1199 const Child child = node.children[o];
1200 if (!(hits & (child.isEmpty() ? 0 : 1) << o))
1201 {
1202 continue;
1203 }
1204 if (stackSize < stackSize_)
1205 {
1206 stack[stackSize++] = Visit(child, ts[o]);
1207 }
1208 else
1209 {
1210 TValue tb;
1211 TObjectIterator b = doIntersect(child, ray, invDir, tb, tMin, info);
1212 if (best != *end_ && tb < tBest)
1213 {
1214 best = b;
1215 tBest = tb;
1216 }
1217 }
1218 }
1219 }
1220 if (best != *end_)
1221 {
1222 t = tBest;
1223 }
1224 return best;
1225}
1226
1227
1228
1229template <typename O, typename OT, typename SH>
1230bool Aabb8Tree<O, OT, SH>::doIntersects(Child root, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax, const TInfo* info) const
1231{
1232 const auto r = makeRay(ray);
1233
1234 Child stack[stackSize_];
1235 TIndex stackSize = 0;
1236 stack[stackSize++] = root;
1237 while (stackSize > 0)
1238 {
1239 const Child index = stack[--stackSize];
1240 LASS_ASSERT(!index.isEmpty());
1241
1242 if (index.isLeaf())
1243 {
1244 const TIndex first = index.first();
1245 const TIndex last = first + index.count();
1246 for (TIndex i = first; i != last; ++i)
1247 {
1248 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1249 if (TObjectTraits::objectIntersects(objects_[i], ray, tMin, tMax, info))
1250 {
1251 return true;
1252 }
1253 }
1254 continue;
1255 }
1256
1257 LASS_ASSERT(index.isInternal());
1258 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1259 LASS_ASSERT(index.node() < nodes_.size());
1260 const Node& node = nodes_[index.node()];
1261
1262 TValue ts[8];
1263 const int hits = impl::aabb8::intersect(node.bounds, r, tMin, tMax, ts);
1264 for (size_t k = 0; k < 8; ++k)
1265 {
1266 const Child child = node.children[k];
1267 if (!(hits & (child.isEmpty() ? 0 : 1) << k))
1268 {
1269 continue;
1270 }
1271 if (stackSize < stackSize_)
1272 {
1273 stack[stackSize++] = child;
1274 }
1275 else if (doIntersects(child, ray, invDir, tMin, tMax, info))
1276 {
1277 return true;
1278 }
1279 }
1280 }
1281 return false;
1282}
1283
1284
1285namespace impl::aabb8
1286{
1287
1288template <typename T>
1289void sort8(size_t order[8], const T values[8])
1290{
1291 std::sort(order, order + 8, [&values](size_t a, size_t b) { return values[a] < values[b]; });
1292}
1293
1294}
1295
1296
1297
1298template <typename O, typename OT, typename SH>
1299void Aabb8Tree<O, OT, SH>::doNearestNeighbour(
1300 Child root, const TPoint& target, const TInfo* info, Neighbour& nearest) const
1301{
1302 const auto pnt = makePoint(target);
1303
1304 struct Visit
1305 {
1306 TValue squaredDistance;
1307 Child index;
1308 Visit() = default;
1309 Visit(Child index, TValue squaredDistance): squaredDistance(squaredDistance), index(index) {}
1310 };
1311 Visit stack[stackSize_];
1312 size_t stackSize = 0;
1313 stack[stackSize++] = Visit(root, 0);
1314 while (stackSize > 0)
1315 {
1316 LASS_ASSERT(nearest.squaredDistance() >= 0);
1317 const Visit visit = stack[--stackSize];
1318 if (nearest.squaredDistance() <= visit.squaredDistance)
1319 {
1320 continue; // we already have a closer neighbour than what this node can offer
1321 }
1322
1323 const Child index = visit.index;
1324 LASS_ASSERT(!index.isEmpty());
1325
1326 if (index.isLeaf())
1327 {
1328 const auto first = index.first();
1329 const auto last = first + index.count();
1330 for (auto i = first; i != last; ++i)
1331 {
1332 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1333 const TValue squaredDistance = TObjectTraits::objectSquaredDistance(objects_[i], target, info);
1334 if (squaredDistance < nearest.squaredDistance())
1335 {
1336 nearest = Neighbour(objects_[i], squaredDistance);
1337 }
1338 }
1339 continue;
1340 }
1341
1342 LASS_ASSERT(index.isInternal());
1343 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1344 LASS_ASSERT(index.node() < nodes_.size());
1345 const Node& node = nodes_[index.node()];
1346
1347 TValue squaredDistances[8];
1348 impl::aabb8::squaredDistance(node.bounds, pnt, squaredDistances);
1349
1350 size_t order[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
1351 impl::aabb8::sort8(order, squaredDistances);
1352
1353 // push in reverse order, so that the first child is on top of the stack
1354 for (int k = 7; k >= 0; --k)
1355 {
1356 const size_t o = order[k];
1357 const Child child = node.children[o];
1358 if (child.isEmpty())
1359 {
1360 continue;
1361 }
1362 if (stackSize < stackSize_)
1363 {
1364 stack[stackSize++] = Visit(child, squaredDistances[o]);
1365 }
1366 else
1367 {
1368 doNearestNeighbour(child, target, info, nearest);
1369 }
1370 }
1371 }
1372}
1373
1374
1375
1376template <typename O, typename OT, typename SH>
1377template <typename RandomIterator>
1378RandomIterator
1379Aabb8Tree<O, OT, SH>::doRangeSearch(
1380 Child root, const TPoint& center, TReference squaredRadius, size_t maxCount, RandomIterator first, RandomIterator last, const TInfo* info) const
1381{
1382 const auto pnt = makePoint(center);
1383
1384 struct Visit
1385 {
1386 TValue squaredDistance;
1387 Child index;
1388 Visit() = default;
1389 Visit(Child index, TValue squaredDistance): squaredDistance(squaredDistance), index(index) {}
1390 };
1391 Visit stack[stackSize_];
1392 size_t stackSize = 0;
1393 stack[stackSize++] = Visit(root, 0);
1394 while (stackSize > 0)
1395 {
1396 LASS_ASSERT(squaredRadius >= 0);
1397 const Visit visit = stack[--stackSize];
1398 if (squaredRadius <= visit.squaredDistance)
1399 {
1400 continue; // node is entirely outside range
1401 }
1402
1403 const Child index = visit.index;
1404 LASS_ASSERT(!index.isEmpty());
1405
1406 if (index.isLeaf())
1407 {
1408 const auto frst = index.first();
1409 const auto lst = frst + index.count();
1410 for (auto i = frst; i != lst; ++i)
1411 {
1412 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1413 const TValue squaredDistance = TObjectTraits::objectSquaredDistance(objects_[i], center, info);
1414 if (squaredDistance < squaredRadius)
1415 {
1416 *last++ = Neighbour(objects_[i], squaredDistance);
1417 std::push_heap(first, last);
1418 LASS_ASSERT(last >= first);
1419 if (static_cast<size_t>(last - first) > maxCount)
1420 {
1421 std::pop_heap(first, last);
1422 --last;
1423 squaredRadius = first->squaredDistance();
1424 }
1425 }
1426 }
1427 continue;
1428 }
1429
1430 LASS_ASSERT(index.isInternal());
1431 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1432 LASS_ASSERT(index.node() < nodes_.size());
1433 const Node& node = nodes_[index.node()];
1434
1435 TValue squaredDistances[8];
1436 impl::aabb8::squaredDistance(node.bounds, pnt, squaredDistances);
1437
1438 size_t order[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
1439 impl::aabb8::sort8(order, squaredDistances);
1440
1441 // push in reverse order, so that the first child is on top of the stack
1442 for (int k = 7; k >= 0; --k)
1443 {
1444 size_t o = order[k];
1445 const Child child = node.children[o];
1446 if (child.isEmpty())
1447 {
1448 continue;
1449 }
1450 if (stackSize < stackSize_)
1451 {
1452 stack[stackSize++] = Visit(child, squaredDistances[o]);
1453 }
1454 else
1455 {
1456 last = doRangeSearch(child, center, squaredRadius, maxCount, first, last, info);
1457 }
1458 }
1459 }
1460
1461 return last;
1462}
1463
1464
1465
1466template <typename O, typename OT, typename SH>
1467bool Aabb8Tree<O, OT, SH>::volumeIntersect(const TAabb& box, const TRay& ray, const TVector& invDir, TReference t, TParam tMin) const
1468{
1469 if (TObjectTraits::aabbContains(box, TObjectTraits::rayPoint(ray, tMin)))
1470 {
1471 t = tMin;
1472 return true;
1473 }
1474 return TObjectTraits::aabbIntersect(box, ray, invDir, t, tMin);
1475}
1476
1477
1478
1479template <typename O, typename OT, typename SH>
1480bool Aabb8Tree<O, OT, SH>::volumeIntersects(const TAabb& box, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax) const
1481{
1482 TValue t = 0;
1483 return volumeIntersect(box, ray, invDir, t, tMin) && t <= tMax;
1484}
1485
1486
1487
1488template <typename O, typename OT, typename SH>
1489typename Aabb8Tree<O, OT, SH>::TPoint_
1490Aabb8Tree<O, OT, SH>::makePoint(const TPoint& point)
1491{
1492 TPoint_ result;
1493 for (size_t d = 0; d < dimension; ++d)
1494 {
1495 result.x[d] = OT::coord(point, d);
1496 }
1497 return result;
1498}
1499
1500
1501
1502template <typename O, typename OT, typename SH>
1503typename Aabb8Tree<O, OT, SH>::TRay_
1504Aabb8Tree<O, OT, SH>::makeRay(const TRay& ray)
1505{
1506 TRay_ result;
1507 const auto support = OT::raySupport(ray);
1508 const auto direction = OT::rayDirection(ray);
1509 const auto invDir = TObjectTraits::vectorReciprocal(direction);
1510 for (size_t d = 0; d < dimension; ++d)
1511 {
1512 result.support[d] = OT::coord(support, d);
1513 result.invDir[d] = OT::coord(invDir, d);
1514 result.sign[d] = std::signbit(OT::coord(direction, d));
1515 }
1516 return result;
1517}
1518
1519
1520
1521template <typename O, typename OT, typename SH>
1522typename Aabb8Tree<O, OT, SH>::TAabb_
1523Aabb8Tree<O, OT, SH>::makeAabb(const TAabb& aabb)
1524{
1525 TAabb_ result;
1526 const auto min = OT::aabbMin(aabb);
1527 const auto max = OT::aabbMax(aabb);
1528 for (size_t d = 0; d < dimension; ++d)
1529 {
1530 result.corners[d][0] = OT::coord(min, d);
1531 result.corners[d][1] = OT::coord(max, d);
1532 }
1533 return result;
1534}
1535
1536
1537
1538}
1539
1540}
an 4-ary AABB tree
Definition aabb8_tree.h:138
bool isEmpty() const
Returns true if there are no objects in the tree.
bool intersects(const TRay &ray, TParam tMin=0, TParam tMax=std::numeric_limits< TValue >::infinity(), const TInfo *info=0) const
Check whether there's any object in the tree that is intersected by ray in the interval [tMin,...
const Neighbour nearestNeighbour(const TPoint &point, const TInfo *info=0) const
Find the object that is closest to point.
RandomIterator rangeSearch(const TPoint &center, TParam maxRadius, size_t maxCount, RandomIterator first, const TInfo *info=0) const
Find all objects that are within maxRadius from center, up to maxCount.
bool contains(const TPoint &point, const TInfo *info=0) const
const TObjectIterator end() const
Returns an iterator not pointing to any object, used to indicate when no object is found.
const TAabb aabb() const
Return the total bounding box of all objecs in the tree.
TObjectIterator intersect(const TRay &ray, TReference t, TParam tMin=0, const TInfo *info=0) const
Find the first object that is intersected by ray, so that t >= tMin and t is minimized.
void swap(TSelf &other)
Swap the contents of this tree with another.
void reset()
Reset the tree to an empty one.
OutputIterator find(const TPoint &point, OutputIterator result, const TInfo *info=0) const
Find all objects that contain point.
T sign(const T &x)
if x < 0 return -1, else if x > 0 return 1, else return 0.
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
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