File indexing completed on 2026-05-27 07:23:58
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011
0012 #include "detray/definitions/algebra.hpp"
0013 #include "detray/definitions/containers.hpp"
0014 #include "detray/definitions/detail/qualifiers.hpp"
0015 #include "detray/definitions/indexing.hpp"
0016 #include "detray/definitions/math.hpp"
0017 #include "detray/definitions/units.hpp"
0018 #include "detray/geometry/coordinates/cartesian3D.hpp"
0019 #include "detray/geometry/coordinates/polar2D.hpp"
0020 #include "detray/geometry/detail/shape_utils.hpp"
0021 #include "detray/geometry/detail/vertexer.hpp"
0022
0023
0024 #include <limits>
0025 #include <ostream>
0026 #include <string_view>
0027
0028 namespace detray {
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048 class annulus2D {
0049 public:
0050
0051 static constexpr std::string_view name = "(stereo) annulus2D";
0052
0053
0054 enum boundaries : unsigned int {
0055 e_min_r = 0u,
0056 e_max_r = 1u,
0057 e_min_phi_rel = 2u,
0058 e_max_phi_rel = 3u,
0059 e_average_phi = 4u,
0060 e_shift_x = 5u,
0061 e_shift_y = 6u,
0062 e_size = 7u,
0063 };
0064
0065
0066 template <concepts::scalar scalar_t>
0067 using bounds_type = darray<scalar_t, boundaries::e_size>;
0068
0069
0070 template <concepts::algebra algebra_t>
0071 using local_frame_type = polar2D<algebra_t>;
0072
0073
0074 template <typename bool_t>
0075 using result_type = detail::boundary_check_result<bool_t>;
0076
0077
0078 static constexpr std::size_t dim{2u};
0079
0080
0081 template <concepts::scalar scalar_t>
0082 DETRAY_HOST_DEVICE darray<scalar_t, 8> stereo_angle(
0083 const bounds_type<scalar_t> &bounds) const {
0084
0085 return 2.f * math::atan(bounds[e_shift_y] / bounds[e_shift_x]);
0086 }
0087
0088
0089 template <concepts::scalar scalar_t, concepts::point point_t>
0090 DETRAY_HOST_DEVICE inline scalar_t get_phi_rel(
0091 const bounds_type<scalar_t> &bounds, const point_t &loc_p) const {
0092
0093 return loc_p[1] - bounds[e_average_phi];
0094 }
0095
0096
0097 template <concepts::scalar scalar_t, concepts::point point_t>
0098 DETRAY_HOST_DEVICE inline scalar_t get_r2_beam_frame(
0099 const bounds_type<scalar_t> &bounds, const point_t &loc_p) const {
0100
0101
0102
0103 point_t shift_xy{};
0104 shift_xy[0u] = -bounds[e_shift_x];
0105 shift_xy[1u] = -bounds[e_shift_y];
0106 const scalar_t shift_r = vector::perp(shift_xy);
0107 const scalar_t shift_phi = vector::phi(shift_xy);
0108
0109 return shift_r * shift_r + loc_p[0] * loc_p[0] +
0110 2.f * shift_r * loc_p[0] *
0111 math::cos(get_phi_rel(bounds, loc_p) - shift_phi);
0112 }
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124 template <concepts::scalar scalar_t, concepts::point point_t>
0125 DETRAY_HOST_DEVICE inline scalar_t min_dist_to_boundary(
0126 const bounds_type<scalar_t> &bounds, const point_t &loc_p) const {
0127
0128
0129
0130 const scalar_t phi_rel_focal = get_phi_rel(bounds, loc_p);
0131
0132
0133 const scalar_t min_phi_dist =
0134 math::min(math::fabs(phi_rel_focal - bounds[e_min_phi_rel]),
0135 math::fabs(bounds[e_max_phi_rel] - phi_rel_focal));
0136
0137 const auto r_beam = math::sqrt(get_r2_beam_frame(bounds, loc_p));
0138
0139 const scalar_t min_r_dist = math::min(math::fabs(r_beam - bounds[e_min_r]),
0140 math::fabs(bounds[e_max_r] - r_beam));
0141
0142
0143 return math::min(min_r_dist,
0144 2.f * loc_p[0] * math::sin(0.5f * min_phi_dist));
0145 }
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155 template <concepts::algebra algebra_t>
0156 DETRAY_HOST_DEVICE constexpr result_type<dbool<algebra_t>> check_boundaries(
0157 const bounds_type<dscalar<algebra_t>> &bounds,
0158 const dtransform3D<algebra_t> &trf, const dpoint3D<algebra_t> &glob_p,
0159 const dscalar<algebra_t> tol =
0160 std::numeric_limits<dscalar<algebra_t>>::epsilon(),
0161 const dscalar<algebra_t> edge_tol = 0.f) const {
0162 using scalar_t = dscalar<algebra_t>;
0163
0164
0165 const auto loc_3D{cartesian3D<algebra_t>::global_to_local(trf, glob_p, {})};
0166
0167
0168 const scalar_t new_x{loc_3D[0] + bounds[e_shift_x]};
0169 const scalar_t new_y{loc_3D[1] + bounds[e_shift_y]};
0170
0171 const scalar_t r_beam{math::sqrt(math::fma(new_x, new_x, new_y * new_y))};
0172
0173 auto inside_mask = ((r_beam + tol) >= bounds[e_min_r]) &&
0174 (r_beam <= (bounds[e_max_r] + tol));
0175
0176
0177 auto phi_focal{detail::invalid_value<scalar_t>()};
0178 if (detail::any_of(inside_mask)) {
0179
0180 phi_focal = vector::phi(loc_3D) - bounds[e_average_phi];
0181
0182 const scalar_t phi_tol{detail::phi_tolerance(tol, r_beam)};
0183
0184 inside_mask = (phi_focal >= (bounds[e_min_phi_rel] - phi_tol)) &&
0185 (phi_focal <= (bounds[e_max_phi_rel] + phi_tol)) &&
0186 inside_mask;
0187 }
0188
0189 decltype(inside_mask) inside_edge{false};
0190 if (detail::any_of(edge_tol > 0.f)) {
0191
0192 const scalar_t full_tol{tol + edge_tol};
0193
0194 inside_edge = ((r_beam + full_tol) >= bounds[e_min_r]) &&
0195 (r_beam <= (bounds[e_max_r] + full_tol));
0196
0197 if (detail::any_of(inside_edge)) {
0198
0199 if (detail::is_invalid_value(phi_focal)) {
0200 phi_focal = vector::phi(loc_3D) - bounds[e_average_phi];
0201 }
0202
0203 const scalar_t phi_tol_full{detail::phi_tolerance(full_tol, r_beam)};
0204
0205 inside_edge = (phi_focal >= (bounds[e_min_phi_rel] - phi_tol_full)) &&
0206 (phi_focal <= (bounds[e_max_phi_rel] + phi_tol_full)) &&
0207 inside_edge;
0208 }
0209 }
0210
0211 return result_type<decltype(inside_mask)>{inside_mask, inside_edge};
0212 }
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223 template <concepts::scalar scalar_t, concepts::point point_t>
0224 DETRAY_HOST_DEVICE constexpr auto check_boundaries(
0225 const bounds_type<scalar_t> &bounds, const point_t &loc_p,
0226 const scalar_t tol = std::numeric_limits<scalar_t>::epsilon(),
0227 const scalar_t edge_tol = 0.f) const {
0228
0229
0230
0231 const scalar_t phi_focal = get_phi_rel(bounds, loc_p);
0232
0233
0234 const scalar_t phi_tol = detail::phi_tolerance(tol, loc_p[0]);
0235 auto inside_mask = !((phi_focal < (bounds[e_min_phi_rel] - phi_tol)) ||
0236 (phi_focal > (bounds[e_max_phi_rel] + phi_tol)));
0237
0238
0239 auto r_beam2{detail::invalid_value<scalar_t>()};
0240 if (detail::any_of(inside_mask)) {
0241 r_beam2 = get_r2_beam_frame(bounds, loc_p);
0242
0243
0244
0245 const scalar_t minR_tol =
0246 math::max(bounds[e_min_r] - tol, static_cast<scalar_t>(0.f));
0247 const scalar_t maxR_tol = bounds[e_max_r] + tol;
0248
0249 assert(detail::all_of(minR_tol >= static_cast<scalar_t>(0.f)));
0250
0251 inside_mask = (r_beam2 >= (minR_tol * minR_tol)) &&
0252 (r_beam2 <= (maxR_tol * maxR_tol)) && inside_mask;
0253 }
0254
0255 decltype(inside_mask) inside_edge{false};
0256 if (detail::any_of(edge_tol > 0.f)) {
0257
0258 const scalar_t full_tol{tol + edge_tol};
0259 const scalar_t phi_tol_full = detail::phi_tolerance(full_tol, loc_p[0]);
0260
0261 const auto phi_check_edge =
0262 (phi_focal >= (bounds[e_min_phi_rel] - phi_tol_full)) &&
0263 (phi_focal <= (bounds[e_max_phi_rel] + phi_tol_full));
0264
0265 if (detail::any_of(inside_edge)) {
0266
0267 if (detail::is_invalid_value(r_beam2)) {
0268 r_beam2 = get_r2_beam_frame(bounds, loc_p);
0269 }
0270
0271 const scalar_t minR_tol_edge =
0272 math::max(bounds[e_min_r] - full_tol, static_cast<scalar_t>(0.f));
0273 const scalar_t maxR_tol_edge = bounds[e_max_r] + full_tol;
0274
0275 assert(detail::all_of(minR_tol_edge >= static_cast<scalar_t>(0.f)));
0276
0277 inside_edge = (r_beam2 >= (minR_tol_edge * minR_tol_edge)) &&
0278 (r_beam2 <= (maxR_tol_edge * maxR_tol_edge)) &&
0279 phi_check_edge;
0280 }
0281 }
0282
0283 return result_type<decltype(inside_mask)>{inside_mask, inside_edge};
0284 }
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294 template <concepts::scalar scalar_t>
0295 DETRAY_HOST_DEVICE constexpr scalar_t measure(
0296 const bounds_type<scalar_t> &bounds) const {
0297 return area(bounds);
0298 }
0299
0300
0301
0302
0303
0304
0305
0306
0307 template <concepts::scalar scalar_t>
0308 DETRAY_HOST_DEVICE constexpr scalar_t area(
0309 const bounds_type<scalar_t> & ) const {
0310 return detail::invalid_value<scalar_t>();
0311 }
0312
0313
0314
0315
0316
0317
0318
0319 template <concepts::scalar scalar_t>
0320 DETRAY_HOST_DEVICE constexpr bounds_type<scalar_t> merge(
0321 const bounds_type<scalar_t> &bounds,
0322 const bounds_type<scalar_t> &o_bounds) const {
0323 assert(bounds[e_average_phi] == o_bounds[e_average_phi]);
0324 assert(bounds[e_shift_x] == o_bounds[e_shift_x]);
0325 assert(bounds[e_shift_y] == o_bounds[e_shift_y]);
0326
0327 bounds_type<scalar_t> new_bounds{};
0328
0329 new_bounds[e_min_r] = math::min(bounds[e_min_r], o_bounds[e_min_r]);
0330 new_bounds[e_max_r] = math::max(bounds[e_max_r], o_bounds[e_max_r]);
0331 new_bounds[e_min_phi_rel] =
0332 math::min(bounds[e_min_phi_rel], o_bounds[e_min_phi_rel]);
0333 new_bounds[e_max_phi_rel] =
0334 math::max(bounds[e_max_phi_rel], o_bounds[e_max_phi_rel]);
0335 new_bounds[e_average_phi] = bounds[e_average_phi];
0336 new_bounds[e_shift_x] = bounds[e_shift_x];
0337 new_bounds[e_shift_y] = bounds[e_shift_y];
0338
0339 return new_bounds;
0340 }
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352 template <concepts::algebra algebra_t>
0353 DETRAY_HOST_DEVICE darray<dscalar<algebra_t>, 6> local_min_bounds(
0354 const bounds_type<dscalar<algebra_t>> &bounds,
0355 const dscalar<algebra_t> env =
0356 std::numeric_limits<dscalar<algebra_t>>::epsilon()) const {
0357 using scalar_t = dscalar<algebra_t>;
0358 using point_t = dpoint2D<algebra_t>;
0359
0360 assert(env > 0.f);
0361
0362 const auto c_pos = corners(bounds);
0363
0364 const scalar_t o_x{bounds[e_shift_x]};
0365 const scalar_t o_y{bounds[e_shift_y]};
0366
0367
0368 const point_t b{c_pos[4] * math::cos(c_pos[5]) - o_x,
0369 c_pos[4] * math::sin(c_pos[5]) - o_y};
0370 const point_t c{c_pos[6] * math::cos(c_pos[7]) - o_x,
0371 c_pos[6] * math::sin(c_pos[7]) - o_y};
0372
0373
0374
0375 const point_t t = bounds[e_max_r] * vector::normalize(c + b);
0376
0377
0378 darray<scalar_t, 5> x_pos{c_pos[2] * math::cos(c_pos[3]) - o_x, b[0], c[0],
0379 c_pos[0] * math::cos(c_pos[1]) - o_x, t[0]};
0380 darray<scalar_t, 5> y_pos{c_pos[2] * math::sin(c_pos[3]) - o_y, b[1], c[1],
0381 c_pos[0] * math::sin(c_pos[1]) - o_y, t[1]};
0382
0383 constexpr scalar_t inv{detail::invalid_value<scalar_t>()};
0384 scalar_t min_x{inv};
0385 scalar_t min_y{inv};
0386 scalar_t max_x{-inv};
0387 scalar_t max_y{-inv};
0388 for (unsigned int i{0u}; i < 5u; ++i) {
0389 min_x = x_pos[i] < min_x ? x_pos[i] : min_x;
0390 max_x = x_pos[i] > max_x ? x_pos[i] : max_x;
0391 min_y = y_pos[i] < min_y ? y_pos[i] : min_y;
0392 max_y = y_pos[i] > max_y ? y_pos[i] : max_y;
0393 }
0394
0395 return {min_x - env, min_y - env, -env, max_x + env, max_y + env, env};
0396 }
0397
0398
0399
0400
0401
0402
0403
0404
0405
0406
0407 template <concepts::scalar scalar_t>
0408 DETRAY_HOST_DEVICE darray<scalar_t, 8> corners(
0409 const bounds_type<scalar_t> &bounds) const {
0410
0411
0412
0413
0414 auto get_strips_pc_r = [&bounds](const scalar_t R,
0415 const scalar_t phi) -> scalar_t {
0416
0417 const scalar_t f2{bounds[e_shift_x] * bounds[e_shift_x] +
0418 bounds[e_shift_y] * bounds[e_shift_y]};
0419
0420
0421
0422 const scalar_t f_sin_phi{bounds[e_shift_x] * math::cos(phi) +
0423 bounds[e_shift_y] * math::sin(phi)};
0424
0425 return f_sin_phi + math::sqrt(f_sin_phi * f_sin_phi - f2 + R * R);
0426 };
0427
0428
0429 const scalar_t min_phi{bounds[e_average_phi] + bounds[e_min_phi_rel]};
0430 const scalar_t max_phi{bounds[e_average_phi] + bounds[e_max_phi_rel]};
0431 darray<scalar_t, 8> corner_pos;
0432
0433 corner_pos[0] = get_strips_pc_r(bounds[e_min_r], min_phi);
0434 corner_pos[1] = min_phi;
0435
0436 corner_pos[2] = get_strips_pc_r(bounds[e_min_r], max_phi);
0437 corner_pos[3] = max_phi;
0438
0439 corner_pos[4] = get_strips_pc_r(bounds[e_max_r], max_phi);
0440 corner_pos[5] = max_phi;
0441
0442 corner_pos[6] = get_strips_pc_r(bounds[e_max_r], min_phi);
0443 corner_pos[7] = min_phi;
0444
0445 return corner_pos;
0446 }
0447
0448
0449
0450
0451 template <concepts::algebra algebra_t>
0452 DETRAY_HOST_DEVICE dpoint3D<algebra_t> centroid(
0453 const bounds_type<dscalar<algebra_t>> &bounds) const {
0454 using scalar_t = dscalar<algebra_t>;
0455
0456
0457 const auto crns = corners(bounds);
0458
0459
0460 const scalar_t r{0.25f * (crns[0] + crns[2] + crns[4] + crns[6])};
0461 const scalar_t phi{bounds[e_average_phi]};
0462
0463 return r * dpoint3D<algebra_t>{math::cos(phi), math::sin(phi),
0464 static_cast<scalar_t>(0)};
0465 }
0466
0467
0468
0469
0470
0471
0472
0473 template <concepts::algebra algebra_t>
0474 DETRAY_HOST dvector<dpoint3D<algebra_t>> vertices(
0475 const bounds_type<dscalar<algebra_t>> &bounds, dindex n_seg) const {
0476 using scalar_t = dscalar<algebra_t>;
0477 using point2_t = dpoint2D<algebra_t>;
0478 using point3_t = dpoint3D<algebra_t>;
0479
0480 scalar_t min_r = bounds[e_min_r];
0481 scalar_t max_r = bounds[e_max_r];
0482 scalar_t min_phi = bounds[e_average_phi] + bounds[e_min_phi_rel];
0483 scalar_t max_phi = bounds[e_average_phi] + bounds[e_max_phi_rel];
0484 scalar_t origin_x = bounds[e_shift_x];
0485 scalar_t origin_y = bounds[e_shift_y];
0486
0487 point2_t origin_m = {origin_x, origin_y};
0488
0489
0490 auto circIx = [](scalar_t O_x, scalar_t O_y, scalar_t r,
0491 scalar_t phi) -> point2_t {
0492
0493
0494
0495
0496
0497
0498
0499
0500
0501
0502 scalar_t m = math::tan(phi);
0503 point2_t dir = {math::cos(phi), math::sin(phi)};
0504 scalar_t x1 = (O_x + O_y * m -
0505 math::sqrt(-math::pow(O_x, 2.f) * math::pow(m, 2.f) +
0506 2.f * O_x * O_y * m - math::pow(O_y, 2.f) +
0507 math::pow(m, 2.f) * math::pow(r, 2.f) +
0508 math::pow(r, 2.f))) /
0509 (math::pow(m, 2.f) + 1.f);
0510 scalar_t x2 = (O_x + O_y * m +
0511 math::sqrt(-math::pow(O_x, 2.f) * math::pow(m, 2.f) +
0512 2.f * O_x * O_y * m - math::pow(O_y, 2.f) +
0513 math::pow(m, 2.f) * math::pow(r, 2.f) +
0514 math::pow(r, 2.f))) /
0515 (math::pow(m, 2.f) + 1.f);
0516
0517 if (point2_t v1 = {x1, m * x1}; vector::dot(v1, dir) > 0.f) {
0518 return v1;
0519 }
0520 return {x2, m * x2};
0521 };
0522
0523
0524 point2_t ul_xy = circIx(origin_x, origin_y, max_r, max_phi);
0525 point2_t ll_xy = circIx(origin_x, origin_y, min_r, max_phi);
0526 point2_t ur_xy = circIx(origin_x, origin_y, max_r, min_phi);
0527 point2_t lr_xy = circIx(origin_x, origin_y, min_r, min_phi);
0528
0529 auto inner_phi = detail::phi_values(vector::phi(lr_xy - origin_m),
0530 vector::phi(ll_xy - origin_m), n_seg);
0531 auto outer_phi = detail::phi_values(vector::phi(ul_xy - origin_m),
0532 vector::phi(ur_xy - origin_m), n_seg);
0533
0534 dvector<point3_t> annulus_vertices;
0535 annulus_vertices.reserve(inner_phi.size() + outer_phi.size());
0536 for (scalar_t iphi : inner_phi) {
0537 annulus_vertices.push_back(point3_t{min_r * math::cos(iphi) + origin_x,
0538 min_r * math::sin(iphi) + origin_y,
0539 0.f});
0540 }
0541
0542 for (scalar_t ophi : outer_phi) {
0543 annulus_vertices.push_back(point3_t{max_r * math::cos(ophi) + origin_x,
0544 max_r * math::sin(ophi) + origin_y,
0545 0.f});
0546 }
0547
0548 return annulus_vertices;
0549 }
0550
0551
0552
0553
0554
0555
0556
0557 template <concepts::scalar scalar_t>
0558 DETRAY_HOST constexpr bool check_consistency(
0559 const bounds_type<scalar_t> &bounds, std::ostream &os) const {
0560 constexpr auto tol{10.f * std::numeric_limits<scalar_t>::epsilon()};
0561
0562 if (math::signbit(bounds[e_min_r]) || bounds[e_max_r] < tol) {
0563 os << "DETRAY ERROR (HOST): Radial bounds must be in the range [0, "
0564 "numeric_max)";
0565 return false;
0566 }
0567 if (bounds[e_min_r] >= bounds[e_max_r] ||
0568 math::fabs(bounds[e_min_r] - bounds[e_max_r]) < tol) {
0569 os << "DETRAY ERROR (HOST): Min radius must be smaller than max "
0570 "radius.";
0571 return false;
0572 }
0573 if ((bounds[e_min_phi_rel] < -constant<scalar_t>::pi ||
0574 bounds[e_min_phi_rel] > constant<scalar_t>::pi) ||
0575 (bounds[e_max_phi_rel] < -constant<scalar_t>::pi ||
0576 bounds[e_max_phi_rel] > constant<scalar_t>::pi) ||
0577 (bounds[e_average_phi] < -constant<scalar_t>::pi ||
0578 bounds[e_average_phi] > constant<scalar_t>::pi)) {
0579 os << "DETRAY ERROR (HOST): Angles must map onto [-pi, pi] range.";
0580 return false;
0581 }
0582 if (bounds[e_min_phi_rel] >= bounds[e_max_phi_rel] ||
0583 math::fabs(bounds[e_min_phi_rel] - bounds[e_max_phi_rel]) < tol) {
0584 os << "DETRAY ERROR (HOST): Min relative angle must be smaller "
0585 "than max relative "
0586 "angle.";
0587 return false;
0588 }
0589
0590 return true;
0591 }
0592 };
0593
0594 }