File indexing completed on 2026-05-27 07:24:21
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include "detray/geometry/surface.hpp"
0011
0012 #include "detray/definitions/indexing.hpp"
0013 #include "detray/utils/geometry_utils.hpp" //< cos incidence angle
0014
0015
0016 #include "detray/test/common/build_toy_detector.hpp"
0017 #include "detray/test/common/build_wire_chamber.hpp"
0018 #include "detray/test/framework/types.hpp"
0019
0020
0021 #include <vecmem/memory/host_memory_resource.hpp>
0022
0023
0024 #include <gtest/gtest.h>
0025
0026 namespace {
0027
0028
0029 enum class mask_id : unsigned int {
0030 e_unmasked = 0u,
0031 };
0032
0033 std::ostream& operator<<(std::ostream& os, mask_id ) {
0034 os << "e_unmasked";
0035 return os;
0036 }
0037
0038
0039 enum class material_id : unsigned int {
0040 e_material_slab = 0u,
0041 };
0042
0043 std::ostream& operator<<(std::ostream& os, material_id ) {
0044 os << "e_material_slab";
0045 return os;
0046 }
0047
0048 constexpr detray::test::scalar tol{5e-5f};
0049
0050 detray::dvector3D<detray::test::algebra> test_dir{0.f, 0.f, 1.f};
0051
0052 }
0053
0054
0055 GTEST_TEST(detray_geometry, surface_descriptor) {
0056 using namespace detray;
0057
0058 using mask_link_t = dtyped_index<mask_id, dindex>;
0059 using material_link_t = dtyped_index<material_id, dindex>;
0060
0061 mask_link_t mask_id{mask_id::e_unmasked, 0u};
0062 material_link_t material_id{material_id::e_material_slab, 0u};
0063
0064 surface_descriptor<mask_link_t, material_link_t> desc(
0065 1u, mask_id, material_id, 2u, surface_id::e_sensitive);
0066
0067 static_assert(sizeof(decltype(desc)) == 16);
0068
0069
0070 ASSERT_EQ(desc.transform(), 1u);
0071 ASSERT_EQ(desc.volume(), 2u);
0072 ASSERT_EQ(desc.id(), surface_id::e_sensitive);
0073 ASSERT_FALSE(desc.is_portal());
0074 ASSERT_FALSE(desc.is_passive());
0075 ASSERT_TRUE(desc.is_sensitive());
0076 ASSERT_EQ(desc.mask(), mask_id);
0077 ASSERT_EQ(desc.material(), material_id);
0078
0079
0080 desc.set_volume(5u);
0081 desc.set_id(surface_id::e_portal);
0082 desc.set_index(6u);
0083 desc.update_transform(7u);
0084 desc.update_mask(7u);
0085 desc.update_material(7u);
0086
0087 ASSERT_EQ(desc.transform(), 8u);
0088 ASSERT_EQ(desc.volume(), 5u);
0089 ASSERT_EQ(desc.id(), surface_id::e_portal);
0090 ASSERT_EQ(desc.index(), 6u);
0091 ASSERT_TRUE(desc.is_portal());
0092 ASSERT_FALSE(desc.is_passive());
0093 ASSERT_FALSE(desc.is_sensitive());
0094 ASSERT_EQ(desc.mask().id(), mask_id::e_unmasked);
0095 ASSERT_EQ(desc.mask().index(), 7u);
0096 ASSERT_EQ(desc.material().id(), material_id::e_material_slab);
0097 ASSERT_EQ(desc.material().index(), 7u);
0098 }
0099
0100
0101
0102 GTEST_TEST(detray_geometry, surface_toy_detector) {
0103 using namespace detray;
0104
0105 using detector_t = detector<test::toy_metadata>;
0106
0107 using test_algebra = detector_t::algebra_type;
0108 using scalar = geometry::surface<detector_t>::scalar_type;
0109 using point2 = geometry::surface<detector_t>::point2_type;
0110 using point3 = geometry::surface<detector_t>::point3_type;
0111 using vector3 = geometry::surface<detector_t>::vector3_type;
0112
0113 vecmem::host_memory_resource host_mr;
0114 toy_det_config<test::scalar> toy_cfg{};
0115 toy_cfg.use_material_maps(true).do_check(true);
0116 const auto [toy_det, names] =
0117 build_toy_detector<test_algebra>(host_mr, toy_cfg);
0118
0119 auto ctx = typename detector_t::geometry_context{};
0120
0121
0122
0123
0124 const auto disc_descr = toy_det.surfaces()[1u];
0125 const auto disc = geometry::surface{toy_det, disc_descr};
0126
0127
0128 ASSERT_EQ(disc.identifier(), disc_descr.identifier());
0129 ASSERT_EQ(disc.volume(), 0u);
0130 ASSERT_EQ(disc.index(), 1u);
0131 ASSERT_EQ(disc.id(), surface_id::e_portal);
0132 ASSERT_EQ(disc.shape_id(), detector_t::masks::id::e_ring2D);
0133 ASSERT_EQ(disc.shape_name(), "ring2D");
0134 ASSERT_FALSE(disc.is_sensitive());
0135 ASSERT_FALSE(disc.is_passive());
0136 ASSERT_TRUE(disc.is_portal());
0137
0138
0139 const auto disc_translation =
0140 disc.transform(ctx).translation();
0141 ASSERT_NEAR(disc_translation[0], 0.f, tol);
0142 ASSERT_NEAR(disc_translation[1], 0.f, tol);
0143 ASSERT_NEAR(disc_translation[2], -827.5f, tol);
0144 auto center = disc.center(ctx);
0145 ASSERT_NEAR(center[0], 0.f, tol);
0146 ASSERT_NEAR(center[1], 0.f, tol);
0147 ASSERT_NEAR(center[2], -827.5f, tol);
0148
0149
0150 const auto z_axis = vector3{0.f, 0.f, 1.f};
0151 const vector3 normal_3D = disc.normal(ctx, point3{0.f, 0.f, 0.f});
0152 const vector3 normal_2D = disc.normal(ctx, point2{0.f, 0.f});
0153
0154 ASSERT_NEAR(normal_3D[0], z_axis[0], tol);
0155 ASSERT_NEAR(normal_3D[1], z_axis[1], tol);
0156 ASSERT_NEAR(normal_3D[2], z_axis[2], tol);
0157 ASSERT_NEAR(normal_2D[0], z_axis[0], tol);
0158 ASSERT_NEAR(normal_2D[1], z_axis[1], tol);
0159 ASSERT_NEAR(normal_2D[2], z_axis[2], tol);
0160
0161
0162 vector3 dir = vector::normalize(vector3{1.f, 0.f, 1.f});
0163 ASSERT_NEAR(cos_angle(ctx, disc, dir, point3{0.f, 0.f, 0.f}),
0164 constant<scalar>::inv_sqrt2, tol);
0165 ASSERT_NEAR(cos_angle(ctx, disc, dir, point2{0.f, 0.f}),
0166 constant<scalar>::inv_sqrt2, tol);
0167
0168 dir = vector::normalize(vector3{1.f, 1.f, 0.f});
0169 ASSERT_NEAR(cos_angle(ctx, disc, dir, point3{1.f, 0.5f, 0.f}), 0.f, tol);
0170 ASSERT_NEAR(cos_angle(ctx, disc, dir, point2{1.f, 0.5f}), 0.f, tol);
0171
0172 dir = vector::normalize(vector3{static_cast<scalar>(1.f),
0173 static_cast<scalar>(0.f),
0174 constant<scalar>::pi});
0175 scalar cos_inc_angle{constant<scalar>::pi /
0176 std::sqrt(1.f + std::pow(constant<scalar>::pi, 2.f))};
0177 ASSERT_NEAR(cos_angle(ctx, disc, dir, point3{2.f, 1.f, 0.f}), cos_inc_angle,
0178 tol);
0179 ASSERT_NEAR(cos_angle(ctx, disc, dir, point2{2.f, 1.f}), cos_inc_angle, tol);
0180
0181
0182 point3 glob_pos = {4.f, 7.f, 4.f};
0183 point3 local = disc.global_to_local(ctx, glob_pos, test_dir);
0184 point2 bound = disc.global_to_bound(ctx, glob_pos, test_dir);
0185
0186 ASSERT_NEAR(local[0], std::sqrt(65.f), tol);
0187 ASSERT_NEAR(local[1], std::atan2(7.f, 4.f), tol);
0188 ASSERT_NEAR(bound[0], local[0], tol);
0189 ASSERT_NEAR(bound[1], local[1], tol);
0190
0191
0192 point3 global = disc.local_to_global(ctx, local, test_dir);
0193 point3 global2 = disc.local_to_global(ctx, bound, test_dir);
0194
0195 ASSERT_NEAR(glob_pos[0], global[0], tol);
0196 ASSERT_NEAR(glob_pos[1], global[1], tol);
0197 ASSERT_NEAR(glob_pos[2], global[2], tol);
0198
0199 ASSERT_NEAR(global2[0], glob_pos[0], tol);
0200 ASSERT_NEAR(global2[1], glob_pos[1], tol);
0201
0202 ASSERT_NEAR(global2[2], disc_translation[2], tol);
0203
0204
0205 ASSERT_TRUE(disc.has_material());
0206 const auto* mat_param = disc.material_parameters({0.f, 0.f});
0207 ASSERT_TRUE(mat_param);
0208 ASSERT_EQ(*mat_param, toy_cfg.mapped_material());
0209
0210
0211
0212
0213 const auto rec_descr = toy_det.surfaces()[586u];
0214 const auto rec = geometry::surface{toy_det, rec_descr};
0215
0216
0217 ASSERT_EQ(rec.identifier(), rec_descr.identifier());
0218 ASSERT_EQ(rec.volume(), 9u);
0219 ASSERT_EQ(rec.index(), 586u);
0220 ASSERT_EQ(rec.id(), surface_id::e_sensitive);
0221 ASSERT_EQ(rec.shape_id(), detector_t::masks::id::e_rectangle2D);
0222 ASSERT_TRUE(rec.is_sensitive());
0223 ASSERT_FALSE(rec.is_passive());
0224 ASSERT_FALSE(rec.is_portal());
0225
0226
0227 const auto& rec_translation = rec.transform(ctx).translation();
0228 ASSERT_NEAR(rec_translation[0], -71.902099f, tol);
0229 ASSERT_NEAR(rec_translation[1], -7.081735f, tol);
0230 ASSERT_NEAR(rec_translation[2], -455.f, tol);
0231 center = rec.center(ctx);
0232 ASSERT_NEAR(center[0], -71.902099f, tol);
0233 ASSERT_NEAR(center[1], -7.081735f, tol);
0234 ASSERT_NEAR(center[2], -455.f, tol);
0235
0236
0237
0238 global = rec.transform(ctx).vector_to_global(z_axis);
0239 ASSERT_EQ(rec.normal(ctx, point3{0.f, 0.f, 0.f}), global);
0240 ASSERT_EQ(rec.normal(ctx, point2{0.f, 0.f}), global);
0241
0242
0243 dir = vector::normalize(global);
0244 ASSERT_NEAR(cos_angle(ctx, rec, dir, point3{0.f, 0.f, 0.f}), 1.f, tol);
0245 ASSERT_NEAR(cos_angle(ctx, rec, dir, point2{0.f, 0.f}), 1.f, tol);
0246
0247 dir = vector::normalize(
0248 vector3{static_cast<scalar>(0.f), -global[2], global[1]});
0249 ASSERT_NEAR(cos_angle(ctx, rec, dir, point3{1.f, 0.5f, 0.f}), 0.f, tol);
0250 ASSERT_NEAR(cos_angle(ctx, rec, dir, point2{1.f, 0.5f}), 0.f, tol);
0251
0252 dir = vector::normalize(vector3{static_cast<scalar>(1.f),
0253 static_cast<scalar>(0.f),
0254 constant<scalar>::pi});
0255 cos_inc_angle = std::fabs(vector::dot(dir, global));
0256 ASSERT_NEAR(cos_angle(ctx, rec, dir, point3{2.f, 1.f, 0.f}), cos_inc_angle,
0257 tol);
0258 ASSERT_NEAR(cos_angle(ctx, rec, dir, point2{2.f, 1.f}), cos_inc_angle, tol);
0259
0260
0261 glob_pos = {4.f, 7.f, 4.f};
0262
0263 local = rec.global_to_local(ctx, glob_pos, test_dir);
0264 global = rec.local_to_global(ctx, local, test_dir);
0265 ASSERT_NEAR(glob_pos[0], global[0], tol);
0266 ASSERT_NEAR(glob_pos[1], global[1], tol);
0267 ASSERT_NEAR(glob_pos[2], global[2], tol);
0268
0269 glob_pos = {-71.902099f, -7.081735f, -460.f};
0270
0271 local = rec.global_to_local(ctx, glob_pos, test_dir);
0272 global = rec.local_to_global(ctx, local, test_dir);
0273 ASSERT_NEAR(glob_pos[0], global[0], tol);
0274 ASSERT_NEAR(glob_pos[1], global[1], tol);
0275 ASSERT_NEAR(glob_pos[2], global[2], tol);
0276
0277 bound = rec.global_to_bound(ctx, glob_pos, test_dir);
0278 global = rec.local_to_global(ctx, bound, test_dir);
0279 ASSERT_NEAR(global[0], glob_pos[0], tol);
0280 ASSERT_NEAR(global[1], glob_pos[1], tol);
0281 ASSERT_NEAR(global[2], glob_pos[2], tol);
0282
0283
0284 ASSERT_FALSE(rec.has_material());
0285
0286
0287
0288
0289 const auto cyl_descr = toy_det.surfaces()[3u];
0290 const auto cyl = geometry::surface{toy_det, cyl_descr};
0291
0292
0293 ASSERT_EQ(cyl.identifier(), cyl_descr.identifier());
0294 ASSERT_EQ(cyl.volume(), 0u);
0295 ASSERT_EQ(cyl.index(), 3u);
0296 ASSERT_EQ(cyl.id(), surface_id::e_portal);
0297 ASSERT_EQ(cyl.shape_id(), detector_t::masks::id::e_concentric_cylinder2D);
0298 ASSERT_FALSE(cyl.is_sensitive());
0299 ASSERT_FALSE(cyl.is_passive());
0300 ASSERT_TRUE(cyl.is_portal());
0301
0302
0303 const auto& cyl_translation = cyl.transform(ctx).translation();
0304 ASSERT_NEAR(cyl_translation[0], 0.f, tol);
0305 ASSERT_NEAR(cyl_translation[1], 0.f, tol);
0306 ASSERT_NEAR(cyl_translation[2], 0.f, tol);
0307 center = cyl.center(ctx);
0308 ASSERT_NEAR(center[0], 0.f, tol);
0309 ASSERT_NEAR(center[1], 0.f, tol);
0310 ASSERT_NEAR(center[2], 0.f, tol);
0311
0312
0313
0314 constexpr scalar r{25.f * unit<scalar>::mm};
0315 const vector3 x_axis{1.f, 0.f, 0.f};
0316 ASSERT_NEAR(
0317 vector::norm(cyl.normal(ctx, point3{static_cast<scalar>(0.f),
0318 static_cast<scalar>(0.f), r}) -
0319 x_axis),
0320 0.f, tol);
0321 ASSERT_NEAR(vector::norm(cyl.normal(ctx, point2{0.f, 0.f}) - x_axis),
0322 static_cast<scalar>(0.f), tol);
0323 ASSERT_NEAR(
0324 vector::norm(cyl.normal(ctx, point3{r * constant<scalar>::pi,
0325 static_cast<scalar>(0.f), r}) +
0326 x_axis),
0327 0.f, tol);
0328 ASSERT_NEAR(vector::norm(cyl.normal(ctx, point2{r * constant<scalar>::pi,
0329 static_cast<scalar>(0.f)}) +
0330 x_axis),
0331 0.f, tol);
0332
0333 const vector3 y_axis{0.f, 1.f, 0.f};
0334 ASSERT_NEAR(
0335 vector::norm(cyl.normal(ctx, point3{r * constant<scalar>::pi_2,
0336 static_cast<scalar>(0.f), r}) -
0337 y_axis),
0338 0.f, tol);
0339 ASSERT_NEAR(vector::norm(cyl.normal(ctx, point2{r * constant<scalar>::pi_2,
0340 static_cast<scalar>(0.f)}) -
0341 y_axis),
0342 0.f, tol);
0343
0344
0345 ASSERT_NEAR(
0346 cos_angle(ctx, cyl, x_axis,
0347 point3{r * constant<scalar>::pi, static_cast<scalar>(0.f), r}),
0348 1.f, tol);
0349 ASSERT_NEAR(
0350 cos_angle(ctx, cyl, x_axis,
0351 point2{r * constant<scalar>::pi, static_cast<scalar>(0.f)}),
0352 1.f, tol);
0353
0354 ASSERT_NEAR(cos_angle(ctx, cyl, z_axis,
0355 point3{r * constant<scalar>::pi_2,
0356 static_cast<scalar>(0.f), r}),
0357 0.f, tol);
0358 ASSERT_NEAR(
0359 cos_angle(ctx, cyl, z_axis,
0360 point2{r * constant<scalar>::pi_2, static_cast<scalar>(0.f)}),
0361 0.f, tol);
0362
0363 dir = vector::normalize(vector3{1.f, 1.f, 0.f});
0364 ASSERT_NEAR(
0365 cos_angle(ctx, cyl, dir,
0366 point3{static_cast<scalar>(0.f), static_cast<scalar>(1.f), r}),
0367 constant<scalar>::inv_sqrt2, tol);
0368 ASSERT_NEAR(cos_angle(ctx, cyl, dir, point2{0.f, 1.f}),
0369 constant<scalar>::inv_sqrt2, tol);
0370
0371
0372 glob_pos = {4.f, 7.f, 4.f};
0373
0374 local = cyl.global_to_local(ctx, glob_pos, test_dir);
0375 global = cyl.local_to_global(ctx, local, test_dir);
0376 ASSERT_NEAR(glob_pos[0], global[0], tol);
0377 ASSERT_NEAR(glob_pos[1], global[1], tol);
0378 ASSERT_NEAR(glob_pos[2], global[2], tol);
0379
0380 glob_pos = {constant<scalar>::inv_sqrt2 * r, constant<scalar>::inv_sqrt2 * r,
0381 static_cast<scalar>(2.f)};
0382
0383 local = cyl.global_to_local(ctx, glob_pos, test_dir);
0384 global = cyl.local_to_global(ctx, local, test_dir);
0385 ASSERT_NEAR(glob_pos[0], global[0], tol);
0386 ASSERT_NEAR(glob_pos[1], global[1], tol);
0387 ASSERT_NEAR(glob_pos[2], global[2], tol);
0388
0389 bound = cyl.global_to_bound(ctx, glob_pos, test_dir);
0390 global = cyl.local_to_global(ctx, bound, test_dir);
0391 ASSERT_NEAR(global[0], glob_pos[0], tol);
0392 ASSERT_NEAR(global[1], glob_pos[1], tol);
0393 ASSERT_NEAR(global[2], glob_pos[2], tol);
0394
0395
0396 ASSERT_TRUE(cyl.has_material());
0397 mat_param = cyl.material_parameters({0.f, 0.f});
0398 ASSERT_TRUE(mat_param);
0399 ASSERT_EQ(*mat_param, toy_cfg.mapped_material());
0400 }
0401
0402
0403
0404 GTEST_TEST(detray_geometry, surface_wire_chamber) {
0405 using namespace detray;
0406
0407 using metadata_t = test::wire_chamber_metadata;
0408 using detector_t = detector<metadata_t>;
0409
0410 using test_algebra = metadata_t::algebra_type;
0411 using scalar = geometry::surface<detector_t>::scalar_type;
0412 using point2 = geometry::surface<detector_t>::point2_type;
0413 using point3 = geometry::surface<detector_t>::point3_type;
0414 using vector3 = geometry::surface<detector_t>::vector3_type;
0415
0416 vecmem::host_memory_resource host_mr;
0417 wire_chamber_config<scalar> cfg{};
0418 const auto [wire_chmbr, names] =
0419 build_wire_chamber<test_algebra>(host_mr, cfg);
0420
0421 auto ctx = typename detector_t::geometry_context{};
0422
0423
0424
0425
0426 const auto line_descr = wire_chmbr.surfaces()[23u];
0427 const auto line = geometry::surface{wire_chmbr, line_descr};
0428
0429
0430 ASSERT_EQ(line.identifier(), line_descr.identifier());
0431 ASSERT_EQ(line.volume(), 1u);
0432 ASSERT_EQ(line.index(), 23u);
0433 ASSERT_EQ(line.id(), surface_id::e_sensitive);
0434 ASSERT_EQ(line.shape_id(), detector_t::masks::id::e_drift_cell);
0435 ASSERT_TRUE(line.is_sensitive());
0436 ASSERT_FALSE(line.is_passive());
0437 ASSERT_FALSE(line.is_portal());
0438
0439
0440 const auto line_translation = line.transform(ctx).translation();
0441 ASSERT_NEAR(line_translation[0], 412.858582f, tol);
0442 ASSERT_NEAR(line_translation[1], 299.412414f, tol);
0443 ASSERT_NEAR(line_translation[2], 0.f, tol);
0444 auto center = line.center(ctx);
0445 ASSERT_NEAR(center[0], 412.858582f, tol);
0446 ASSERT_NEAR(center[1], 299.412414f, tol);
0447 ASSERT_NEAR(center[2], 0.f, tol);
0448
0449
0450 const auto z_axis = vector3{0.f, 0.f, 1.f};
0451 point3 global = line.transform(ctx).vector_to_global(z_axis);
0452 const vector3 normal_3D = line.normal(ctx, point3{1.f, 0.f, 0.f});
0453 const vector3 normal_2D = line.normal(ctx, point2{1.f, 0.f});
0454 const vector3 neg_normal_3D = line.normal(ctx, point3{-1.f, 0.f, 0.f});
0455 const vector3 neg_normal_2D = line.normal(ctx, point2{-1.f, 0.f});
0456
0457 ASSERT_NEAR(normal_3D[0], global[0], tol);
0458 ASSERT_NEAR(normal_3D[1], global[1], tol);
0459 ASSERT_NEAR(normal_3D[2], global[2], tol);
0460 ASSERT_NEAR(normal_2D[0], global[0], tol);
0461 ASSERT_NEAR(normal_2D[1], global[1], tol);
0462 ASSERT_NEAR(normal_2D[2], global[2], tol);
0463 ASSERT_NEAR(neg_normal_3D[0], global[0], tol);
0464 ASSERT_NEAR(neg_normal_3D[1], global[1], tol);
0465 ASSERT_NEAR(neg_normal_3D[2], global[2], tol);
0466 ASSERT_NEAR(neg_normal_2D[0], global[0], tol);
0467 ASSERT_NEAR(neg_normal_2D[1], global[1], tol);
0468 ASSERT_NEAR(neg_normal_2D[2], global[2], tol);
0469
0470
0471 vector3 dir = vector::normalize(global);
0472 ASSERT_NEAR(cos_angle(ctx, line, dir, point3{1.f, 0.f, 0.f}), 1.f, tol);
0473 ASSERT_NEAR(cos_angle(ctx, line, dir, point2{1.f, 0.f}), 1.f, tol);
0474
0475 dir = vector::normalize(
0476 vector3{static_cast<scalar>(0.f), -global[2], global[1]});
0477 ASSERT_NEAR(cos_angle(ctx, line, dir, point3{1.f, 100.f, 0.f}), 0.f, tol);
0478 ASSERT_NEAR(cos_angle(ctx, line, dir, point2{1.f, 100.f}), 0.f, tol);
0479
0480 dir = vector3{-0.685475f, -0.0404595f, 0.726971f};
0481 ASSERT_NEAR(cos_angle(ctx, line, dir, point3{2.f, 1.f, 0.f}),
0482 constant<scalar>::inv_sqrt2, 0.0005);
0483 ASSERT_NEAR(cos_angle(ctx, line, dir, point2{2.f, 1.f}),
0484 constant<scalar>::inv_sqrt2, 0.0005);
0485
0486
0487 point3 glob_pos = {4.f, 7.f, 4.f};
0488
0489 point3 local = line.global_to_local(ctx, glob_pos, test_dir);
0490 global = line.local_to_global(ctx, local, dir);
0491
0492
0493 scalar red_tol{0.00015f};
0494 ASSERT_NEAR(glob_pos[0], global[0], red_tol);
0495 ASSERT_NEAR(glob_pos[1], global[1], red_tol);
0496 ASSERT_NEAR(glob_pos[2], global[2], red_tol);
0497
0498 glob_pos = center;
0499
0500 local = line.global_to_local(ctx, glob_pos, test_dir);
0501 global = line.local_to_global(ctx, local, test_dir);
0502 red_tol = 7.f * 1e-5f;
0503 ASSERT_NEAR(glob_pos[0], global[0], red_tol);
0504 ASSERT_NEAR(glob_pos[1], global[1], red_tol);
0505 ASSERT_NEAR(glob_pos[2], global[2], red_tol);
0506
0507 point2 bound = line.global_to_bound(ctx, glob_pos, test_dir);
0508 global = line.local_to_global(ctx, bound, test_dir);
0509 ASSERT_NEAR(global[0], glob_pos[0], red_tol);
0510 ASSERT_NEAR(global[1], glob_pos[1], red_tol);
0511 ASSERT_NEAR(global[2], glob_pos[2], red_tol);
0512
0513
0514 ASSERT_TRUE(line.has_material());
0515 const auto* mat_param = line.material_parameters({0.f, 0.f});
0516 ASSERT_TRUE(mat_param);
0517 ASSERT_EQ(*mat_param, tungsten<scalar>());
0518 }