EBGeometry
Compact, header-only C++ library for fast evaluation of signed distance functions
Loading...
Searching...
No Matches
EBGeometry_OffsetRayForms.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
21#ifndef EBGEOMETRY_OFFSETRAYFORMS_HPP
22#define EBGEOMETRY_OFFSETRAYFORMS_HPP
23
24// Std includes
25#include <cmath>
26#include <cstdint>
27#include <type_traits>
28
29// Our includes
35#include "EBGeometry_Fused.hpp"
36#include "EBGeometry_Math.hpp"
39#include "EBGeometry_Ray.hpp"
41
42namespace EBGeometry {
43
44namespace RayDetail {
45
52template <class T>
53struct Piece
54{
59
64
69
74
79 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
80 inline bool
81 hit() const noexcept
82 {
83 return m_in <= m_out;
84 }
85
90 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
91 static inline Piece
92 empty() noexcept
93 {
94 Piece p;
95
98
99 return p;
100 }
101
109 inline void
110 clip(const RayT<T>& a_ray, const Vec3T<T>& a_n, const T a_d) noexcept
111 {
112 const T denom = dot(a_n, a_ray.m_direction);
113 const T num = a_d - dot(a_n, a_ray.m_origin);
114
115 if (denom == T(0)) {
116 if (num < T(0)) {
117 *this = Piece::empty();
118 }
119
120 return;
121 }
122
123 const T t = num / denom;
124
125 if (denom < T(0)) {
126 if (t > m_in) {
127 m_in = t;
128 m_nIn = a_n;
129 }
130 }
131 else if (t < m_out) {
132 m_out = t;
133 m_nOut = a_n;
134 }
135 }
136
142 inline void
143 unite(const Piece& a_other) noexcept
144 {
145 if (!a_other.hit()) {
146 return;
147 }
148
149 if (!this->hit()) {
150 *this = a_other;
151
152 return;
153 }
154
155 if (a_other.m_in < m_in) {
156 m_in = a_other.m_in;
157 m_nIn = a_other.m_nIn;
158 }
159
160 if (a_other.m_out > m_out) {
161 m_out = a_other.m_out;
162 m_nOut = a_other.m_nOut;
163 }
164 }
165};
166
177template <class T>
178[[nodiscard]] EBGEOMETRY_HOST_DEVICE
179inline Piece<T>
180ballPiece(const RayT<T>& a_ray, const Vec3T<T>& a_center, const T a_radius) noexcept
181{
182 const Vec3T<T> oc = a_ray.m_origin - a_center;
183 const T b = dot(oc, a_ray.m_direction);
184 const Vec3T<T> perp = oc - b * a_ray.m_direction;
185 const T h = a_radius * a_radius - dot(perp, perp);
186
187 if (!(a_radius > T(0)) || h < T(0)) {
188 return Piece<T>::empty();
189 }
190
191 const T s = std::sqrt(h);
192
193 Piece<T> p;
194
195 p.m_in = -b - s;
196 p.m_out = -b + s;
197 p.m_nIn = (a_ray.at(p.m_in) - a_center) / a_radius;
198 p.m_nOut = (a_ray.at(p.m_out) - a_center) / a_radius;
199
200 return p;
201}
202
213template <class T>
214[[nodiscard]] EBGEOMETRY_HOST_DEVICE
215inline Piece<T>
216tubePiece(const RayT<T>& a_ray, const Vec3T<T>& a_point, const Vec3T<T>& a_axis, const T a_radius) noexcept
217{
218 if (!(a_radius > T(0))) {
219 return Piece<T>::empty();
220 }
221
222 const Vec3T<T> oa = a_ray.m_origin - a_point;
223 const Vec3T<T> dp = a_ray.m_direction - dot(a_ray.m_direction, a_axis) * a_axis;
224 const Vec3T<T> op = oa - dot(oa, a_axis) * a_axis;
225
226 const T qa = dot(dp, dp);
227 const T qb = dot(dp, op);
228
229 Piece<T> p;
230
231 if (qa > T(0)) {
232 const Vec3T<T> perp = op - (qb / qa) * dp;
233 const T h = qa * (a_radius * a_radius - dot(perp, perp));
234
235 if (h < T(0)) {
236 return Piece<T>::empty();
237 }
238
239 const T s = std::sqrt(h);
240
241 p.m_in = (-qb - s) / qa;
242 p.m_out = (-qb + s) / qa;
243 p.m_nIn = (op + p.m_in * dp) / a_radius;
244 p.m_nOut = (op + p.m_out * dp) / a_radius;
245 }
246 else if (dot(op, op) > a_radius * a_radius) {
247 return Piece<T>::empty();
248 }
249
250 return p;
251}
252
266template <class T>
267[[nodiscard]] EBGEOMETRY_HOST_DEVICE
269inline Piece<T>
270capsulePiece(const RayT<T>& a_ray, const Vec3T<T>& a_a, const Vec3T<T>& a_b, const T a_radius) noexcept
271{
272 Piece<T> p = RayDetail::ballPiece(a_ray, a_a, a_radius);
273
274 p.unite(RayDetail::ballPiece(a_ray, a_b, a_radius));
275
276 const Vec3T<T> axis = a_b - a_a;
277 const T length = axis.length();
278
279 if (length > T(0)) {
280 const Vec3T<T> u = axis / length;
281
282 Piece<T> tube = RayDetail::tubePiece(a_ray, a_a, u, a_radius);
283
284 if (tube.hit()) {
285 tube.clip(a_ray, -u, -dot(u, a_a));
286 tube.clip(a_ray, u, dot(u, a_b));
287
288 p.unite(tube);
289 }
290 }
291
292 return p;
293}
294
304template <class T>
305[[nodiscard]] EBGEOMETRY_HOST_DEVICE
306inline Piece<T>
307boxPiece(const RayT<T>& a_ray, const Vec3T<T>& a_lo, const Vec3T<T>& a_hi) noexcept
308{
309 Piece<T> p;
310
311 for (size_t dir = 0; dir < 3; dir++) {
312 if (!(a_lo[dir] <= a_hi[dir])) {
313 return Piece<T>::empty();
314 }
315
316 p.clip(a_ray, -Vec3T<T>::unit(dir), -a_lo[dir]);
317 p.clip(a_ray, Vec3T<T>::unit(dir), a_hi[dir]);
318 }
319
320 return p;
321}
322
334template <class T>
335[[nodiscard]] EBGEOMETRY_HOST_DEVICE
337inline Piece<T>
338roundedBoxPiece(const RayT<T>& a_ray, const Vec3T<T>& a_lo, const Vec3T<T>& a_hi, const T a_radius) noexcept
339{
340 // It lies in the box grown by the radius along every axis.
341 const Vec3T<T> grow = a_radius * Vec3T<T>::ones();
342
343 if (!RayDetail::boxPiece(a_ray, a_lo - grow, a_hi + grow).hit()) {
344 return Piece<T>::empty();
345 }
346
347 // The three boxes grown along one axis each. Where the ray enters their union through a face across the axis
348 // its box grows along, that face is the rounded box's, and the entry is the rounded box's (it is convex); so
349 // for the exit. Near an edge it is a capsule's.
351
352 size_t dirIn = 0;
353 size_t dirOut = 0;
354
355 for (size_t dir = 0; dir < 3; dir++) {
356 const Vec3T<T> along = a_radius * Vec3T<T>::unit(dir);
357 const Piece<T> box = RayDetail::boxPiece(a_ray, a_lo - along, a_hi + along);
358
359 if (box.hit()) {
360 if (!p.hit() || box.m_in < p.m_in) {
361 dirIn = dir;
362 }
363
364 if (!p.hit() || box.m_out > p.m_out) {
365 dirOut = dir;
366 }
367
368 p.unite(box);
369 }
370 }
371
372 if (p.hit() && p.m_nIn[dirIn] != T(0) && p.m_nOut[dirOut] != T(0)) {
373 return p;
374 }
375
376 for (size_t dir = 0; dir < 3; dir++) {
377 const size_t d1 = (dir + 1) % 3;
378 const size_t d2 = (dir + 2) % 3;
379
380 for (int corner = 0; corner < 4; corner++) {
381 Vec3T<T> a = a_lo;
382
383 a[d1] = ((corner & 1) != 0) ? a_hi[d1] : a_lo[d1];
384 a[d2] = ((corner & 2) != 0) ? a_hi[d2] : a_lo[d2];
385
386 Vec3T<T> b = a;
387
388 b[dir] = a_hi[dir];
389
390 p.unite(RayDetail::capsulePiece(a_ray, a, b, a_radius));
391 }
392 }
393
394 return p;
395}
396
407template <class T, class Edges>
408[[nodiscard]] EBGEOMETRY_HOST_DEVICE
409inline T
410twiceArea(const Vec3T<T>& a_n, const Edges& a_edges) noexcept
411{
412 T area = T(0);
413 bool start = true;
414 Vec3T<T> first = Vec3T<T>::zeros();
415
416 a_edges([&](const Vec3T<T>& a_a, const Vec3T<T>& a_b) noexcept {
417 if (start) {
418 first = a_a;
419 start = false;
420 }
421
422 area += dot(cross(a_a - first, a_b - a_a), a_n);
423 });
424
425 return area;
426}
427
451template <class T, class Edges>
453inline bool
455 const Vec3T<T>& a_n,
456 const Vec3T<T>& a_p0,
457 const Edges& a_edges,
458 const T a_radius,
459 const T a_tMin,
460 const T a_best,
461 T& a_t,
462 Vec3T<T>& a_normal,
463 bool& a_inSlab) noexcept
464{
465 const T dn = dot(a_n, a_ray.m_direction);
466 const T h0 = dot(a_n, a_ray.m_origin - a_p0);
467
470
471 if (dn != T(0)) {
472 const T a = (-a_radius - h0) / dn;
473 const T b = (a_radius - h0) / dn;
474
475 s0 = Math::min(a, b);
476 s1 = Math::max(a, b);
477 }
478 else if (std::abs(h0) > a_radius) {
479 a_inSlab = false;
480
481 return false;
482 }
483
484 a_inSlab = s0 < a_tMin && s1 >= a_tMin;
485
486 if (s1 < a_tMin || !(s0 < a_best)) {
487 return false;
488 }
489
490 const T area = (dn != T(0)) ? RayDetail::twiceArea(a_n, a_edges) : T(0);
491
492 if (area != T(0)) {
493 // Edge values (edge length times the in-plane distance from the edge's line, positive inside) where the
494 // ray's part in the slab begins and ends, projected onto the plane.
495 const T sign = (area > T(0)) ? T(1) : T(-1);
496 const T tIn = Math::max(s0, a_tMin);
497 const Vec3T<T> qIn = a_ray.at(tIn) - dot(a_n, a_ray.at(tIn) - a_p0) * a_n;
498 const Vec3T<T> qOut = a_ray.at(s1) - dot(a_n, a_ray.at(s1) - a_p0) * a_n;
499
500 bool over = true;
501 bool separated = false;
502
503 a_edges([&](const Vec3T<T>& a_a, const Vec3T<T>& a_b) noexcept {
504 const Vec3T<T> e = a_b - a_a;
505
506 const T in = sign * dot(cross(e, qIn - a_a), a_n);
507 const T out = sign * dot(cross(e, qOut - a_a), a_n);
508 const T r = a_radius * e.length();
509
510 over = over && in >= T(0);
511 separated = separated || (in < -r && out < -r);
512 });
513
514 if (s0 >= a_tMin && over) {
515 a_t = s0;
516 a_normal = ((dn < T(0)) ? T(1) : T(-1)) * a_n;
517
518 return true;
519 }
520
521 if (separated) {
522 return false;
523 }
524 }
525
526 // The rim: each edge's capsule, if the ray passes within the radius of the edge's line.
527 T best = a_best;
528 bool found = false;
529
530 a_edges([&](const Vec3T<T>& a_a, const Vec3T<T>& a_b) noexcept {
531 const Vec3T<T> m = cross(a_ray.m_direction, a_b - a_a);
532 const T m2 = dot(m, m);
533 const T along = dot(a_ray.m_origin - a_a, m);
534
535 if (m2 > T(0) && along * along > a_radius * a_radius * m2) {
536 return;
537 }
538
539 const Piece<T> capsule = RayDetail::capsulePiece(a_ray, a_a, a_b, a_radius);
540
541 if (capsule.hit() && capsule.m_in >= a_tMin && capsule.m_in < best) {
542 best = capsule.m_in;
543 a_t = capsule.m_in;
544 a_normal = capsule.m_nIn;
545 found = true;
546 }
547 });
548
549 return found;
550}
551
565template <class T, class Edges>
566[[nodiscard]] EBGEOMETRY_HOST_DEVICE
568inline Piece<T>
570 const RayT<T>& a_ray, const Vec3T<T>& a_n, const Vec3T<T>& a_p0, const Edges& a_edges, const T a_radius) noexcept
571{
573
574 const T area = RayDetail::twiceArea(a_n, a_edges);
575
576 if (area != T(0)) {
577 const T sign = (area > T(0)) ? T(1) : T(-1);
578
579 Piece<T> prism;
580
581 prism.clip(a_ray, a_n, dot(a_n, a_p0) + a_radius);
582 prism.clip(a_ray, -a_n, -dot(a_n, a_p0) + a_radius);
583
584 a_edges([&](const Vec3T<T>& a_a, const Vec3T<T>& a_b) noexcept {
585 // Outward of the face, in its plane.
586 const Vec3T<T> side = sign * cross(a_b - a_a, a_n);
587 const T len = side.length();
588
589 if (len > T(0)) {
590 prism.clip(a_ray, side / len, dot(side / len, a_a));
591 }
592 });
593
594 p = prism;
595 }
596
597 a_edges([&](const Vec3T<T>& a_a, const Vec3T<T>& a_b) noexcept {
598 p.unite(RayDetail::capsulePiece(a_ray, a_a, a_b, a_radius));
599 });
600
601 return p;
602}
603
610template <class T>
612{
616 using Scalar = T;
617
621 using Key = T;
622
627
632
637
642
650 inline GrownRayQuery(const RayT<T>& a_ray, const T a_tMin, const T a_grow) noexcept
651 : m_origin(a_ray.m_origin),
652 m_inverseDirection(T(1) / a_ray.m_direction[0], T(1) / a_ray.m_direction[1], T(1) / a_ray.m_direction[2]),
653 m_tMin(a_tMin),
654 m_grow(a_grow)
655 {}
656
663 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
664 inline T
665 entry(const T (&a_lo)[3], const T (&a_hi)[3]) const noexcept
666 {
667 const T o[3] = {m_origin[0], m_origin[1], m_origin[2]};
668 const T inv[3] = {m_inverseDirection[0], m_inverseDirection[1], m_inverseDirection[2]};
669 const T lo[3] = {a_lo[0] - m_grow, a_lo[1] - m_grow, a_lo[2] - m_grow};
670 const T hi[3] = {a_hi[0] + m_grow, a_hi[1] + m_grow, a_hi[2] + m_grow};
671
672 return BVH::QueryDetail::slabEntry<T>(lo, hi, o, inv, m_tMin);
673 }
674
681 template <size_t K>
682 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
683 inline Array<T, K>
684 keys(const BVH::ChildBoxes<T, K>& a_row) const noexcept
685 {
687
688 for (size_t k = 0; k < K; k++) {
689 keys[k] = this->key(a_row, k);
690 }
691
692 return keys;
693 }
694
702 template <size_t K>
703 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
704 inline T
705 key(const BVH::ChildBoxes<T, K>& a_row, const size_t a_k) const noexcept
706 {
707 const T lo[3] = {a_row.m_lo[0][a_k], a_row.m_lo[1][a_k], a_row.m_lo[2][a_k]};
708 const T hi[3] = {a_row.m_hi[0][a_k], a_row.m_hi[1][a_k], a_row.m_hi[2][a_k]};
709
710 return this->entry(lo, hi);
711 }
712
718 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
719 inline T
720 key(const BoundingVolumes::AABBT<T>& a_box) const noexcept
721 {
722 const Vec3T<T>& l = a_box.getLowCorner();
723 const Vec3T<T>& h = a_box.getHighCorner();
724
725 const T lo[3] = {l[0], l[1], l[2]};
726 const T hi[3] = {h[0], h[1], h[2]};
727
728 return this->entry(lo, hi);
729 }
730};
731
739template <class T>
741{
745 static constexpr uint32_t Capacity = 4;
746
751
756
761
765 uint32_t m_count = 0;
766
771
779 inline void
780 insert(T a_in, T a_out, Vec3T<T> a_nOut) noexcept
781 {
782 if (a_in > m_horizon) {
783 return;
784 }
785
786 // The spans it meets, [first, last), merged into one.
787 uint32_t first = 0;
788
789 while (first < m_count && m_out[first] < a_in) {
790 first++;
791 }
792
793 uint32_t last = first;
794
795 while (last < m_count && m_in[last] <= a_out) {
796 a_in = Math::min(a_in, m_in[last]);
797
798 if (m_out[last] > a_out) {
799 a_out = m_out[last];
800 a_nOut = m_nOut[last];
801 }
802
803 last++;
804 }
805
806 if (last > first) {
807 m_in[first] = a_in;
808 m_out[first] = a_out;
809 m_nOut[first] = a_nOut;
810
811 const uint32_t removed = last - first - 1U;
812
813 for (uint32_t k = first + 1U; k + removed < m_count; k++) {
814 m_in[k] = m_in[k + removed];
815 m_out[k] = m_out[k + removed];
816 m_nOut[k] = m_nOut[k + removed];
817 }
818
819 m_count -= removed;
820
821 return;
822 }
823
824 // A new span at position first.
825 if (m_count == Capacity) {
826 if (first == m_count) {
827 m_horizon = a_in;
828
829 return;
830 }
831
832 m_horizon = m_in[Capacity - 1U];
833
834 m_count--;
835 }
836
837 for (uint32_t k = m_count; k > first; k--) {
838 m_in[k] = m_in[k - 1U];
839 m_out[k] = m_out[k - 1U];
840 m_nOut[k] = m_nOut[k - 1U];
841 }
842
843 m_in[first] = a_in;
844 m_out[first] = a_out;
845 m_nOut[first] = a_nOut;
846
847 m_count++;
848 }
849
858 inline void
859 toSpans(const T a_t0, RaySpansT<T>& a_spans) const noexcept
860 {
861 a_spans.reset(m_horizon);
862
863 for (uint32_t i = 0; i < m_count && m_in[i] <= m_horizon; i++) {
864 const SpanEndT<T> a = (m_in[i] < a_t0) ? RayDetail::openAt(a_t0) : SpanEndT<T>{m_in[i], 0U, SpanEndT<T>::Exact};
865 const SpanEndT<T> b =
866 (m_out[i] >= m_horizon) ? RayDetail::openAt(m_horizon) : SpanEndT<T>{m_out[i], 0U, SpanEndT<T>::Exact};
867
868 a_spans.add(a, b);
869 }
870 }
871};
872
881template <class S>
883{
884};
885
892template <class T, size_t K, size_t W>
893struct MeshFaces<TriMeshSDF<T, K, W>>
894{
908 template <class State, class Face, class Bound>
910 static inline void
912 const RayT<T>& a_ray,
913 const T a_tMin,
914 const T a_grow,
915 State& a_state,
916 const Face& a_face,
917 const Bound& a_bound) noexcept
918 {
919 const auto bvh = a_mesh.getRoot();
920 const auto groups = bvh.getPrimitives();
921
922 const auto leaf = [&groups, &a_face](State& a_s, size_t a_offset, size_t a_count) noexcept {
923 for (size_t i = a_offset; i < a_offset + a_count; i++) {
924 const auto& group = groups[static_cast<uint32_t>(i)];
925
926 for (size_t lane = 0; lane < W; lane++) {
927 const Vec3T<T> v[3] = {group.getVertex(lane, 0), group.getVertex(lane, 1), group.getVertex(lane, 2)};
928
929 const auto edges = [&v](const auto& a_f) noexcept {
930 a_f(v[0], v[1]);
931 a_f(v[1], v[2]);
932 a_f(v[2], v[0]);
933 };
934
935 const auto distance2 = [&group, lane](const Vec3T<T>& a_point) noexcept {
936 const T d = group.signedDistanceAt(static_cast<uint32_t>(lane), a_point);
937
938 return d * d;
939 };
940
941 a_face(a_s, group.getNormal(lane), v[0], edges, distance2, group.getFaceId(lane));
942 }
943 }
944 };
945
946 bvh.traverse(GrownRayQuery<T>(a_ray, a_tMin, a_grow), a_state, leaf, a_bound);
947 }
948};
949
955template <class T, size_t K>
956struct MeshFaces<MeshSDF<T, K>>
957{
971 template <class State, class Face, class Bound>
973 static inline void
974 walk(const MeshSDF<T, K>& a_mesh,
975 const RayT<T>& a_ray,
976 const T a_tMin,
977 const T a_grow,
978 State& a_state,
979 const Face& a_face,
980 const Bound& a_bound) noexcept
981 {
982 const auto bvh = a_mesh.getRoot();
983 const auto mesh = a_mesh.getMesh();
984 const auto faceIds = bvh.getPrimitives();
985
986 const auto leaf = [&faceIds, &mesh, &a_face](State& a_s, size_t a_offset, size_t a_count) noexcept {
987 for (size_t i = a_offset; i < a_offset + a_count; i++) {
988 const uint32_t id = faceIds[static_cast<uint32_t>(i)];
989
990 MeshFaces::visit(mesh, id, a_s, a_face);
991 }
992 };
993
994 bvh.traverse(GrownRayQuery<T>(a_ray, a_tMin, a_grow), a_state, leaf, a_bound);
995 }
996
1007 template <class Mesh, class State, class Face>
1009 static inline void
1010 visit(const Mesh& a_mesh, const uint32_t a_id, State& a_state, const Face& a_face) noexcept
1011 {
1012 const auto& face = a_mesh.getFace(a_id);
1013
1014 const auto edges = [&a_mesh, &face](const auto& a_f) noexcept {
1015 for (DCEL::EdgeIteratorT<T> iter(a_mesh, face); iter.ok(); ++iter) {
1016 const auto& edge = a_mesh.getEdge(iter());
1017
1018 a_f(edge.getVertex(a_mesh).getPosition(), edge.getNextEdge(a_mesh).getVertex(a_mesh).getPosition());
1019 }
1020 };
1021
1022 const auto distance2 = [&a_mesh, &face](const Vec3T<T>& a_point) noexcept {
1023 return face.unsignedDistance2(a_point, a_mesh);
1024 };
1025
1026 a_face(a_state, face.getNormal(), face.getCentroid(), edges, distance2, a_id);
1027 }
1028};
1029
1034template <class T>
1036{
1046 template <class State, class Face, class Bound>
1048 static inline void
1049 walk(const FlatMeshSDF<T>& a_mesh,
1050 const RayT<T>& /*a_ray*/,
1051 const T /*a_tMin*/,
1052 const T /*a_grow*/,
1053 State& a_state,
1054 const Face& a_face,
1055 const Bound& /*a_bound*/) noexcept
1056 {
1057 const auto mesh = a_mesh.getMesh();
1058
1059 for (uint32_t i = 0; i < mesh.numFaces(); i++) {
1060 MeshFaces<MeshSDF<T, 4>>::visit(mesh, i, a_state, a_face);
1061 }
1062 }
1063};
1064
1070template <class T>
1072{
1077
1081 uint32_t m_face;
1082
1087
1092};
1093
1106template <class T, class S>
1107[[nodiscard]] EBGEOMETRY_HOST_DEVICE
1110nearestOffsetFace(const S& a_mesh, const RayT<T>& a_ray, const T a_tMin, const T a_tMax, const T a_radius) noexcept
1111{
1112 NearestOffsetFace<T> found{a_tMax, UINT32_MAX, Vec3T<T>::zeros(), false};
1113
1114 const auto face = [&a_ray, a_tMin, a_radius](NearestOffsetFace<T>& a_state,
1115 const Vec3T<T>& a_n,
1116 const Vec3T<T>& a_p0,
1117 const auto& a_edges,
1118 const auto& a_distance2,
1119 const uint32_t a_id) noexcept {
1120 if (a_state.m_inside) {
1121 return;
1122 }
1123
1124 T t = T(0);
1126 bool inSlab = false;
1127
1128 const bool entered =
1129 RayDetail::offsetFaceEntry(a_ray, a_n, a_p0, a_edges, a_radius, a_tMin, a_state.m_t, t, normal, inSlab);
1130
1131 // A start within the radius of the face is inside the grown surface.
1132 if (inSlab && a_distance2(a_ray.at(a_tMin)) <= a_radius * a_radius) {
1133 a_state.m_inside = true;
1134 a_state.m_t = a_tMin;
1135
1136 return;
1137 }
1138
1139 if (entered) {
1140 a_state.m_t = t;
1141 a_state.m_face = a_id;
1142 a_state.m_normal = normal;
1143 }
1144 };
1145
1146 const auto bound = [](const NearestOffsetFace<T>& a_state) noexcept -> T { return a_state.m_t; };
1147
1148 MeshFaces<S>::walk(a_mesh, a_ray, a_tMin, a_radius, found, face, bound);
1149
1150 return found;
1151}
1152
1165template <class T, class S>
1168inline void
1169offsetFaceUnion(const S& a_mesh,
1170 const RayT<T>& a_ray,
1171 const T a_tMin,
1172 const T a_tMax,
1173 const T a_radius,
1174 OffsetFaceUnion<T>& a_union) noexcept
1175{
1176 a_union.m_count = 0;
1177 a_union.m_horizon = a_tMax;
1178
1179 const auto face = [&a_ray, a_tMin, a_radius](OffsetFaceUnion<T>& a_state,
1180 const Vec3T<T>& a_n,
1181 const Vec3T<T>& a_p0,
1182 const auto& a_edges,
1183 const auto& /*a_distance2*/,
1184 const uint32_t /*a_id*/) noexcept {
1185 const Piece<T> p = RayDetail::offsetFacePiece(a_ray, a_n, a_p0, a_edges, a_radius);
1186
1187 if (p.hit() && p.m_out >= a_tMin && p.m_in <= a_state.m_horizon) {
1188 a_state.insert(p.m_in, p.m_out, p.m_nOut);
1189 }
1190 };
1191
1192 const auto bound = [](const OffsetFaceUnion<T>& a_state) noexcept -> T { return a_state.m_horizon; };
1193
1194 MeshFaces<S>::walk(a_mesh, a_ray, a_tMin, a_radius, a_union, face, bound);
1195}
1196
1211template <class T, class S>
1214inline void
1215meshSpans(const S& a_mesh,
1216 const RayT<T>& a_ray,
1217 const T a_tMin,
1218 const T a_tMax,
1219 const RayOptions<T>& a_options,
1220 const bool a_firstOnly,
1221 RaySpansT<T>& a_spans) noexcept
1222{
1223 RayDetail::shapeSpans(a_mesh, a_ray, a_tMin, a_tMax, a_options, a_firstOnly, a_spans);
1224}
1225
1232template <class S>
1233struct IsMeshLevel : std::false_type
1234{
1235};
1236
1243template <class T, size_t K, size_t W>
1244struct IsMeshLevel<TriMeshSDF<T, K, W>> : std::true_type
1245{
1246};
1247
1253template <class T, size_t K>
1254struct IsMeshLevel<MeshSDF<T, K>> : std::true_type
1255{
1256};
1257
1262template <class T>
1263struct IsMeshLevel<FlatMeshSDF<T>> : std::true_type
1264{
1265};
1266
1272template <class T, class C>
1273struct IsMeshLevel<Fused::Node<Fused::AffineOp<T>, C>> : IsMeshLevel<C>
1274{
1275};
1276
1284template <class T, bool IsAnnulus, class C>
1285struct DeferredOperand<Fused::Node<Fused::ShiftOp<T, IsAnnulus>, C>, std::enable_if_t<IsMeshLevel<C>::value>>
1286 : std::true_type
1287{
1288};
1289
1297template <class S, class = void>
1299{
1300};
1301
1306template <class T>
1308{
1312 static constexpr bool Convex = true;
1313
1321 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
1322 static inline Piece<T>
1323 piece(const SphereSDF<T>& a_shape, const T a_level, const RayT<T>& a_ray) noexcept
1324 {
1325 return RayDetail::ballPiece(a_ray, a_shape.getCenter(), a_shape.getRadius() + a_level);
1326 }
1327};
1328
1333template <class T>
1335{
1339 static constexpr bool Convex = true;
1340
1348 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
1349 static inline Piece<T>
1350 piece(const CapsuleSDF<T>& a_shape, const T a_level, const RayT<T>& a_ray) noexcept
1351 {
1352 return RayDetail::capsulePiece(a_ray, a_shape.getCenter1(), a_shape.getCenter2(), a_shape.getRadius() + a_level);
1353 }
1354};
1355
1360template <class T>
1362{
1366 static constexpr bool Convex = true;
1367
1375 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
1376 static inline Piece<T>
1377 piece(const InfiniteCylinderSDF<T>& a_shape, const T a_level, const RayT<T>& a_ray) noexcept
1378 {
1379 return RayDetail::tubePiece(
1380 a_ray, a_shape.getCenter(), Vec3T<T>::unit(a_shape.getAxis()), a_shape.getRadius() + a_level);
1381 }
1382};
1383
1388template <class T>
1390{
1394 static constexpr bool Convex = true;
1395
1403 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
1404 static inline Piece<T>
1405 piece(const PlaneSDF<T>& a_shape, const T a_level, const RayT<T>& a_ray) noexcept
1406 {
1407 Piece<T> p;
1408
1409 p.clip(a_ray, a_shape.getNormal(), dot(a_shape.getNormal(), a_shape.getPoint()) + a_level);
1410
1411 return p;
1412 }
1413};
1414
1419template <class T>
1421{
1425 static constexpr bool Convex = true;
1426
1434 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
1435 static inline Piece<T>
1436 piece(const BoxSDF<T>& a_shape, const T a_level, const RayT<T>& a_ray) noexcept
1437 {
1438 const Vec3T<T>& lo = a_shape.getLowCorner();
1439 const Vec3T<T>& hi = a_shape.getHighCorner();
1440
1441 if (a_level > T(0)) {
1442 return RayDetail::roundedBoxPiece(a_ray, lo, hi, a_level);
1443 }
1444
1445 const Vec3T<T> shrink = -a_level * Vec3T<T>::ones();
1446
1447 return RayDetail::boxPiece(a_ray, lo + shrink, hi - shrink);
1448 }
1449};
1450
1455template <class S>
1457{
1461 static constexpr bool Convex = false;
1462
1466 static constexpr bool Mesh = true;
1467};
1468
1475template <class T, size_t K, size_t W>
1476struct LevelForm<TriMeshSDF<T, K, W>> : MeshLevelForm<TriMeshSDF<T, K, W>>
1477{
1478};
1479
1485template <class T, size_t K>
1486struct LevelForm<MeshSDF<T, K>> : MeshLevelForm<MeshSDF<T, K>>
1487{
1488};
1489
1494template <class T>
1495struct LevelForm<FlatMeshSDF<T>> : MeshLevelForm<FlatMeshSDF<T>>
1496{
1497};
1498
1504template <class S, class = void>
1505struct HasLevelForm : std::false_type
1506{
1507};
1508
1513template <class S>
1514struct HasLevelForm<S, std::void_t<decltype(LevelForm<S>::Convex)>> : std::true_type
1515{
1516};
1517
1524template <class T, class C>
1525struct LevelForm<Fused::Node<Fused::AffineOp<T>, C>, std::enable_if_t<HasLevelForm<C>::value>>
1526{
1530 static constexpr bool Convex = false;
1531
1535 static constexpr bool Mesh = false;
1536};
1537
1545template <class S>
1547{
1552
1556 static constexpr DistanceQuality distanceQuality = distanceQualityOf<S>;
1557
1562
1567
1572
1578 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
1579 inline T
1580 signedDistance(const Vec3T<T>& a_point) const noexcept
1581 {
1582 const T f = m_shape.signedDistance(a_point);
1583
1584 return (m_annulus ? std::abs(f) : f) - m_level;
1585 }
1586
1591 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
1593 computeBoundingVolume() const noexcept
1594 {
1595 const BoundingVolumes::AABBT<T> box = m_shape.computeBoundingVolume();
1596 const Vec3T<T> grow = Math::max(m_level, T(0)) * Vec3T<T>::ones();
1597
1598 return BoundingVolumes::AABBT<T>(box.getLowCorner() - grow, box.getHighCorner() + grow);
1599 }
1600};
1601
1606template <class S>
1607struct IsComposition<LevelView<S>> : std::bool_constant<CalledOutOfLine<S>>
1608{
1609};
1610
1622template <class T, class S>
1624inline int
1625convexLevelSpans(const LevelView<S>& a_view, const RayT<T>& a_ray, Piece<T> (&a_spans)[2]) noexcept
1626{
1627 if (a_view.m_annulus && !(a_view.m_level > T(0))) {
1628 return 0;
1629 }
1630
1631 const Piece<T> outer = LevelForm<S>::piece(a_view.m_shape, a_view.m_level, a_ray);
1632
1633 if (!outer.hit()) {
1634 return 0;
1635 }
1636
1637 const Piece<T> inner =
1638 a_view.m_annulus ? LevelForm<S>::piece(a_view.m_shape, -a_view.m_level, a_ray) : Piece<T>::empty();
1639
1640 if (!inner.hit()) {
1641 a_spans[0] = outer;
1642
1643 return 1;
1644 }
1645
1646 int n = 0;
1647
1648 if (inner.m_in > outer.m_in) {
1649 a_spans[n++] = Piece<T>{outer.m_in, inner.m_in, outer.m_nIn, -inner.m_nIn};
1650 }
1651
1652 if (outer.m_out > inner.m_out) {
1653 a_spans[n++] = Piece<T>{inner.m_out, outer.m_out, -inner.m_nOut, outer.m_nOut};
1654 }
1655
1656 return n;
1657}
1658
1670template <class T>
1671[[nodiscard]] EBGEOMETRY_HOST_DEVICE
1672inline HitT<T>
1674 const RayT<T>& a_ray, const T a_t, const Vec3T<T>& a_normal, const bool a_entering, const uint32_t a_primitive)
1675{
1676 HitT<T> hit;
1677
1679 hit.m_t = a_t;
1680 hit.m_point = a_ray.at(a_t);
1681 hit.m_normal = a_normal;
1682 hit.m_entering = a_entering;
1683 hit.m_exact = true;
1684 hit.m_primitive = a_primitive;
1685
1686 return hit;
1687}
1688
1701template <class T, class S>
1702[[nodiscard]] EBGEOMETRY_HOST_DEVICE
1703inline Vec3T<T>
1704levelNormal(const LevelView<S>& a_view, const Vec3T<T>& a_point, const T a_h) noexcept
1705{
1706 const Vec3T<T> n = RayDetail::gradientNormal(a_view.m_shape, a_point, a_h);
1707
1708 return (a_view.m_annulus && RayDetail::valueAt(a_view.m_shape, a_point) < T(0)) ? -n : n;
1709}
1710
1711// A level view's spans, out of line on a device: declared here for its ray form, defined after it.
1712template <class T, class S>
1715inline void
1716levelSpans(const LevelView<S>& a_view,
1717 const RayT<T>& a_ray,
1718 T a_tMin,
1719 T a_tMax,
1720 const RayOptions<T>& a_options,
1721 bool a_firstOnly,
1722 RaySpansT<T>& a_spans) noexcept;
1723
1724} // namespace RayDetail
1725
1735template <class S>
1736struct RayForm<RayDetail::LevelView<S>, std::enable_if_t<RayDetail::HasLevelForm<S>::value>>
1737{
1742
1746 using T = typename View::T;
1747
1757 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
1758 static inline HitT<T>
1759 firstHit(const View& a_view, const RayT<T>& a_ray, T a_tMin, T a_tMax, const RayOptions<T>& a_options) noexcept
1760 {
1761 using Form = RayDetail::LevelForm<S>;
1762
1763 if constexpr (Form::Convex) {
1764 RayDetail::Piece<T> spans[2];
1765
1766 const int n = RayDetail::convexLevelSpans(a_view, a_ray, spans);
1767
1768 for (int i = 0; i < n; i++) {
1769 const RayDetail::Piece<T>& s = spans[i];
1770
1771 if (s.m_in >= a_tMin && s.m_in <= a_tMax) {
1772 return RayDetail::levelHitAt(a_ray, s.m_in, s.m_nIn, true, 0U);
1773 }
1774
1775 if (s.m_in < a_tMin && s.m_out >= a_tMin && s.m_out <= a_tMax) {
1776 return RayDetail::levelHitAt(a_ray, s.m_out, s.m_nOut, false, 0U);
1777 }
1778 }
1779
1780 return HitT<T>();
1781 }
1782 else if constexpr (Form::Mesh) {
1783 if (a_view.m_annulus || a_view.m_level == T(0)) {
1784 if (a_view.m_level == T(0) && !a_view.m_annulus) {
1785 return RayDetail::shapeHit(a_view.m_shape, a_ray, a_tMin, a_tMax, a_options);
1786 }
1787
1788 return annulusHit(a_view, a_ray, a_tMin, a_tMax);
1789 }
1790
1791 // An offset of a mesh: from its spans, the crossing's normal from the mesh (leaf 0) or its offset faces
1792 // (leaf 1).
1793 const auto trace =
1794 [&a_view, &a_ray, &a_options](const T a_t0, const T a_t1, const bool a_all, RaySpansT<T>& a_spans) noexcept {
1795 RayDetail::levelSpans(a_view, a_ray, a_t0, a_t1, a_options, !a_all, a_spans);
1796 };
1797
1798 const auto resolve = [&a_view, &a_ray, &a_options](const T a_t, const uint32_t a_leaf) noexcept {
1799 if (a_leaf == 0U) {
1800 return RayDetail::leafHit(a_view.m_shape, a_ray, a_t, a_options);
1801 }
1802
1803 return annulusLeafHit(View{a_view.m_shape, std::abs(a_view.m_level), true}, a_ray, a_t, a_options);
1804 };
1805
1806 return RayDetail::traceSpans(trace, resolve, a_ray, a_tMin, a_tMax);
1807 }
1808 else {
1809 const auto& map = a_view.m_shape.layout().m_op;
1810
1812 const RayDetail::MappedRay<T> mapped = RayDetail::mapRay(m.m_values, a_ray, a_tMin, a_tMax);
1813
1814 const auto inner = mappedView(a_view);
1815
1816 HitT<T> hit = EBGeometry::firstHit(inner, mapped.m_ray, mapped.m_t0, mapped.m_t1, a_options);
1817
1818 if (hit.m_status != HitStatus::Miss) {
1819 hit.m_t = Math::clamp(hit.m_t / mapped.m_scale, a_tMin, a_tMax);
1820 }
1821
1822 if (hit.hit()) {
1823 hit.m_point = a_ray.at(hit.m_t);
1824 hit.m_normal = RayDetail::mapNormalBack(m.m_values, hit.m_normal);
1825 }
1826
1827 return hit;
1828 }
1829 }
1830
1842 static inline void
1843 spans(const View& a_view,
1844 const RayT<T>& a_ray,
1845 T a_tMin,
1846 T a_tMax,
1847 const RayOptions<T>& a_options,
1848 bool a_firstOnly,
1849 RaySpansT<T>& a_spans) noexcept
1850 {
1851 using Form = RayDetail::LevelForm<S>;
1852
1853 if constexpr (Form::Convex) {
1854 RayDetail::Piece<T> spans[2];
1855
1856 const int n = RayDetail::convexLevelSpans(a_view, a_ray, spans);
1857
1858 a_spans.reset(a_tMax);
1859
1860 for (int i = 0; i < n; i++) {
1861 a_spans.addClipped(spans[i].m_in, spans[i].m_out, a_tMin, a_tMax);
1862 }
1863 }
1864 else if constexpr (Form::Mesh) {
1865 const T level = a_view.m_level;
1866
1867 if (!a_view.m_annulus && level == T(0)) {
1868 RayDetail::meshSpans(a_view.m_shape, a_ray, a_tMin, a_tMax, a_options, a_firstOnly, a_spans);
1869
1870 return;
1871 }
1872
1873 if (a_view.m_annulus) {
1874 annulusSpans(a_view, a_ray, a_tMin, a_tMax, a_firstOnly, a_spans);
1875
1876 return;
1877 }
1878
1879 // An offset: the mesh (leaf 0) united with its faces offset by the level (leaf 1), or less those offset
1880 // by its opposite.
1881 const View faces{a_view.m_shape, std::abs(level), true};
1882 RaySpansT<T> grown;
1883 RaySpansT<T> scratch;
1884
1885 if (level > T(0)) {
1886 RayDetail::meshSpans(a_view.m_shape, a_ray, a_tMin, a_tMax, a_options, a_firstOnly, a_spans);
1887 annulusSpans(faces, a_ray, a_tMin, a_spans.m_horizon, a_firstOnly, grown);
1888 RayDetail::nameLeaf(1U, grown);
1889 RayDetail::uniteInto(a_spans, grown);
1890
1891 if (a_firstOnly) {
1892 RayDetail::keepFirst(a_spans);
1893 }
1894 }
1895 else {
1896 RayDetail::meshSpans(a_view.m_shape, a_ray, a_tMin, a_tMax, a_options, false, a_spans);
1897 annulusSpans(faces, a_ray, a_tMin, a_spans.m_horizon, false, grown);
1898 RayDetail::nameLeaf(1U, grown);
1899 RayDetail::complement(grown, a_tMin, scratch);
1900 RayDetail::intersectInto(a_spans, scratch, grown);
1901 }
1902 }
1903 else {
1904 const auto& map = a_view.m_shape.layout().m_op;
1905
1907 const RayDetail::MappedRay<T> mapped = RayDetail::mapRay(m.m_values, a_ray, a_tMin, a_tMax);
1908
1909 RayDetail::shapeSpans(
1910 mappedView(a_view), mapped.m_ray, mapped.m_t0, mapped.m_t1, a_options, a_firstOnly, a_spans);
1911 RayDetail::mapBack(mapped, a_tMin, a_tMax, a_spans);
1912 }
1913 }
1914
1915private:
1921 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
1922 static inline auto
1923 mappedView(const View& a_view) noexcept
1924 {
1925 const auto& op = a_view.m_shape.layout().m_op;
1926 const auto operand = RayDetail::fusedOperand<0>(a_view.m_shape);
1927
1928 return RayDetail::LevelView<std::decay_t<decltype(operand)>>{
1929 operand, a_view.m_level / op.m_valueScale, a_view.m_annulus};
1930 }
1931
1941 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
1942 static inline HitT<T>
1943 annulusHit(const View& a_view, const RayT<T>& a_ray, const T a_tMin, const T a_tMax) noexcept
1944 {
1945 if (!(a_view.m_level > T(0))) {
1946 return HitT<T>();
1947 }
1948
1949 const RayDetail::NearestOffsetFace<T> found =
1950 RayDetail::nearestOffsetFace(a_view.m_shape, a_ray, a_tMin, a_tMax, a_view.m_level);
1951
1952 if (!found.m_inside) {
1953 return (found.m_face == UINT32_MAX) ? HitT<T>()
1954 : RayDetail::levelHitAt(a_ray, found.m_t, found.m_normal, true, found.m_face);
1955 }
1956
1957 RayDetail::OffsetFaceUnion<T> merged;
1958
1959 RayDetail::offsetFaceUnion(a_view.m_shape, a_ray, a_tMin, a_tMax, a_view.m_level, merged);
1960
1961 if (merged.m_count > 0U && merged.m_out[0] < merged.m_horizon) {
1962 return RayDetail::levelHitAt(a_ray, merged.m_out[0], merged.m_nOut[0], false, 0U);
1963 }
1964
1965 HitT<T> hit;
1966
1967 if (merged.m_horizon < a_tMax) {
1968 hit.m_status = HitStatus::Unresolved;
1969 hit.m_t = merged.m_horizon;
1970 }
1971
1972 return hit;
1973 }
1974
1985 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
1986 static inline HitT<T>
1987 annulusLeafHit(const View& a_view, const RayT<T>& a_ray, const T a_t, const RayOptions<T>& a_options) noexcept
1988 {
1989 const auto box = a_view.computeBoundingVolume();
1990 const T width = a_options.m_tolerance * RayDetail::scaleOf(box.getLowCorner(), box.getHighCorner(), a_options);
1991
1992 HitT<T> hit = annulusHit(a_view, a_ray, a_t - width, a_t + width);
1993
1994 if (!hit.hit()) {
1995 hit.m_normal = RayDetail::levelNormal(a_view, a_ray.at(a_t), width);
1996 }
1997
1998 return hit;
1999 }
2000
2012 static inline void
2013 annulusSpans(const View& a_view,
2014 const RayT<T>& a_ray,
2015 const T a_tMin,
2016 const T a_tMax,
2017 const bool a_firstOnly,
2018 RaySpansT<T>& a_spans) noexcept
2019 {
2020 a_spans.reset(a_tMax);
2021
2022 if (!(a_view.m_level > T(0))) {
2023 return;
2024 }
2025
2026 if (a_firstOnly) {
2027 const RayDetail::NearestOffsetFace<T> found =
2028 RayDetail::nearestOffsetFace(a_view.m_shape, a_ray, a_tMin, a_tMax, a_view.m_level);
2029
2030 if (!found.m_inside) {
2031 if (found.m_face != UINT32_MAX) {
2032 a_spans.add(SpanEndT<T>{found.m_t, 0U, SpanEndT<T>::Exact}, RayDetail::openAt(found.m_t));
2033
2034 a_spans.m_horizon = found.m_t;
2035 }
2036
2037 return;
2038 }
2039 }
2040
2041 RayDetail::OffsetFaceUnion<T> merged;
2042
2043 RayDetail::offsetFaceUnion(a_view.m_shape, a_ray, a_tMin, a_tMax, a_view.m_level, merged);
2044
2045 merged.toSpans(a_tMin, a_spans);
2046 }
2047};
2048
2049namespace RayDetail {
2050
2066template <class T, class S>
2069inline void
2071 const RayT<T>& a_ray,
2072 const T a_tMin,
2073 const T a_tMax,
2074 const RayOptions<T>& a_options,
2075 const bool a_firstOnly,
2076 RaySpansT<T>& a_spans) noexcept
2077{
2078 RayForm<LevelView<S>>::spans(a_view, a_ray, a_tMin, a_tMax, a_options, a_firstOnly, a_spans);
2079}
2080
2096template <class T, class S>
2099inline void
2100levelSpansWithin(const LevelView<S>& a_view,
2101 const RayT<T>& a_ray,
2102 const RaySpansT<T>& a_within,
2103 const T a_scale,
2104 const RayOptions<T>& a_options,
2105 const bool a_firstOnly,
2106 RaySpansT<T>& a_spans) noexcept
2107{
2108 const T horizon = a_within.m_horizon * a_scale;
2109
2110 a_spans.reset(horizon);
2111
2112 RaySpansT<T> piece;
2113
2114 for (uint32_t i = 0; i < a_within.m_count && a_within.m_spans[i].m_in.m_t * a_scale <= horizon; i++) {
2115 const T t0 = a_within.m_spans[i].m_in.m_t * a_scale;
2116 const T t1 = Math::min(a_within.m_spans[i].m_out.m_t * a_scale, horizon);
2117
2118 RayDetail::levelSpans(a_view, a_ray, t0, t1, a_options, a_firstOnly, piece);
2119
2120 for (uint32_t k = 0; k < piece.m_count && piece.m_spans[k].m_in.m_t <= piece.m_horizon; k++) {
2121 const typename RaySpansT<T>::Span span = RayDetail::cut(piece.m_spans[k], piece.m_horizon);
2122
2123 a_spans.add(span.m_in, span.m_out);
2124 }
2125
2126 if (piece.m_horizon < t1 || (a_firstOnly && piece.m_count > 0U)) {
2127 a_spans.m_horizon = Math::min(a_spans.m_horizon, piece.m_horizon);
2128 }
2129
2130 // Cut back to this span or before it: the list is known no further, so no later span can add to it.
2131 if (a_spans.m_horizon <= t1) {
2132 return;
2133 }
2134 }
2135}
2136
2149template <class T, class S>
2150[[nodiscard]] EBGEOMETRY_HOST_DEVICE
2152inline HitT<T>
2153levelHit(const LevelView<S>& a_view,
2154 const RayT<T>& a_ray,
2155 const T a_tMin,
2156 const T a_tMax,
2157 const RayOptions<T>& a_options) noexcept
2158{
2159 return RayForm<LevelView<S>>::firstHit(a_view, a_ray, a_tMin, a_tMax, a_options);
2160}
2161
2176template <class T, class S>
2177[[nodiscard]] EBGEOMETRY_HOST_DEVICE
2179inline HitT<T>
2180levelLeafHit(const LevelView<S>& a_view, const RayT<T>& a_ray, const T a_t, const RayOptions<T>& a_options) noexcept
2181{
2182 const auto box = a_view.computeBoundingVolume();
2183 const T width = a_options.m_tolerance * RayDetail::scaleOf(box.getLowCorner(), box.getHighCorner(), a_options);
2184
2185 HitT<T> nearest;
2186
2187 T from = a_t - width;
2188
2189 // As leafHit(): at most three crossings in the window.
2190 for (int k = 0; k < 3; k++) {
2191 const HitT<T> hit = RayDetail::levelHit(a_view, a_ray, from, a_t + width, a_options);
2192
2193 if (!hit.hit()) {
2194 break;
2195 }
2196
2197 if (!nearest.hit() || std::abs(hit.m_t - a_t) < std::abs(nearest.m_t - a_t)) {
2198 nearest = hit;
2199 }
2200
2201 if (hit.m_t >= a_t) {
2202 break;
2203 }
2204
2205 from = hit.m_t + T(1e-3) * width;
2206 }
2207
2208 if (!nearest.hit()) {
2209 nearest.m_normal = RayDetail::levelNormal(a_view, a_ray.at(a_t), width);
2210 }
2211
2212 return nearest;
2213}
2214
2215} // namespace RayDetail
2216
2224template <class T, bool IsAnnulus, class C>
2225struct RayForm<Fused::Node<Fused::ShiftOp<T, IsAnnulus>, C>, std::enable_if_t<RayDetail::HasLevelForm<C>::value>>
2226{
2231
2241 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
2242 static inline HitT<T>
2243 firstHit(const Node& a_shape, const RayT<T>& a_ray, T a_tMin, T a_tMax, const RayOptions<T>& a_options) noexcept
2244 {
2245 return RayDetail::levelHit(view(a_shape), a_ray, a_tMin, a_tMax, a_options);
2246 }
2247
2259 static inline void
2260 spans(const Node& a_shape,
2261 const RayT<T>& a_ray,
2262 T a_tMin,
2263 T a_tMax,
2264 const RayOptions<T>& a_options,
2265 bool a_firstOnly,
2266 RaySpansT<T>& a_spans) noexcept
2267 {
2268 RayDetail::levelSpans(view(a_shape), a_ray, a_tMin, a_tMax, a_options, a_firstOnly, a_spans);
2269 }
2270
2271private:
2277 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
2278 static inline RayDetail::LevelView<C>
2279 view(const Node& a_shape) noexcept
2280 {
2281 return RayDetail::LevelView<C>{RayDetail::fusedOperand<0>(a_shape), a_shape.layout().m_op.m_amount, IsAnnulus};
2282 }
2283};
2284
2285} // namespace EBGeometry
2286
2287#endif
File containing various analytic signed distance fields.
Ray forms (see RayForm) for the analytic shapes that have a closed-form crossing: PlaneSDF,...
What PackedBVH::traverse() tracks through a tree: query types, the child-box rows they test,...
The ray forms (see RayForm) of compositions: the fused union, intersection, difference,...
Declaration of iterators for DCEL surface tessellations.
Fused CSG: the operations of the expression builder (EBGeometry_Expr.hpp) as types,...
#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
Scalar helpers and numeric limits that compile for both host and device.
Declaration of signed distance functions for DCEL meshes.
The ray forms (see RayForm) of the mesh SDFs. TriMeshSDF: its BVH walked along the ray (PackedBVH::tr...
void meshSpans(const S &a_mesh, const RayT< T > &a_ray, const T a_tMin, const T a_tMax, const RayOptions< T > &a_options, const bool a_firstOnly, RaySpansT< T > &a_spans) noexcept
A mesh's own spans (RayDetail::shapeSpans()), out of line on a device, where its level sets need them...
Definition EBGeometry_OffsetRayForms.hpp:1215
Piece< T > boxPiece(const RayT< T > &a_ray, const Vec3T< T > &a_lo, const Vec3T< T > &a_hi) noexcept
A ray through an axis-aligned box.
Definition EBGeometry_OffsetRayForms.hpp:307
NearestOffsetFace< T > nearestOffsetFace(const S &a_mesh, const RayT< T > &a_ray, const T a_tMin, const T a_tMax, const T a_radius) noexcept
The nearest entry after a_tMin of a mesh's faces offset by a radius, and whether one holds a_tMin.
Definition EBGeometry_OffsetRayForms.hpp:1110
HitT< T > levelHitAt(const RayT< T > &a_ray, const T a_t, const Vec3T< T > &a_normal, const bool a_entering, const uint32_t a_primitive)
A crossing at a distance, with its normal, as a hit.
Definition EBGeometry_OffsetRayForms.hpp:1673
bool offsetFaceEntry(const RayT< T > &a_ray, const Vec3T< T > &a_n, const Vec3T< T > &a_p0, const Edges &a_edges, const T a_radius, const T a_tMin, const T a_best, T &a_t, Vec3T< T > &a_normal, bool &a_inSlab) noexcept
Where a ray first enters a planar convex face offset by a radius, if before a bound: the offset face'...
Definition EBGeometry_OffsetRayForms.hpp:454
Piece< T > tubePiece(const RayT< T > &a_ray, const Vec3T< T > &a_point, const Vec3T< T > &a_axis, const T a_radius) noexcept
A ray through an infinite cylinder: the points within a radius of a line.
Definition EBGeometry_OffsetRayForms.hpp:216
void offsetFaceUnion(const S &a_mesh, const RayT< T > &a_ray, const T a_tMin, const T a_tMax, const T a_radius, OffsetFaceUnion< T > &a_union) noexcept
The union of a mesh's faces offset by a radius along a ray, merged span by span.
Definition EBGeometry_OffsetRayForms.hpp:1169
Piece< T > roundedBoxPiece(const RayT< T > &a_ray, const Vec3T< T > &a_lo, const Vec3T< T > &a_hi, const T a_radius) noexcept
A ray through a box grown by a radius: three boxes each grown along one axis, and a capsule along eac...
Definition EBGeometry_OffsetRayForms.hpp:338
Vec3T< T > levelNormal(const LevelView< S > &a_view, const Vec3T< T > &a_point, const T a_h) noexcept
A level view's outward normal at a point, from its shape's gradient (out of line already): reversed o...
Definition EBGeometry_OffsetRayForms.hpp:1704
Piece< T > offsetFacePiece(const RayT< T > &a_ray, const Vec3T< T > &a_n, const Vec3T< T > &a_p0, const Edges &a_edges, const T a_radius) noexcept
Where a ray is inside a planar convex face offset by a radius: its slab over the face,...
Definition EBGeometry_OffsetRayForms.hpp:569
int convexLevelSpans(const LevelView< S > &a_view, const RayT< T > &a_ray, Piece< T >(&a_spans)[2]) noexcept
The spans of a convex level form's offset (one piece) or annulus (the outer piece less the inner,...
Definition EBGeometry_OffsetRayForms.hpp:1625
Piece< T > capsulePiece(const RayT< T > &a_ray, const Vec3T< T > &a_a, const Vec3T< T > &a_b, const T a_radius) noexcept
A ray through a capsule: the segment [a, b] grown by a radius, the cylinder between the end planes un...
Definition EBGeometry_OffsetRayForms.hpp:270
T twiceArea(const Vec3T< T > &a_n, const Edges &a_edges) noexcept
Twice a planar polygon's signed area along its normal.
Definition EBGeometry_OffsetRayForms.hpp:410
Piece< T > ballPiece(const RayT< T > &a_ray, const Vec3T< T > &a_center, const T a_radius) noexcept
A ray through a ball.
Definition EBGeometry_OffsetRayForms.hpp:180
void levelSpans(const LevelView< S > &a_view, const RayT< T > &a_ray, T a_tMin, T a_tMax, const RayOptions< T > &a_options, bool a_firstOnly, RaySpansT< T > &a_spans) noexcept
A level view's spans (its ray form's spans()), out of line on a device.
Definition EBGeometry_OffsetRayForms.hpp:2070
The span arithmetic that traces a composition along a ray: each operand's spans (where the ray is ins...
Ray queries on any shape: where a ray, or the segment between two points, first crosses its surface (...
Axis-aligned bounding box (AABB) enclosing a set of 3D points.
Definition EBGeometry_BoundingVolumes.hpp:37
Vec3T< T > & getLowCorner() noexcept
Get a modifiable reference to the low corner of the AABB.
Vec3T< T > & getHighCorner() noexcept
Get a modifiable reference to the high corner of the AABB.
Signed distance field for an axis-aligned box (AABB).
Definition EBGeometry_AnalyticDistanceFunctions.hpp:351
Signed distance field for a capsule (pill shape): a cylinder capped with hemispheres.
Definition EBGeometry_AnalyticDistanceFunctions.hpp:1117
Half-edge iterator class for navigating the half edge mesh.
Definition EBGeometry_DCEL_Iterator.hpp:51
bool ok() const noexcept
Function which checks if the iteration can be continued.
Signed distance function for a DCEL mesh. Does not use BVHs.
Definition EBGeometry_MeshDistanceFunctions.hpp:79
A fused composition: an operation applied to its operands, itself a shape (isShape).
Definition EBGeometry_Fused.hpp:313
Signed distance field for an infinitely long cylinder.
Definition EBGeometry_AnalyticDistanceFunctions.hpp:947
A FlatMeshSDF must be trivially copyable: that is what lets a rebasedView() be byte-copied into a dev...
Definition EBGeometry_MeshDistanceFunctions.hpp:249
Signed distance function for a plane.
Definition EBGeometry_AnalyticDistanceFunctions.hpp:60
Signed distance field for a sphere.
Definition EBGeometry_AnalyticDistanceFunctions.hpp:199
Signed distance function for a pure triangle mesh using SoA-grouped primitives in a compact (lineariz...
Definition EBGeometry_MeshDistanceFunctions.hpp:514
Three-dimensional vector class with arithmetic operators.
Definition EBGeometry_Vec.hpp:258
T length() const noexcept
Compute vector length.
static constexpr Vec3T< T > ones() noexcept
Return a vector with x = y = z = 1.
static constexpr Vec3T< T > unit(const size_t a_dir) noexcept
Return the unit basis vector for the given coordinate axis.
static constexpr Vec3T< T > zeros() noexcept
Return a vector with x = y = z = 0.
AffineCoefficients< T > affineCoefficients(const Affine3T< T > &a_map) noexcept
An affine map's coefficients, laid out as affinePoint() reads them.
Definition EBGeometry_CSGOps.hpp:389
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.
typename ExprDetail::ShapeInfo< S >::precision ShapePrecision
The precision of a shape type: T for a shape whose box type is AABBT<T>.
Definition EBGeometry_Expr.hpp:158
constexpr Vec3T< T > cross(const Vec3T< T > &u, const Vec3T< T > &v) noexcept
Cross product of two 3D vectors.
T dot(const Vec2T< T > &u, const Vec2T< T > &v) noexcept
Dot product of two 2D vectors.
Vec3T< T > normal(const F &a_function, const Vec3T< T > &a_point, const T &a_delta) noexcept
Outward unit normal of a function's level set, by central finite differences.
DistanceQuality
How far the value of a signed distance function can be trusted, from strongest to weakest.
Definition EBGeometry_DistanceQuality.hpp:25
@ Bound
The value's magnitude never exceeds the distance to the surface: it may underestimate,...
@ 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.
Fixed-size array with host- and device-callable members.
Definition EBGeometry_Array.hpp:33
The boxes of the K children of one interior node, in SoA layout: m_lo[axis][child] and m_hi[axis][chi...
Definition EBGeometry_BVHQuery.hpp:47
An affine map's 12 coefficients: the three rows of its matrix, then its shift.
Definition EBGeometry_CSGOps.hpp:373
T m_values[12]
Row 0, row 1 and row 2 of the matrix, then the shift.
Definition EBGeometry_CSGOps.hpp:377
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
Numeric limits, with the meanings of the std::numeric_limits members of the same names.
Definition EBGeometry_Math.hpp:91
Whether an intersection traces a fused node's operand only within the spans of its other operands,...
Definition EBGeometry_CSGRayForms.hpp:180
A BVH ray query whose boxes are grown by a radius: what a mesh's offset faces are walked with.
Definition EBGeometry_OffsetRayForms.hpp:612
GrownRayQuery(const RayT< T > &a_ray, const T a_tMin, const T a_grow) noexcept
The query along a ray.
Definition EBGeometry_OffsetRayForms.hpp:650
Array< T, K > keys(const BVH::ChildBoxes< T, K > &a_row) const noexcept
Where the ray enters the K grown child boxes.
Definition EBGeometry_OffsetRayForms.hpp:684
Vec3T< T > m_inverseDirection
1 / direction, per component.
Definition EBGeometry_OffsetRayForms.hpp:631
T key(const BVH::ChildBoxes< T, K > &a_row, const size_t a_k) const noexcept
Where the ray enters one grown child box.
Definition EBGeometry_OffsetRayForms.hpp:705
T m_tMin
Start of the ray.
Definition EBGeometry_OffsetRayForms.hpp:636
Vec3T< T > m_origin
Ray origin.
Definition EBGeometry_OffsetRayForms.hpp:626
T entry(const T(&a_lo)[3], const T(&a_hi)[3]) const noexcept
Where the ray enters a grown box.
Definition EBGeometry_OffsetRayForms.hpp:665
T m_grow
How much each box is grown.
Definition EBGeometry_OffsetRayForms.hpp:641
T key(const BoundingVolumes::AABBT< T > &a_box) const noexcept
Where the ray enters a grown box.
Definition EBGeometry_OffsetRayForms.hpp:720
T Scalar
Floating-point precision.
Definition EBGeometry_OffsetRayForms.hpp:616
T Key
Key type: a distance along the ray.
Definition EBGeometry_OffsetRayForms.hpp:621
Whether a shape's level sets have a closed form (a LevelForm specialisation).
Definition EBGeometry_OffsetRayForms.hpp:1506
Whether a shape's level sets are a mesh's (its faces offset), or a fused affine map of one: costly to...
Definition EBGeometry_OffsetRayForms.hpp:1234
static Piece< T > piece(const BoxSDF< T > &a_shape, const T a_level, const RayT< T > &a_ray) noexcept
The piece.
Definition EBGeometry_OffsetRayForms.hpp:1436
static Piece< T > piece(const CapsuleSDF< T > &a_shape, const T a_level, const RayT< T > &a_ray) noexcept
The piece.
Definition EBGeometry_OffsetRayForms.hpp:1350
static Piece< T > piece(const InfiniteCylinderSDF< T > &a_shape, const T a_level, const RayT< T > &a_ray) noexcept
The piece.
Definition EBGeometry_OffsetRayForms.hpp:1377
static Piece< T > piece(const PlaneSDF< T > &a_shape, const T a_level, const RayT< T > &a_ray) noexcept
The piece.
Definition EBGeometry_OffsetRayForms.hpp:1405
static Piece< T > piece(const SphereSDF< T > &a_shape, const T a_level, const RayT< T > &a_ray) noexcept
The piece.
Definition EBGeometry_OffsetRayForms.hpp:1323
A level set {f <= c} of a shape's value f along a ray.
Definition EBGeometry_OffsetRayForms.hpp:1299
A level set of a shape, {f <= c}, or an annulus of it, {f <= c} less {f < -c}: a shape whose ray form...
Definition EBGeometry_OffsetRayForms.hpp:1547
T m_level
The level c: the offset, or the annulus's half-thickness.
Definition EBGeometry_OffsetRayForms.hpp:1566
S m_shape
The shape.
Definition EBGeometry_OffsetRayForms.hpp:1561
T signedDistance(const Vec3T< T > &a_point) const noexcept
The value: f - c, or |f| - c.
Definition EBGeometry_OffsetRayForms.hpp:1580
ShapePrecision< S > T
Floating-point precision.
Definition EBGeometry_OffsetRayForms.hpp:1551
static constexpr DistanceQuality distanceQuality
The quality of its value: the operand's.
Definition EBGeometry_OffsetRayForms.hpp:1556
BoundingVolumes::AABBT< T > computeBoundingVolume() const noexcept
The shape's box, grown by the level.
Definition EBGeometry_OffsetRayForms.hpp:1593
bool m_annulus
Whether this is an annulus.
Definition EBGeometry_OffsetRayForms.hpp:1571
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
static void walk(const FlatMeshSDF< T > &a_mesh, const RayT< T > &, const T, const T, State &a_state, const Face &a_face, const Bound &) noexcept
Walk every face.
Definition EBGeometry_OffsetRayForms.hpp:1049
static void walk(const MeshSDF< T, K > &a_mesh, const RayT< T > &a_ray, const T a_tMin, const T a_grow, State &a_state, const Face &a_face, const Bound &a_bound) noexcept
Walk the faces whose boxes, grown by a_grow, the ray enters before the bound.
Definition EBGeometry_OffsetRayForms.hpp:974
static void visit(const Mesh &a_mesh, const uint32_t a_id, State &a_state, const Face &a_face) noexcept
Call a_face for one DCEL face.
Definition EBGeometry_OffsetRayForms.hpp:1010
static void walk(const TriMeshSDF< T, K, W > &a_mesh, const RayT< T > &a_ray, const T a_tMin, const T a_grow, State &a_state, const Face &a_face, const Bound &a_bound) noexcept
Walk the faces whose boxes, grown by a_grow, the ray enters before the bound.
Definition EBGeometry_OffsetRayForms.hpp:911
How a mesh's faces are walked along a ray, each given as its normal, a point in its plane,...
Definition EBGeometry_OffsetRayForms.hpp:883
A mesh's level set: the mesh united with its faces offset by c, or less its faces offset by -c.
Definition EBGeometry_OffsetRayForms.hpp:1457
static constexpr bool Convex
Not a convex piece.
Definition EBGeometry_OffsetRayForms.hpp:1461
static constexpr bool Mesh
A mesh.
Definition EBGeometry_OffsetRayForms.hpp:1466
What a mesh's nearest offset-face walk finds.
Definition EBGeometry_OffsetRayForms.hpp:1072
T m_t
The nearest entry at or after the start, or the end of the interval.
Definition EBGeometry_OffsetRayForms.hpp:1076
Vec3T< T > m_normal
Its outward normal.
Definition EBGeometry_OffsetRayForms.hpp:1086
uint32_t m_face
Its face, or UINT32_MAX.
Definition EBGeometry_OffsetRayForms.hpp:1081
bool m_inside
Whether an offset face holds the start: the ray starts inside the grown surface.
Definition EBGeometry_OffsetRayForms.hpp:1091
The union of the offset faces a ray meets, as disjoint spans merged as they come, at most Capacity: a...
Definition EBGeometry_OffsetRayForms.hpp:741
T m_out[Capacity]
Exits.
Definition EBGeometry_OffsetRayForms.hpp:755
uint32_t m_count
How many are held.
Definition EBGeometry_OffsetRayForms.hpp:765
Vec3T< T > m_nOut[Capacity]
Outward normals at the exits, of the piece each exit is on.
Definition EBGeometry_OffsetRayForms.hpp:760
T m_horizon
The union is known on [t0, m_horizon].
Definition EBGeometry_OffsetRayForms.hpp:770
void insert(T a_in, T a_out, Vec3T< T > a_nOut) noexcept
Take a piece.
Definition EBGeometry_OffsetRayForms.hpp:780
T m_in[Capacity]
Entries.
Definition EBGeometry_OffsetRayForms.hpp:750
static constexpr uint32_t Capacity
Most spans held.
Definition EBGeometry_OffsetRayForms.hpp:745
void toSpans(const T a_t0, RaySpansT< T > &a_spans) const noexcept
The union as a list over [a_t0, horizon].
Definition EBGeometry_OffsetRayForms.hpp:859
Where a ray is inside a convex piece: [m_in, m_out], with the outward normals there; empty if m_in > ...
Definition EBGeometry_OffsetRayForms.hpp:54
T m_out
Where it leaves.
Definition EBGeometry_OffsetRayForms.hpp:63
bool hit() const noexcept
Whether the ray meets the piece.
Definition EBGeometry_OffsetRayForms.hpp:81
Vec3T< T > m_nOut
Outward normal where it leaves.
Definition EBGeometry_OffsetRayForms.hpp:73
T m_in
Where the ray enters.
Definition EBGeometry_OffsetRayForms.hpp:58
void clip(const RayT< T > &a_ray, const Vec3T< T > &a_n, const T a_d) noexcept
Clip to the half-space n . x <= d.
Definition EBGeometry_OffsetRayForms.hpp:110
Vec3T< T > m_nIn
Outward normal where it enters.
Definition EBGeometry_OffsetRayForms.hpp:68
void unite(const Piece &a_other) noexcept
Unite with another piece, the union being convex: the nearer entry and the farther exit.
Definition EBGeometry_OffsetRayForms.hpp:143
static Piece empty() noexcept
An empty piece.
Definition EBGeometry_OffsetRayForms.hpp:92
static void spans(const Node &a_shape, const RayT< T > &a_ray, T a_tMin, T a_tMax, const RayOptions< T > &a_options, bool a_firstOnly, RaySpansT< T > &a_spans) noexcept
Where a ray is inside over an interval.
Definition EBGeometry_OffsetRayForms.hpp:2260
static HitT< T > firstHit(const Node &a_shape, const RayT< T > &a_ray, T a_tMin, T a_tMax, const RayOptions< T > &a_options) noexcept
The first crossing along a ray.
Definition EBGeometry_OffsetRayForms.hpp:2243
static void spans(const View &a_view, const RayT< T > &a_ray, T a_tMin, T a_tMax, const RayOptions< T > &a_options, bool a_firstOnly, RaySpansT< T > &a_spans) noexcept
Where a ray is inside over an interval.
Definition EBGeometry_OffsetRayForms.hpp:1843
typename View::T T
Floating-point precision.
Definition EBGeometry_OffsetRayForms.hpp:1746
static HitT< T > firstHit(const View &a_view, const RayT< T > &a_ray, T a_tMin, T a_tMax, const RayOptions< T > &a_options) noexcept
The first crossing along a ray.
Definition EBGeometry_OffsetRayForms.hpp:1759
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
Vec3T< T > at(T a_t) const noexcept
The point at a distance along the ray.
One end of a span: where a ray enters or leaves an object.
Definition EBGeometry_Ray.hpp:200
@ Exact
A crossing found exactly, by a ray form.
Definition EBGeometry_Ray.hpp:207