EBGeometry
Compact, header-only C++ library for fast evaluation of signed distance functions
Loading...
Searching...
No Matches
EBGeometry_RaySpans.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Robert Marskar <robert.marskar@sintef.no>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
20#ifndef EBGEOMETRY_RAYSPANS_HPP
21#define EBGEOMETRY_RAYSPANS_HPP
22
23// Std includes
24#include <cmath>
25#include <cstdint>
26
27// Our includes
28#include "EBGeometry_Macros.hpp"
29#include "EBGeometry_Math.hpp"
30#include "EBGeometry_Ray.hpp"
31
32namespace EBGeometry {
33
34// A span list is left uninitialized past m_count (zeroing it costs local-memory stores on a device), and GCC
35// cannot see that only the first m_count spans are ever read.
36#if defined(__GNUC__) && !defined(__clang__)
37#pragma GCC diagnostic push
38#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
39#endif
40
41namespace RayDetail {
42
50template <class T>
51[[nodiscard]] EBGEOMETRY_HOST_DEVICE
52inline SpanEndT<T>
53openAt(const T a_t) noexcept
54{
55 return SpanEndT<T>{a_t, 0U, SpanEndT<T>::Open};
56}
57
65template <class T>
66[[nodiscard]] EBGEOMETRY_HOST_DEVICE
67inline SpanEndT<T>
68endOf(const HitT<T>& a_hit) noexcept
69{
70 return SpanEndT<T>{a_hit.m_t, 0U, a_hit.m_exact ? SpanEndT<T>::Exact : SpanEndT<T>::Marched};
71}
72
80template <class T>
82inline void
83copySpans(const RaySpansT<T>& a_from, RaySpansT<T>& a_to) noexcept
84{
85 a_to.m_count = a_from.m_count;
86 a_to.m_horizon = a_from.m_horizon;
87
88 for (uint32_t i = 0; i < a_from.m_count; i++) {
89 a_to.m_spans[i] = a_from.m_spans[i];
90 }
91}
92
101template <class T>
102[[nodiscard]] EBGEOMETRY_HOST_DEVICE
103inline typename RaySpansT<T>::Span
104cut(const typename RaySpansT<T>::Span& a_span, const T a_horizon) noexcept
105{
106 typename RaySpansT<T>::Span span = a_span;
107
108 if (span.m_out.m_t > a_horizon) {
109 span.m_out = openAt(a_horizon);
110 }
111
112 return span;
113}
114
124template <class T>
126inline void
127unite(const RaySpansT<T>& a_a, const RaySpansT<T>& a_b, RaySpansT<T>& a_out) noexcept
128{
129 const T horizon = Math::min(a_a.m_horizon, a_b.m_horizon);
130
131 a_out.reset(horizon);
132
133 uint32_t i = 0;
134 uint32_t j = 0;
135
136 bool open = false;
137 typename RaySpansT<T>::Span current{};
138
139 while (true) {
140 const bool fromA = i < a_a.m_count && a_a.m_spans[i].m_in.m_t <= horizon;
141 const bool fromB = j < a_b.m_count && a_b.m_spans[j].m_in.m_t <= horizon;
142
143 if (!fromA && !fromB) {
144 break;
145 }
146
147 // The span that begins first, and of two that begin together the one already inside.
148 bool takeA = fromA;
149
150 if (fromA && fromB) {
151 const SpanEndT<T>& a = a_a.m_spans[i].m_in;
152 const SpanEndT<T>& b = a_b.m_spans[j].m_in;
153
154 takeA = a.m_t < b.m_t || (a.m_t == b.m_t && !a.crossing());
155 }
156
157 const typename RaySpansT<T>::Span span = cut(takeA ? a_a.m_spans[i++] : a_b.m_spans[j++], horizon);
158
159 if (!open) {
160 current = span;
161 open = true;
162 }
163 else if (span.m_in.m_t <= current.m_out.m_t) {
164 if (span.m_out.m_t > current.m_out.m_t || (span.m_out.m_t == current.m_out.m_t && !span.m_out.crossing())) {
165 current.m_out = span.m_out;
166 }
167 }
168 else {
169 a_out.add(current.m_in, current.m_out);
170
171 current = span;
172 }
173 }
174
175 if (open) {
176 a_out.add(current.m_in, current.m_out);
177 }
178}
179
189template <class T>
191inline void
192intersect(const RaySpansT<T>& a_a, const RaySpansT<T>& a_b, RaySpansT<T>& a_out) noexcept
193{
194 const T horizon = Math::min(a_a.m_horizon, a_b.m_horizon);
195
196 a_out.reset(horizon);
197
198 uint32_t i = 0;
199 uint32_t j = 0;
200
201 while (i < a_a.m_count && j < a_b.m_count) {
202 if (a_a.m_spans[i].m_in.m_t > horizon || a_b.m_spans[j].m_in.m_t > horizon) {
203 break;
204 }
205
206 const typename RaySpansT<T>::Span a = cut(a_a.m_spans[i], horizon);
207 const typename RaySpansT<T>::Span b = cut(a_b.m_spans[j], horizon);
208
209 const bool laterA = a.m_in.m_t > b.m_in.m_t || (a.m_in.m_t == b.m_in.m_t && a.m_in.crossing());
210 const bool earlyA = a.m_out.m_t < b.m_out.m_t || (a.m_out.m_t == b.m_out.m_t && a.m_out.crossing());
211
212 const SpanEndT<T>& in = laterA ? a.m_in : b.m_in;
213 const SpanEndT<T>& out = earlyA ? a.m_out : b.m_out;
214
215 if (in.m_t <= out.m_t) {
216 a_out.add(in, out);
217 }
218
219 // The span that ends first can meet nothing more.
220 if (a.m_out.m_t <= b.m_out.m_t) {
221 i++;
222 }
223 else {
224 j++;
225 }
226 }
227}
228
236template <class T>
238inline void
239cutTo(const T a_horizon, RaySpansT<T>& a_spans) noexcept
240{
241 uint32_t count = 0;
242
243 while (count < a_spans.m_count && a_spans.m_spans[count].m_in.m_t <= a_horizon) {
244 if (a_spans.m_spans[count].m_out.m_t > a_horizon) {
245 a_spans.m_spans[count].m_out = openAt(a_horizon);
246 }
247
248 count++;
249 }
250
251 a_spans.m_count = count;
252 a_spans.m_horizon = a_horizon;
253}
254
263template <class T>
265inline void
266keepFirst(RaySpansT<T>& a_spans) noexcept
267{
268 if (a_spans.m_count == 0U) {
269 return;
270 }
271
272 const typename RaySpansT<T>::Span& first = a_spans.m_spans[0];
273
274 if (first.m_in.crossing()) {
275 RayDetail::cutTo(first.m_in.m_t, a_spans);
276 }
277 else if (first.m_out.crossing()) {
278 RayDetail::cutTo(first.m_out.m_t, a_spans);
279 }
280}
281
291template <class T>
293inline void
294uniteInto(RaySpansT<T>& a_spans, const RaySpansT<T>& a_operand) noexcept
295{
296 const T horizon = Math::min(a_spans.m_horizon, a_operand.m_horizon);
297
298 if (horizon < a_spans.m_horizon) {
299 RayDetail::cutTo(horizon, a_spans);
300 }
301
302 // Each of the operand's spans in turn: merged with the list's spans it meets, or inserted between them.
303 for (uint32_t k = 0; k < a_operand.m_count && a_operand.m_spans[k].m_in.m_t <= horizon; k++) {
304 typename RaySpansT<T>::Span span = RayDetail::cut(a_operand.m_spans[k], horizon);
305
306 uint32_t first = 0;
307
308 while (first < a_spans.m_count && a_spans.m_spans[first].m_out.m_t < span.m_in.m_t) {
309 first++;
310 }
311
312 // Where two ends meet, the one that is no crossing wins: the ray is inside the other span there.
313 uint32_t last = first;
314
315 while (last < a_spans.m_count && a_spans.m_spans[last].m_in.m_t <= span.m_out.m_t) {
316 const typename RaySpansT<T>::Span& other = a_spans.m_spans[last];
317
318 if (other.m_in.m_t < span.m_in.m_t || (other.m_in.m_t == span.m_in.m_t && !other.m_in.crossing())) {
319 span.m_in = other.m_in;
320 }
321 if (other.m_out.m_t > span.m_out.m_t || (other.m_out.m_t == span.m_out.m_t && !other.m_out.crossing())) {
322 span.m_out = other.m_out;
323 }
324
325 last++;
326 }
327
328 const uint32_t merged = last - first;
329
330 if (merged == 0U) {
331 // A new span: a full list keeps its first spans, and is known only up to where the dropped one begins.
332 if (a_spans.m_count == RaySpansT<T>::Capacity) {
333 if (first == a_spans.m_count) {
334 a_spans.m_horizon = Math::min(a_spans.m_horizon, span.m_in.m_t);
335
336 break;
337 }
338
339 a_spans.m_horizon = Math::min(a_spans.m_horizon, a_spans.m_spans[a_spans.m_count - 1U].m_in.m_t);
340 a_spans.m_count--;
341 }
342
343 for (uint32_t m = a_spans.m_count; m > first; m--) {
344 a_spans.m_spans[m] = a_spans.m_spans[m - 1U];
345 }
346
347 a_spans.m_count++;
348 }
349 else if (merged > 1U) {
350 for (uint32_t m = last; m < a_spans.m_count; m++) {
351 a_spans.m_spans[m - merged + 1U] = a_spans.m_spans[m];
352 }
353
354 a_spans.m_count -= merged - 1U;
355 }
356
357 a_spans.m_spans[first] = span;
358 }
359}
360
369template <class T>
371inline void
372intersectInto(RaySpansT<T>& a_spans, const RaySpansT<T>& a_operand, RaySpansT<T>& a_scratch) noexcept
373{
374 if (a_operand.m_count == 0U || a_spans.m_count == 0U) {
375 a_spans.reset(Math::min(a_spans.m_horizon, a_operand.m_horizon));
376
377 return;
378 }
379
380 RayDetail::intersect(a_spans, a_operand, a_scratch);
381 RayDetail::copySpans(a_scratch, a_spans);
382}
383
393template <class T>
395inline void
396complement(const RaySpansT<T>& a_a, const T a_t0, RaySpansT<T>& a_out) noexcept
397{
398 const T horizon = a_a.m_horizon;
399
400 a_out.reset(horizon);
401
402 // The gap the ray is in: from where it starts, while there is one.
403 SpanEndT<T> start = openAt(a_t0);
404 bool gap = true;
405
406 for (uint32_t i = 0; i < a_a.m_count && a_a.m_spans[i].m_in.m_t <= horizon; i++) {
407 const typename RaySpansT<T>::Span span = cut(a_a.m_spans[i], horizon);
408
409 // A span that begins at the start of the interval, inside, has no gap before it.
410 if (span.m_in.crossing() && gap) {
411 a_out.add(start, span.m_in);
412 }
413
414 // A span that ends where nothing more is known leaves no gap after it.
415 gap = span.m_out.crossing();
416
417 if (!gap) {
418 break;
419 }
420
421 start = span.m_out;
422 }
423
424 if (gap) {
425 a_out.add(start, openAt(horizon));
426 }
427}
428
435template <class T>
437{
442
447
452
457};
458
470template <class T>
471[[nodiscard]] EBGEOMETRY_HOST_DEVICE
472inline MappedRay<T>
473mapRay(const T* a_map, const RayT<T>& a_ray, const T a_t0, const T a_t1) noexcept
474{
475 const T* m = a_map;
476 const Vec3T<T>& o = a_ray.m_origin;
477 const Vec3T<T>& d = a_ray.m_direction;
478
479 const Vec3T<T> origin(m[0] * o[0] + m[1] * o[1] + m[2] * o[2] + m[9],
480 m[3] * o[0] + m[4] * o[1] + m[5] * o[2] + m[10],
481 m[6] * o[0] + m[7] * o[1] + m[8] * o[2] + m[11]);
482 const Vec3T<T> direction(m[0] * d[0] + m[1] * d[1] + m[2] * d[2],
483 m[3] * d[0] + m[4] * d[1] + m[5] * d[2],
484 m[6] * d[0] + m[7] * d[1] + m[8] * d[2]);
485
486 const T scale = direction.length();
487
488 return MappedRay<T>{RayT<T>{origin, direction / scale}, scale, a_t0 * scale, a_t1 * scale};
489}
490
503template <class T>
505inline void
506mapBack(const MappedRay<T>& a_mapped, const T a_t0, const T a_t1, RaySpansT<T>& a_spans) noexcept
507{
508 const auto distance = [&a_mapped, a_t0, a_t1](const T a_t) noexcept -> T {
509 if (a_t <= a_mapped.m_t0) {
510 return a_t0;
511 }
512 if (a_t >= a_mapped.m_t1) {
513 return a_t1;
514 }
515
516 // Rounded, the quotient can land just outside the original interval.
517 return Math::clamp(a_t / a_mapped.m_scale, a_t0, a_t1);
518 };
519
520 a_spans.m_horizon = distance(a_spans.m_horizon);
521
522 for (uint32_t i = 0; i < a_spans.m_count; i++) {
523 a_spans.m_spans[i].m_in.m_t = distance(a_spans.m_spans[i].m_in.m_t);
524 a_spans.m_spans[i].m_out.m_t = distance(a_spans.m_spans[i].m_out.m_t);
525 }
526}
527
537template <class T>
538[[nodiscard]] EBGEOMETRY_HOST_DEVICE
539inline Vec3T<T>
540mapNormalBack(const T* a_map, const Vec3T<T>& a_normal) noexcept
541{
542 const T* m = a_map;
543 const Vec3T<T>& n = a_normal;
544 const Vec3T<T> g(m[0] * n[0] + m[3] * n[1] + m[6] * n[2],
545 m[1] * n[0] + m[4] * n[1] + m[7] * n[2],
546 m[2] * n[0] + m[5] * n[1] + m[8] * n[2]);
547 const T length = g.length();
548
549 return (length > T(0)) ? g / length : g;
550}
551
559template <class T>
561inline void
562nameLeaf(const uint32_t a_leaf, RaySpansT<T>& a_spans) noexcept
563{
564 for (uint32_t i = 0; i < a_spans.m_count; i++) {
565 a_spans.m_spans[i].m_in.m_leaf = a_leaf;
566 a_spans.m_spans[i].m_out.m_leaf = a_leaf;
567 }
568}
569
578template <class T>
580inline void
581offsetLeaves(const uint32_t a_offset, RaySpansT<T>& a_spans) noexcept
582{
583 for (uint32_t i = 0; i < a_spans.m_count; i++) {
584 a_spans.m_spans[i].m_in.m_leaf += a_offset;
585 a_spans.m_spans[i].m_out.m_leaf += a_offset;
586 }
587}
588
599template <class T, class S>
600[[nodiscard]] EBGEOMETRY_HOST_DEVICE
601inline bool
602insideAt(const S& a_shape, const Vec3T<T>& a_point) noexcept
603{
604 const auto box = a_shape.computeBoundingVolume();
605 const Vec3T<T>& lo = box.getLowCorner();
606 const Vec3T<T>& hi = box.getHighCorner();
607
608 for (size_t dir = 0; dir < 3; dir++) {
609 if (!(a_point[dir] >= lo[dir] && a_point[dir] <= hi[dir])) {
610 return false;
611 }
612 }
613
614 return RayDetail::valueAt(a_shape, a_point) < T(0);
615}
616
631template <class T, class S>
632[[nodiscard]] EBGEOMETRY_HOST_DEVICE
634inline HitT<T>
635shapeHit(const S& a_shape, const RayT<T>& a_ray, const T a_t0, const T a_t1, const RayOptions<T>& a_options) noexcept
636{
637 return EBGeometry::firstHit(a_shape, a_ray, a_t0, a_t1, a_options);
638}
639
655template <class T, class S>
657inline void
658firstSpan(const S& a_shape,
659 const RayT<T>& a_ray,
660 const T a_t0,
661 const T a_t1,
662 const RayOptions<T>& a_options,
663 RaySpansT<T>& a_spans) noexcept
664{
665 a_spans.reset(a_t1);
666
667 const HitT<T> hit = RayDetail::shapeHit(a_shape, a_ray, a_t0, a_t1, a_options);
668
669 switch (hit.m_status) {
670 case HitStatus::Hit: {
671 if (hit.m_entering) {
672 a_spans.add(endOf(hit), openAt(hit.m_t));
673 }
674 else {
675 a_spans.add(openAt(a_t0), endOf(hit));
676 }
677
678 a_spans.m_horizon = hit.m_t;
679
680 break;
681 }
683 a_spans.m_horizon = Math::max(a_t0, Math::min(hit.m_t, a_t1));
684
685 break;
686 }
687 default: {
688 if (RayDetail::insideAt(a_shape, a_ray.at(a_t0))) {
689 a_spans.add(openAt(a_t0), openAt(a_t1));
690 }
691
692 break;
693 }
694 }
695}
696
705template <class T>
707{
712
716 bool m_inside = false;
717
721 bool m_first = true;
722
731 inline void
732 take(const SpanEndT<T>& a_end, const bool a_entering, const T a_t0, RaySpansT<T>& a_spans) noexcept
733 {
734 if (a_entering) {
735 if (!m_inside) {
736 m_start = a_end;
737 m_inside = true;
738 }
739 }
740 else if (m_inside) {
741 a_spans.add(m_start, a_end);
742
743 m_inside = false;
744 }
745 else if (m_first) {
746 a_spans.add(openAt(a_t0), a_end);
747 }
748
749 m_first = false;
750 }
751
758 inline void
759 finish(const T a_t, RaySpansT<T>& a_spans) noexcept
760 {
761 if (m_inside) {
762 a_spans.add(m_start, openAt(a_t));
763
764 m_inside = false;
765 }
766 }
767};
768
783template <class T, class S>
785inline void
786crossingSpans(const S& a_shape,
787 const RayT<T>& a_ray,
788 const T a_t0,
789 const T a_t1,
790 const RayOptions<T>& a_options,
791 RaySpansT<T>& a_spans) noexcept
792{
793 a_spans.reset(a_t1);
794
795 const auto box = a_shape.computeBoundingVolume();
796 const T scale = RayDetail::scaleOf(box.getLowCorner(), box.getHighCorner(), a_options);
797 const T step = a_options.m_tolerance * scale;
798
799 CrossingPairer<T> pairer;
800
801 T t = a_t0;
802
803 // Two crossings per span, and one more to find out the list is full.
804 for (uint32_t k = 0; k < 2U * RaySpansT<T>::Capacity + 1U; k++) {
805 const HitT<T> hit = RayDetail::shapeHit(a_shape, a_ray, t, a_t1, a_options);
806
807 if (hit.m_status == HitStatus::Miss) {
808 break;
809 }
810
811 if (hit.m_status == HitStatus::Unresolved) {
812 const T horizon = Math::max(a_t0, Math::min(hit.m_t, a_t1));
813
814 pairer.finish(horizon, a_spans);
815
816 a_spans.m_horizon = Math::min(a_spans.m_horizon, horizon);
817
818 return;
819 }
820
821 pairer.take(endOf(hit), hit.m_entering, a_t0, a_spans);
822
823 if (a_spans.m_horizon < a_t1) {
824 return;
825 }
826
827 t = hit.m_t + step;
828
829 if (!(t <= a_t1)) {
830 break;
831 }
832
833 if (k + 1U == 2U * RaySpansT<T>::Capacity + 1U) {
834 // Out of searches with the interval not done: known only up to here.
835 pairer.finish(t, a_spans);
836
837 a_spans.m_horizon = Math::min(a_spans.m_horizon, t);
838
839 return;
840 }
841 }
842
843 if (pairer.m_first) {
844 if (RayDetail::insideAt(a_shape, a_ray.at(a_t0))) {
845 a_spans.add(openAt(a_t0), openAt(a_t1));
846 }
847 }
848 else {
849 pairer.finish(a_t1, a_spans);
850 }
851}
852
867template <class T, class S>
869inline void
870shapeSpans(const S& a_shape,
871 const RayT<T>& a_ray,
872 const T a_t0,
873 const T a_t1,
874 const RayOptions<T>& a_options,
875 const bool a_firstOnly,
876 RaySpansT<T>& a_spans) noexcept
877{
878 if constexpr (HasRaySpans<S, T>::value) {
879 RayForm<S>::spans(a_shape, a_ray, a_t0, a_t1, a_options, a_firstOnly, a_spans);
880 }
881 else {
882 if (a_firstOnly) {
883 RayDetail::firstSpan(a_shape, a_ray, a_t0, a_t1, a_options, a_spans);
884 }
885 else {
886 RayDetail::crossingSpans(a_shape, a_ray, a_t0, a_t1, a_options, a_spans);
887 }
888 }
889}
890
910template <class T, class S>
912inline void
913spansWithin(const S& a_shape,
914 const RayT<T>& a_ray,
915 const RaySpansT<T>& a_within,
916 const RayOptions<T>& a_options,
917 const bool a_firstOnly,
918 RaySpansT<T>& a_piece,
919 RaySpansT<T>& a_spans) noexcept
920{
921 a_spans.reset(a_within.m_horizon);
922
923 for (uint32_t i = 0; i < a_within.m_count && a_within.m_spans[i].m_in.m_t <= a_within.m_horizon; i++) {
924 const T t0 = a_within.m_spans[i].m_in.m_t;
925 const T t1 = Math::min(a_within.m_spans[i].m_out.m_t, a_within.m_horizon);
926
927 RayDetail::shapeSpans(a_shape, a_ray, t0, t1, a_options, a_firstOnly, a_piece);
928
929 for (uint32_t k = 0; k < a_piece.m_count && a_piece.m_spans[k].m_in.m_t <= a_piece.m_horizon; k++) {
930 const typename RaySpansT<T>::Span span = RayDetail::cut(a_piece.m_spans[k], a_piece.m_horizon);
931
932 a_spans.add(span.m_in, span.m_out);
933 }
934
935 if (a_piece.m_horizon < t1 || (a_firstOnly && a_piece.m_count > 0U)) {
936 a_spans.m_horizon = Math::min(a_spans.m_horizon, a_piece.m_horizon);
937 }
938
939 // Cut back to this span or before it: the list is known no further, so no later span can add to it.
940 if (a_spans.m_horizon <= t1) {
941 return;
942 }
943 }
944}
945
960template <class T, class S>
961[[nodiscard]] EBGEOMETRY_HOST_DEVICE
962inline HitT<T>
963leafHit(const S& a_shape, const RayT<T>& a_ray, const T a_t, const RayOptions<T>& a_options) noexcept
964{
965 const auto box = a_shape.computeBoundingVolume();
966 const T delta = a_options.m_tolerance * RayDetail::scaleOf(box.getLowCorner(), box.getHighCorner(), a_options);
967
968 HitT<T> nearest;
969
970 T from = a_t - delta;
971
972 // At most three crossings in the window: a ray grazing an edge may cross two faces there.
973 for (int k = 0; k < 3; k++) {
974 const HitT<T> hit = RayDetail::shapeHit(a_shape, a_ray, from, a_t + delta, a_options);
975
976 if (!hit.hit()) {
977 break;
978 }
979
980 if (!nearest.hit() || std::abs(hit.m_t - a_t) < std::abs(nearest.m_t - a_t)) {
981 nearest = hit;
982 }
983
984 if (hit.m_t >= a_t) {
985 break;
986 }
987
988 from = hit.m_t + T(1e-3) * delta;
989 }
990
991 if (!nearest.hit()) {
992 const Vec3T<T> point = a_ray.at(a_t);
993
994 nearest.m_normal = RayDetail::gradientNormal(a_shape, point, delta);
995 nearest.m_primitive = RayDetail::primitiveOf(a_shape, point);
996 }
997
998 return nearest;
999}
1000
1019template <class T, class Trace, class Resolve>
1020[[nodiscard]] EBGEOMETRY_HOST_DEVICE
1021inline HitT<T>
1023 const Trace& a_trace, const Resolve& a_resolve, const RayT<T>& a_ray, const T a_tMin, const T a_tMax) noexcept
1024{
1025 constexpr uint32_t MaxPasses = 64;
1026
1027 HitT<T> hit;
1028
1029 if (!(a_tMin <= a_tMax)) {
1030 return hit;
1031 }
1032
1033 T t0 = a_tMin;
1034 bool all = false;
1035
1036 RaySpansT<T> spans;
1037
1038 for (uint32_t pass = 0; pass < MaxPasses; pass++) {
1039 a_trace(t0, a_tMax, all, spans);
1040
1041 const T horizon = spans.m_horizon;
1042
1043 for (uint32_t i = 0; i < spans.m_count; i++) {
1044 for (int side = 0; side < 2; side++) {
1045 const SpanEndT<T>& end = (side == 0) ? spans.m_spans[i].m_in : spans.m_spans[i].m_out;
1046
1047 if (end.crossing() && end.m_t >= t0 && end.m_t <= horizon) {
1048 const HitT<T> leaf = a_resolve(end.m_t, end.m_leaf);
1049
1051 hit.m_t = end.m_t;
1052 hit.m_point = a_ray.at(end.m_t);
1053 hit.m_primitive = leaf.m_primitive;
1054 hit.m_normal = leaf.m_normal;
1055 hit.m_entering = (side == 0);
1056 hit.m_exact = (end.m_kind == SpanEndT<T>::Exact);
1057
1058 // The leaf's normal faces out of the leaf; the composition's faces the way the ray crosses. For a ray
1059 // along the surface the dot product is rounding noise, and either orientation may come out.
1060 if ((dot(hit.m_normal, a_ray.m_direction) < T(0)) != hit.m_entering) {
1061 hit.m_normal = -hit.m_normal;
1062 }
1063
1064 return hit;
1065 }
1066 }
1067 }
1068
1069 // No crossing up to the horizon: none at all if that is the end, otherwise start again from it.
1070 if (!(horizon < a_tMax)) {
1071 return hit;
1072 }
1073
1074 if (horizon > t0) {
1075 t0 = horizon;
1076 all = false;
1077 }
1078 else if (!all) {
1079 all = true;
1080 }
1081 else {
1082 break;
1083 }
1084 }
1085
1087 hit.m_t = t0;
1088 hit.m_point = a_ray.at(t0);
1089
1090 return hit;
1091}
1092
1093} // namespace RayDetail
1094
1095#if defined(__GNUC__) && !defined(__clang__)
1096#pragma GCC diagnostic pop
1097#endif
1098
1099} // namespace EBGeometry
1100
1101#endif
#define EBGEOMETRY_DEVICE_NOINLINE
Keep a function out of line in the device pass (CUDA/HIP); nothing on the host.
Definition EBGeometry_GPU.hpp:110
#define EBGEOMETRY_HOST_DEVICE
Mark a function as callable from both host and device code (no-op on host compilers).
Definition EBGeometry_GPU.hpp:80
Utility macros for EBGeometry.
Scalar helpers and numeric limits that compile for both host and device.
void intersectInto(RaySpansT< T > &a_spans, const RaySpansT< T > &a_operand, RaySpansT< T > &a_scratch) noexcept
Intersect a list with another, in place.
Definition EBGeometry_RaySpans.hpp:372
void nameLeaf(const uint32_t a_leaf, RaySpansT< T > &a_spans) noexcept
Name the leaf every crossing of a list lies on.
Definition EBGeometry_RaySpans.hpp:562
void uniteInto(RaySpansT< T > &a_spans, const RaySpansT< T > &a_operand) noexcept
Unite a list into another, in place: each of the operand's spans merged with the spans it meets,...
Definition EBGeometry_RaySpans.hpp:294
void shapeSpans(const S &a_shape, const RayT< T > &a_ray, const T a_t0, const T a_t1, const RayOptions< T > &a_options, const bool a_firstOnly, RaySpansT< T > &a_spans) noexcept
A shape's list over an interval: from its ray form's spans() if it has one, and otherwise from firstH...
Definition EBGeometry_RaySpans.hpp:870
void offsetLeaves(const uint32_t a_offset, RaySpansT< T > &a_spans) noexcept
Shift the leaves every crossing of a list names by an offset: an operand's own leaves,...
Definition EBGeometry_RaySpans.hpp:581
SpanEndT< T > openAt(const T a_t) noexcept
An end that is no crossing.
Definition EBGeometry_RaySpans.hpp:53
HitT< T > leafHit(const S &a_shape, const RayT< T > &a_ray, const T a_t, const RayOptions< T > &a_options) noexcept
A shape's crossing at a distance along a ray, with its normal and primitive: what a composition asks ...
Definition EBGeometry_RaySpans.hpp:963
void complement(const RaySpansT< T > &a_a, const T a_t0, RaySpansT< T > &a_out) noexcept
The complement of a list over [a_t0, a_t1]: where the ray is outside. A crossing is the same crossing...
Definition EBGeometry_RaySpans.hpp:396
SpanEndT< T > endOf(const HitT< T > &a_hit) noexcept
The end a hit is.
Definition EBGeometry_RaySpans.hpp:68
RaySpansT< T >::Span cut(const typename RaySpansT< T >::Span &a_span, const T a_horizon) noexcept
A span cut to a horizon: an end beyond it becomes an Open end there.
Definition EBGeometry_RaySpans.hpp:104
HitT< T > shapeHit(const S &a_shape, const RayT< T > &a_ray, const T a_t0, const T a_t1, const RayOptions< T > &a_options) noexcept
A shape's first crossing along a ray (firstHit()).
Definition EBGeometry_RaySpans.hpp:635
bool insideAt(const S &a_shape, const Vec3T< T > &a_point) noexcept
Whether a point is inside a shape: within its box, and its value negative.
Definition EBGeometry_RaySpans.hpp:602
void cutTo(const T a_horizon, RaySpansT< T > &a_spans) noexcept
Cut a list to a horizon: spans that begin beyond it dropped, and ends beyond it Open ends there.
Definition EBGeometry_RaySpans.hpp:239
void unite(const RaySpansT< T > &a_a, const RaySpansT< T > &a_b, RaySpansT< T > &a_out) noexcept
The union of two lists: where the ray is inside either.
Definition EBGeometry_RaySpans.hpp:127
void spansWithin(const S &a_shape, const RayT< T > &a_ray, const RaySpansT< T > &a_within, const RayOptions< T > &a_options, const bool a_firstOnly, RaySpansT< T > &a_piece, RaySpansT< T > &a_spans) noexcept
A marched operand's list within another list's spans: what an intersection asks a marched operand for...
Definition EBGeometry_RaySpans.hpp:913
void crossingSpans(const S &a_shape, const RayT< T > &a_ray, const T a_t0, const T a_t1, const RayOptions< T > &a_options, RaySpansT< T > &a_spans) noexcept
Every span of a shape over an interval, from firstHit() called from one crossing to the next.
Definition EBGeometry_RaySpans.hpp:786
void copySpans(const RaySpansT< T > &a_from, RaySpansT< T > &a_to) noexcept
Copy a list, only as many spans as it holds.
Definition EBGeometry_RaySpans.hpp:83
void mapBack(const MappedRay< T > &a_mapped, const T a_t0, const T a_t1, RaySpansT< T > &a_spans) noexcept
Map an operand's list, traced along a mapped ray, back to the original ray: distances divided by the ...
Definition EBGeometry_RaySpans.hpp:506
Vec3T< T > mapNormalBack(const T *a_map, const Vec3T< T > &a_normal) noexcept
A normal of the operand of an affine map, mapped back to the original frame: by the transposed matrix...
Definition EBGeometry_RaySpans.hpp:540
void intersect(const RaySpansT< T > &a_a, const RaySpansT< T > &a_b, RaySpansT< T > &a_out) noexcept
The intersection of two lists: where the ray is inside both.
Definition EBGeometry_RaySpans.hpp:192
MappedRay< T > mapRay(const T *a_map, const RayT< T > &a_ray, const T a_t0, const T a_t1) noexcept
A ray mapped by an affine map (CSGOps::affinePoint()'s coefficients: the matrix's rows,...
Definition EBGeometry_RaySpans.hpp:473
HitT< T > traceSpans(const Trace &a_trace, const Resolve &a_resolve, const RayT< T > &a_ray, const T a_tMin, const T a_tMax) noexcept
The first crossing of a composition along a ray, from its lists: the first crossing in the list,...
Definition EBGeometry_RaySpans.hpp:1022
void firstSpan(const S &a_shape, const RayT< T > &a_ray, const T a_t0, const T a_t1, const RayOptions< T > &a_options, RaySpansT< T > &a_spans) noexcept
The list up to a shape's first crossing, from firstHit(): a span from it if the ray enters there,...
Definition EBGeometry_RaySpans.hpp:658
void keepFirst(RaySpansT< T > &a_spans) noexcept
Keep a list only up to its first crossing, which is all a union asked for its first crossing needs: t...
Definition EBGeometry_RaySpans.hpp:266
Ray queries on any shape: where a ray, or the segment between two points, first crosses its surface (...
Three-dimensional vector class with arithmetic operators.
Definition EBGeometry_Vec.hpp:258
T length() const noexcept
Compute vector length.
constexpr T clamp(const T &a_value, const T &a_lo, const T &a_hi) noexcept
A value limited to [lo, hi].
Definition EBGeometry_Math.hpp:78
constexpr T max(const T &a_a, const T &a_b) noexcept
The larger of two values.
Definition EBGeometry_Math.hpp:61
constexpr T min(const T &a_a, const T &a_b) noexcept
The smaller of two values.
Definition EBGeometry_Math.hpp:44
Namespace containing all of EBGeometry's functionality.
Definition EBGeometry_Affine.hpp:26
T length(const Vec2T< T > &v) noexcept
Euclidean length of a 2D vector.
T dot(const Vec2T< T > &u, const Vec2T< T > &v) noexcept
Dot product of two 2D vectors.
@ Unresolved
The query ran out of steps or passes before it could decide (a ray grazing the surface,...
@ Miss
The ray does not cross the surface in the interval.
@ Hit
The ray crosses the surface; HitT says where.
HitT< T > firstHit(const S &a_shape, const RayT< T > &a_ray, typename RayT< T >::Scalar a_tMin, typename RayT< T >::Scalar a_tMax, const RayOptions< T > &a_options=RayOptions< T >()) noexcept
The first crossing of a shape's surface along a ray, entering or leaving.
Where a ray first crosses a surface.
Definition EBGeometry_Ray.hpp:96
bool m_entering
Whether the ray enters the object there (it was outside just before), rather than leaving it.
Definition EBGeometry_Ray.hpp:129
bool m_exact
Whether the crossing is exact (found by a ray form), rather than found to within RayOptions::m_tolera...
Definition EBGeometry_Ray.hpp:135
bool hit() const noexcept
Whether the ray crosses the surface.
T m_t
Distance along the ray: of the crossing for a hit, of the last point reached for an unresolved query,...
Definition EBGeometry_Ray.hpp:106
Vec3T< T > m_normal
The outward unit normal at the crossing: from the shape's ray form, or the normalized gradient of its...
Definition EBGeometry_Ray.hpp:124
uint32_t m_primitive
The finest element the shape can name at the crossing: the face of a mesh SDF, the deciding leaf of a...
Definition EBGeometry_Ray.hpp:118
HitStatus m_status
Whether the ray crosses the surface.
Definition EBGeometry_Ray.hpp:100
Vec3T< T > m_point
The point at m_t (for a hit or an unresolved query).
Definition EBGeometry_Ray.hpp:111
Pairs crossings, in order, into spans: a span from each crossing the ray enters at to the next it lea...
Definition EBGeometry_RaySpans.hpp:707
void take(const SpanEndT< T > &a_end, const bool a_entering, const T a_t0, RaySpansT< T > &a_spans) noexcept
Take the next crossing.
Definition EBGeometry_RaySpans.hpp:732
SpanEndT< T > m_start
Where the open span begins.
Definition EBGeometry_RaySpans.hpp:711
bool m_first
Whether no crossing has been seen yet.
Definition EBGeometry_RaySpans.hpp:721
void finish(const T a_t, RaySpansT< T > &a_spans) noexcept
Close the open span, if any, at an Open end.
Definition EBGeometry_RaySpans.hpp:759
bool m_inside
Whether a span is open.
Definition EBGeometry_RaySpans.hpp:716
A ray seen through an affine map, with the interval it is traced over: what the operand of the map is...
Definition EBGeometry_RaySpans.hpp:437
RayT< T > m_ray
The mapped ray, with a unit direction.
Definition EBGeometry_RaySpans.hpp:441
T m_t1
End of the interval, along the mapped ray.
Definition EBGeometry_RaySpans.hpp:456
T m_t0
Start of the interval, along the mapped ray.
Definition EBGeometry_RaySpans.hpp:451
T m_scale
Distance along the mapped ray per distance along the original.
Definition EBGeometry_RaySpans.hpp:446
How to ray trace a shape directly, instead of marching its signed distance.
Definition EBGeometry_Ray.hpp:347
How a query marches, where a shape has no ray form.
Definition EBGeometry_Ray.hpp:155
A span: where the ray enters, and where it leaves.
Definition EBGeometry_Ray.hpp:258
SpanEndT< T > m_out
Where it leaves.
Definition EBGeometry_Ray.hpp:267
SpanEndT< T > m_in
Where the ray enters.
Definition EBGeometry_Ray.hpp:262
Where a ray is inside an object over an interval [t0, t1]: its spans, in order, each from where it en...
Definition EBGeometry_Ray.hpp:248
uint32_t m_count
How many are held.
Definition EBGeometry_Ray.hpp:278
T m_horizon
The list is complete on [t0, m_horizon]: nothing about the object beyond it is known.
Definition EBGeometry_Ray.hpp:283
Span m_spans[Capacity]
The spans, in order and disjoint.
Definition EBGeometry_Ray.hpp:273
A ray: the points origin + t * direction.
Definition EBGeometry_Ray.hpp:54
One end of a span: where a ray enters or leaves an object.
Definition EBGeometry_Ray.hpp:200
Kind m_kind
What the end is.
Definition EBGeometry_Ray.hpp:225
T m_t
Distance along the ray.
Definition EBGeometry_Ray.hpp:214
uint32_t m_leaf
Which of a composition's leaves the crossing lies on, numbered by the composition; a shape that is no...
Definition EBGeometry_Ray.hpp:220
bool crossing() const noexcept
Whether the end is a crossing of the surface.