File indexing completed on 2026-06-24 08:00:23
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include <algorithm>
0012 #include <string>
0013 #include <tuple>
0014 #include <utility>
0015 #include <vector>
0016
0017 #include "actsvg/core.hpp"
0018 #include "actsvg/display/tools.hpp"
0019 #include "actsvg/proto/surface.hpp"
0020 #include "actsvg/styles/defaults.hpp"
0021
0022 namespace actsvg {
0023
0024 using namespace defaults;
0025
0026 namespace display {
0027
0028 struct surface_options {
0029
0030
0031 bool _boolean_shape = true;
0032
0033 bool _focus = false;
0034
0035 bool _draw_at_scale = false;
0036
0037 bool _draw_as_template = false;
0038
0039 bool _label_measures = false;
0040 };
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050 template <typename surface_type, typename view_type>
0051 svg::object surface(const std::string& id_, const surface_type& s_,
0052 const view_type& v_,
0053 const surface_options& o_ = surface_options{}) {
0054
0055 svg::object s;
0056
0057
0058 if (s_._template_object.is_defined()) {
0059
0060 style::transform draw_transform = s_._transform;
0061
0062 if (o_._draw_as_template) {
0063 draw_transform._tr = {0., 0.};
0064 draw_transform._rot = {0., 0., 0.};
0065 }
0066
0067 if (!o_._draw_at_scale) {
0068 draw_transform._scale = {1., 1.};
0069 }
0070
0071
0072 s = draw::from_template(id_, s_._template_object, s_._fill, s_._stroke,
0073 draw_transform);
0074 return s;
0075 }
0076
0077 style::transform draw_transform =
0078 o_._focus ? style::transform{} : s_._transform;
0079 draw_transform._scale = s_._transform._scale;
0080
0081
0082 if (s_._type == surface_type::type::e_annulus) {
0083
0084 scalar min_r = s_._measures[0];
0085 scalar max_r = s_._measures[1];
0086 scalar min_phi_rel = s_._measures[2];
0087 scalar max_phi_rel = s_._measures[3];
0088
0089 scalar origin_x = s_._measures[5];
0090 scalar origin_y = s_._measures[6];
0091
0092 auto in_left_s_xy =
0093 annulusCircleIx(origin_x, origin_y, min_r, max_phi_rel);
0094 auto in_right_s_xy =
0095 annulusCircleIx(origin_x, origin_y, min_r, min_phi_rel);
0096 auto out_right_s_xy =
0097 annulusCircleIx(origin_x, origin_y, max_r, min_phi_rel);
0098 auto out_left_s_xy =
0099 annulusCircleIx(origin_x, origin_y, max_r, max_phi_rel);
0100
0101 point2 translation = {0., 0.};
0102 if (s_._surface_transform.has_value()) {
0103 const auto& srot = s_._surface_transform.value()._rotation;
0104 scalar alpha = std::acos(srot[0][0]);
0105 if (srot[1][0] < 0) {
0106 alpha = -alpha;
0107 }
0108 in_left_s_xy = utils::rotate(in_left_s_xy, alpha);
0109 in_right_s_xy = utils::rotate(in_right_s_xy, alpha);
0110 out_right_s_xy = utils::rotate(out_right_s_xy, alpha);
0111 out_left_s_xy = utils::rotate(out_left_s_xy, alpha);
0112
0113 const auto& str = s_._surface_transform.value()._translation;
0114 translation = {static_cast<scalar>(str[0]),
0115 static_cast<scalar>(str[1])};
0116 in_left_s_xy = utils::add<point2, 2u>(in_left_s_xy, translation);
0117 in_right_s_xy = utils::add<point2, 2u>(in_right_s_xy, translation);
0118 out_right_s_xy =
0119 utils::add<point2, 2u>(out_right_s_xy, translation);
0120 out_left_s_xy = utils::add<point2, 2u>(out_left_s_xy, translation);
0121 }
0122
0123
0124 scalar irx = in_right_s_xy[0];
0125 scalar iry = in_right_s_xy[1];
0126 scalar ilx = in_left_s_xy[0];
0127 scalar ily = in_left_s_xy[1];
0128 scalar orx = out_right_s_xy[0];
0129 scalar ory = out_right_s_xy[1];
0130 scalar olx = out_left_s_xy[0];
0131 scalar only = out_left_s_xy[1];
0132
0133 s._tag = "path";
0134 s._id = id_;
0135 std::string path =
0136 "M " + std::to_string(irx) + " " + std::to_string(iry);
0137 path += " A " + std::to_string(min_r) + " " + std::to_string(min_r);
0138 path += " 0 0 1 ";
0139 path += std::to_string(ilx) + " " + std::to_string(ily);
0140 path += " L " + std::to_string(olx) + " " + std::to_string(only);
0141 path += " A " + std::to_string(max_r) + " " + std::to_string(max_r);
0142 path += " 0 0 0 ";
0143 path += std::to_string(orx) + " " + std::to_string(ory);
0144 path += " L " + std::to_string(irx) + " " + std::to_string(iry);
0145 s._attribute_map["d"] = path;
0146 s._fill = s_._fill;
0147 s._stroke = s_._stroke;
0148 s._transform = draw_transform;
0149 if (!o_._focus) {
0150 s._x_range = {-max_r, max_r};
0151 s._y_range = {-max_r, max_r};
0152 }
0153
0154 if (o_._label_measures) {
0155
0156 auto sc = s;
0157 s = svg::object::create_group(id_ + "_labeled_group");
0158 s.add_object(sc);
0159
0160
0161 s.add_object(draw::circle(id_ + "_inner_circle", {0., 0}, min_r,
0162 style::fill{},
0163 defaults::__bl_dotted_stroke));
0164 s.add_object(draw::circle(id_ + "_outer_circle", {0., 0}, max_r,
0165 style::fill{},
0166 defaults::__bl_dotted_stroke));
0167
0168
0169 point2 origin = {-origin_x, -origin_y};
0170 auto el0 = point2{olx, only};
0171 auto el1 = point2{orx, ory};
0172 std::array<point2, 2u> lines = {el0, el1};
0173 for (const auto [il, lline] : utils::enumerate(lines)) {
0174 point2 dline = utils::unit_direction<>(origin, lline);
0175 point2 dneg = utils::scale<point2, 2u>(dline, -0.2 * min_r);
0176 point2 dpos = utils::scale<point2, 2u>(dneg, -1.);
0177
0178 auto line = draw::line(id_ + "_line" + std::to_string(il),
0179 utils::add<point2, 2u>(origin, dneg),
0180 utils::add<point2, 2u>(lline, dpos),
0181 defaults::__bl_dotted_stroke);
0182 line._transform = draw_transform;
0183 s.add_object(line);
0184 }
0185 }
0186 } else if (s_._type == surface_type::type::e_disc) {
0187
0188
0189 if constexpr (std::is_same_v<view_type, views::x_y>) {
0190
0191
0192 if (not utils::inside_range(s_._zparameters[0u],
0193 v_._scene._range[1u])) {
0194 s._active = false;
0195 return s;
0196 }
0197
0198 if (std::abs((s_._opening[1u] - s_._opening[0u]) - 2 * M_PI) >
0199 5 * std::numeric_limits<scalar>::epsilon()) {
0200 auto view_vertices = generators::sector_contour(
0201 s_._radii[0u], s_._radii[1u], s_._opening[0u],
0202 s_._opening[1u]);
0203 s = draw::polygon(id_, view_vertices, s_._fill, s_._stroke,
0204 draw_transform);
0205 } else {
0206
0207 s = draw::circle(id_, {0., 0.}, s_._radii[1u], s_._fill,
0208 s_._stroke, draw_transform);
0209
0210
0211 if (s_._radii[0u] != 0.) {
0212
0213 std::string mask_id = id_ + "_mask";
0214
0215 auto s_c_ = s_;
0216 s_c_._radii = {0., s_._radii[1u]};
0217
0218 svg::object outer_mask =
0219 surface(id_ + "_mask_surface_outer", s_c_, v_, {false});
0220 outer_mask._fill = style::fill{true};
0221 outer_mask._stroke = style::stroke{true};
0222 outer_mask._attribute_map["fill"] = "white";
0223
0224 s_c_._radii = {0., s_._radii[0u]};
0225 svg::object inner_mask =
0226 surface(id_ + "_mask_surface_inner", s_c_, v_, {false});
0227 inner_mask._fill = style::fill{true};
0228 inner_mask._stroke = style::stroke{true};
0229 inner_mask._attribute_map["fill"] = "black";
0230
0231
0232 svg::object mask;
0233 mask._fill = style::fill{true};
0234 mask._stroke = style::stroke{true};
0235 mask._id = mask_id;
0236 mask._tag = "mask";
0237 mask.add_object(outer_mask);
0238 mask.add_object(inner_mask);
0239
0240
0241 s._definitions.push_back(mask);
0242 s._attribute_map["mask"] = utils::id_to_url(mask_id);
0243 }
0244 }
0245 }
0246
0247 if constexpr (std::is_same_v<view_type, views::z_r>) {
0248 scalar zpos = s_._zparameters[0u];
0249 point2 start = {zpos, s_._radii[0u]};
0250 point2 end = {zpos, s_._radii[1u]};
0251 s = draw::line(id_, start, end, s_._stroke, draw_transform);
0252 }
0253
0254 } else if (s_._type == surface_type::type::e_cylinder) {
0255
0256
0257 if constexpr (std::is_same_v<view_type, views::x_y>) {
0258
0259 if (not utils::inside_range(s_._zparameters[0u],
0260 v_._scene._range[1u])) {
0261 s._active = false;
0262 return s;
0263 }
0264
0265 if (std::abs((s_._opening[1u] - s_._opening[0u]) - 2 * M_PI) >
0266 5 * std::numeric_limits<scalar>::epsilon()) {
0267 scalar r = s_._radii[1u];
0268 point2 start = {r * std::cos(s_._opening[0u]),
0269 r * s_._opening[0u]};
0270 point2 end = {r * std::cos(s_._opening[1u]),
0271 r * s_._opening[1u]};
0272 s = draw::arc(id_, r, start, end, style::fill({}), s_._stroke,
0273 draw_transform);
0274 } else {
0275 s = draw::circle(id_, {0., 0.}, s_._radii[1u], __w_fill,
0276 s_._stroke, draw_transform);
0277 }
0278 }
0279
0280
0281 if constexpr (std::is_same_v<view_type, views::z_r>) {
0282 scalar zpos = s_._zparameters[0u];
0283 scalar zhalf = s_._zparameters[1u];
0284 point2 start = {zpos - zhalf, s_._radii[1u]};
0285 point2 end = {zpos + zhalf, s_._radii[1u]};
0286 s = draw::line(id_, start, end, s_._stroke, draw_transform);
0287 }
0288
0289 } else if (s_._type == surface_type::type::e_straw) {
0290
0291 if constexpr (std::is_same_v<view_type, views::x_y>) {
0292
0293 auto ss = draw::circle(id_, {0., 0.}, s_._radii[1u], s_._fill,
0294 s_._stroke, draw_transform);
0295
0296 auto ws = draw::circle(id_, {0., 0.}, s_._radii[0u], __s_fill,
0297 s_._stroke, draw_transform);
0298 s._tag = "g";
0299 s.add_object(ss);
0300 s.add_object(ws);
0301 }
0302
0303 } else if (not s_._vertices.empty()) {
0304
0305
0306
0307 bool view_active = true;
0308 if constexpr (std::is_same_v<view_type, views::x_y>) {
0309 view_active = false;
0310 for (auto v : s_._vertices) {
0311 if (utils::inside_range(v[2], v_._scene._range[1u])) {
0312 view_active = true;
0313 break;
0314 }
0315 }
0316 }
0317 if (not view_active) {
0318 s._active = view_active;
0319 return s;
0320 }
0321
0322
0323
0324 std::vector<typename surface_type::point3_type> vertices = s_._vertices;
0325 if (s_._surface_transform.has_value()) {
0326 const auto& sftr = s_._surface_transform.value();
0327 std::for_each(vertices.begin(), vertices.end(),
0328 [&sftr](auto& v) { v = sftr.point_to_global(v); });
0329 }
0330
0331 auto view_vertices = v_.path(vertices);
0332
0333 if constexpr (std::is_same_v<view_type, views::z_rphi>) {
0334
0335
0336
0337 if (vertices.size() == 4u) {
0338 scalar min_phi = std::numeric_limits<scalar>::max();
0339 scalar max_phi = std::numeric_limits<scalar>::min();
0340
0341 std::vector<typename surface_type::point3_type> n_vertices;
0342 std::vector<typename surface_type::point3_type> p_vertices;
0343
0344 for (const auto& v : s_._vertices) {
0345 scalar phi = std::atan2(v[1u], v[0u]);
0346 min_phi = std::min(min_phi, phi);
0347 max_phi = std::max(max_phi, phi);
0348 if (phi < 0.) {
0349 n_vertices.push_back(v);
0350 } else {
0351 p_vertices.push_back(v);
0352 }
0353 }
0354
0355 if (max_phi - min_phi > 1.5 * M_PI) {
0356
0357
0358
0359
0360
0361 if (n_vertices.size() < p_vertices.size()) {
0362 std::vector<typename surface_type::point3_type>
0363 bottom_vertices = {p_vertices[2u], p_vertices[0u]};
0364 std::vector<typename surface_type::point3_type>
0365 top_vertices = {n_vertices[0u], n_vertices[0u]};
0366
0367 for (size_t index = 0u; index < 2u; index++) {
0368 auto p_bottom = bottom_vertices[index];
0369 auto p_top = top_vertices[index];
0370 float m = (p_bottom[1u] - p_top[1u]) /
0371 (p_bottom[2u] - p_top[2u]);
0372 float q = p_bottom[1u] - m * p_bottom[2u];
0373 float z = -q / m;
0374
0375 typename surface_type::point3_type add = {
0376 p_bottom[0u],
0377 std::numeric_limits<scalar>::epsilon(), z};
0378 p_vertices.push_back(add);
0379 add[1u] = -std::numeric_limits<scalar>::epsilon();
0380 n_vertices.push_back(add);
0381 }
0382
0383 auto p_view_vertices = v_.path(p_vertices);
0384 auto p_middle = p_view_vertices.back();
0385 p_middle[1u] = static_cast<scalar>(
0386 1.5 * p_view_vertices.back()[1u] -
0387 0.5 * p_view_vertices.front()[1u]);
0388 p_middle[0u] = static_cast<scalar>(
0389 0.5 * (p_view_vertices.at(p_view_vertices.size() -
0390 2)[0u] +
0391 p_view_vertices.back()[0u]));
0392 p_view_vertices.insert(p_view_vertices.end() - 1,
0393 p_middle);
0394
0395 auto s_n = draw::polygon(id_ + std::string("_n_split"),
0396 v_.path(n_vertices), s_._fill,
0397 s_._stroke, draw_transform);
0398
0399 auto s_p = draw::polygon(id_ + std::string("_p_split"),
0400 p_view_vertices, s_._fill,
0401 s_._stroke, draw_transform);
0402
0403
0404 s._tag = "g";
0405
0406 s.add_object(s_n);
0407 s.add_object(s_p);
0408 return s;
0409 }
0410
0411
0412 s._tag = "g";
0413
0414
0415 auto n4 = n_vertices[0u];
0416 auto n2 = n_vertices[1u];
0417
0418 n2[1u] = -std::numeric_limits<scalar>::epsilon();
0419 n4[1u] = -std::numeric_limits<scalar>::epsilon();
0420
0421
0422 typename surface_type::point3_type n3 = {
0423 n2[0u],
0424 static_cast<scalar>(0.5 *
0425 (n_vertices[1u][1u] + n2[1u])),
0426 static_cast<scalar>(0.5 * (n2[2u] + n4[2u]))};
0427
0428 n_vertices.push_back(n2);
0429 n_vertices.push_back(n3);
0430 n_vertices.push_back(n4);
0431
0432 auto s_n = draw::polygon(id_ + std::string("_n_split"),
0433 v_.path(n_vertices), s_._fill,
0434 s_._stroke, draw_transform);
0435
0436 auto p4 = p_vertices[0u];
0437 auto p2 = p_vertices[1u];
0438 p2[1u] = std::numeric_limits<scalar>::epsilon();
0439 p4[1u] = std::numeric_limits<scalar>::epsilon();
0440 typename surface_type::point3_type p3 = {
0441 p2[0u], p2[1u],
0442 static_cast<scalar>(0.5 * (p2[2u] + p4[2u]))};
0443 p_vertices.push_back(p2);
0444 p_vertices.push_back(p3);
0445 p_vertices.push_back(p4);
0446
0447 auto p_view_vertices = v_.path(p_vertices);
0448 const auto& p_v_1 = p_view_vertices[1u];
0449 const auto& p_v_2 = p_view_vertices[2u];
0450 auto& p_v_3 = p_view_vertices[3u];
0451 p_v_3[1u] =
0452 static_cast<scalar>(1.5 * p_v_2[1u] - 0.5 * p_v_1[1u]);
0453
0454 auto s_p = draw::polygon(id_ + std::string("_p_split"),
0455 p_view_vertices, s_._fill,
0456 s_._stroke, draw_transform);
0457 s.add_object(s_n);
0458 s.add_object(s_p);
0459 return s;
0460 }
0461 }
0462 }
0463
0464 s = draw::polygon(id_, view_vertices, s_._fill, s_._stroke,
0465 draw_transform);
0466 }
0467
0468 if (o_._boolean_shape) {
0469
0470 if constexpr (std::is_same_v<view_type, views::x_y>) {
0471 if (s_._boolean_surface.size() == 1u and
0472 s_._boolean_operation == surface_type::boolean::e_subtraction) {
0473 std::string mask_id = id_ + "_mask";
0474
0475 svg::object outer_mask =
0476 surface(id_ + "_mask_surface_outer", s_, v_, {false});
0477 outer_mask._fill = style::fill{true};
0478 outer_mask._stroke = style::stroke{true};
0479 outer_mask._attribute_map["fill"] = "white";
0480
0481 svg::object inner_mask = surface(id_ + "_mask_surface_inner",
0482 s_._boolean_surface[0], v_);
0483 inner_mask._fill = style::fill{true};
0484 inner_mask._stroke = style::stroke{true};
0485 inner_mask._attribute_map["fill"] = "black";
0486
0487
0488 svg::object mask;
0489 mask._fill = style::fill{true};
0490 mask._stroke = s_._stroke;
0491 mask._id = mask_id;
0492 mask._tag = "mask";
0493 mask.add_object(outer_mask);
0494 mask.add_object(inner_mask);
0495
0496
0497 s._definitions.push_back(mask);
0498 s._attribute_map["mask"] = utils::id_to_url(mask_id);
0499 }
0500 }
0501 }
0502
0503 return s;
0504 }
0505
0506
0507
0508
0509
0510
0511
0512
0513 template <typename surface_type, typename view_type>
0514 svg::object oriented_polygon(const std::string& id_, const surface_type& s_,
0515 const view_type& v_) {
0516 svg::object s = svg::object::create_group(id_);
0517 s._fill._sterile = true;
0518 s._stroke._sterile = true;
0519
0520 if (s_._vertices.empty()) {
0521 throw std::runtime_error("Surface is no polygon: no vertices defined!");
0522 }
0523
0524
0525 if constexpr (std::is_same_v<view_type, views::x_y>) {
0526
0527 auto path = v_.path(s_._vertices);
0528 point2 center = {0., 0.};
0529 for (const auto& p : path) {
0530 center[0] += p[0];
0531 center[1] += p[1];
0532 }
0533 center[0] /= path.size();
0534 center[1] /= path.size();
0535
0536
0537 scalar angle = std::atan2(center[1], center[0]);
0538
0539
0540 for_each(path.begin(), path.end(), [¢er, &angle](point2& p) {
0541 p[0] -= center[0];
0542 p[1] -= center[1];
0543
0544 utils::rotate(p, -angle);
0545 });
0546
0547 style::transform s_transform{};
0548 s_transform._tr[0] = center[0];
0549 s_transform._tr[1] = center[1];
0550 s_transform._rot[0] = angle;
0551 auto polygon =
0552 draw::polygon(id_, path, s_._fill, s_._stroke, s_transform, false);
0553 return polygon;
0554 }
0555
0556 return s;
0557 }
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568 template <typename portal_type, typename view_type>
0569 svg::object portal_link(const std::string& id_,
0570 [[maybe_unused]] const portal_type& p_,
0571 const typename portal_type::link& link_,
0572 const view_type& v_) {
0573 svg::object l = svg::object::create_group(id_);
0574
0575 scalar d_z = static_cast<scalar>(link_._end[2u] - link_._start[2u]);
0576
0577 if constexpr (std::is_same_v<view_type, views::x_y>) {
0578 if (v_._scene._strict and d_z * v_._scene._view[1] < 0) {
0579 l._active = false;
0580 return l;
0581 }
0582 }
0583
0584 scalar d_x = static_cast<scalar>(link_._end[0u] - link_._start[0u]);
0585 scalar d_y = static_cast<scalar>(link_._end[1u] - link_._start[1u]);
0586 scalar d_r = std::sqrt(d_x * d_x + d_y * d_y);
0587
0588 if (std::is_same_v<view_type, views::x_y> and
0589 d_r <= std::numeric_limits<scalar>::epsilon()) {
0590 svg::object arr_xy = svg::object::create_group(id_ + "_arrow");
0591 arr_xy.add_object(draw::circle(id_ + "_arrow_top",
0592 {static_cast<scalar>(link_._start[0u]),
0593 static_cast<scalar>(link_._start[1u])},
0594 link_._end_marker._size,
0595 link_._end_marker._fill));
0596
0597 if (d_z * v_._scene._view[1] < 0) {
0598 arr_xy.add_object(
0599 draw::circle(id_ + "_arrow_top_tip",
0600 {static_cast<scalar>(link_._start[0u]),
0601 static_cast<scalar>(link_._start[1u])},
0602 link_._end_marker._size * 0.1_scalar, __w_fill));
0603 } else {
0604 scalar d_l_x = link_._end_marker._size * 0.9_scalar *
0605 std::cos(0.25_scalar * pi);
0606 scalar d_l_y = link_._end_marker._size * 0.9_scalar *
0607 std::sin(0.25_scalar * pi);
0608 arr_xy.add_object(
0609 draw::line(id_ + "_arrow_top_cl0",
0610 {static_cast<scalar>(link_._start[0u] - d_l_x),
0611 static_cast<scalar>(link_._start[1u] - d_l_y)},
0612 {static_cast<scalar>(link_._start[0u] + d_l_x),
0613 static_cast<scalar>(link_._start[1u] + d_l_y)},
0614 __w_stroke));
0615 arr_xy.add_object(
0616 draw::line(id_ + "_arrow_top_cl1",
0617 {static_cast<scalar>(link_._start[0u] + d_l_x),
0618 static_cast<scalar>(link_._start[1u] - d_l_y)},
0619 {static_cast<scalar>(link_._start[0u] - d_l_x),
0620 static_cast<scalar>(link_._start[1u] + d_l_y)},
0621 __w_stroke));
0622 }
0623 l.add_object(arr_xy);
0624
0625 } else {
0626 typename portal_type::container_type start_end_3d = {link_._start,
0627 link_._end};
0628 auto start_end = v_.path(start_end_3d);
0629
0630 l.add_object(draw::arrow(id_ + "_arrow", start_end[0u], start_end[1u],
0631 link_._stroke, link_._start_marker,
0632 link_._end_marker));
0633 }
0634 return l;
0635 }
0636
0637
0638
0639
0640
0641
0642
0643
0644
0645 template <typename portal_type, typename view_type>
0646 svg::object portal(const std::string& id_, const portal_type& p_,
0647 const view_type& v_) {
0648 svg::object p = svg::object::create_group(id_);
0649 p._fill._sterile = true;
0650 p._stroke._sterile = true;
0651
0652 p.add_object(surface(id_ + "_surface", p_._surface, v_));
0653 for (auto [il, vl] : utils::enumerate(p_._volume_links)) {
0654 p.add_object(portal_link(id_ + "_volume_link_" + std::to_string(il), p_,
0655 vl, v_));
0656 }
0657
0658 return p;
0659 }
0660
0661
0662
0663
0664
0665
0666
0667
0668
0669
0670
0671 template <typename volume_type, typename view_type>
0672 svg::object volume(const std::string& id_, const volume_type& dv_,
0673 const view_type& v_, bool p_ = true, bool s_ = true) {
0674 svg::object v = svg::object::create_group(id_);
0675 v._fill._sterile = true;
0676 v._stroke._sterile = true;
0677
0678
0679 if (not dv_._vertices.empty() and std::is_same_v<view_type, views::z_r>) {
0680 auto view_vertices = v_.path(dv_._vertices);
0681 auto pv = draw::polygon(id_ + "_volume", view_vertices, dv_._fill,
0682 dv_._stroke, dv_._transform);
0683 v.add_object(pv);
0684 } else {
0685 if (dv_._type == volume_type::type::e_cylinder and
0686 dv_._bound_values.size() >= 6u) {
0687 scalar ri = dv_._bound_values[0u];
0688 scalar ro = dv_._bound_values[1u];
0689 scalar zp = dv_._bound_values[2u];
0690 scalar zh = dv_._bound_values[3u];
0691 scalar ps = dv_._bound_values[4u];
0692 scalar ap = dv_._bound_values[5u];
0693 if constexpr (std::is_same_v<view_type, views::x_y>) {
0694
0695 typename volume_type::surface_type s;
0696 s._name = id_ + "_volume";
0697 s._radii = {ri, ro};
0698 s._opening = {ap - ps, ap + ps};
0699 s._zparameters = {zp, zh};
0700 s._fill = dv_._fill;
0701 s._stroke = dv_._stroke;
0702 s._type = decltype(s)::type::e_disc;
0703 v.add_object(surface(s._name, s, v_));
0704 }
0705 if constexpr (std::is_same_v<view_type, views::z_r>) {
0706 std::vector<point2> view_vertices = {
0707 {zp - zh, ri}, {zp + zh, ri}, {zp + zh, ro}, {zp - zh, ro}};
0708 auto pv = draw::polygon(id_ + "_volume", view_vertices,
0709 dv_._fill, dv_._stroke, dv_._transform);
0710 v.add_object(pv);
0711 }
0712 }
0713 }
0714
0715
0716 if (p_) {
0717 for (auto [ip, p] : utils::enumerate(dv_._portals)) {
0718 v.add_object(portal(id_ + "_portal_" + std::to_string(ip), p, v_));
0719 }
0720 }
0721
0722 if (s_) {
0723 for (auto [is, s] : utils::enumerate(dv_._v_surfaces)) {
0724 v.add_object(display::surface(
0725 id_ + "_surface_" + std::to_string(is), s, v_));
0726 }
0727 }
0728 return v;
0729 }
0730
0731
0732
0733
0734
0735
0736
0737
0738
0739 template <typename detector_type, typename view_type>
0740 svg::object detector(const std::string& id_, const detector_type& d_,
0741 const view_type& v_) {
0742 svg::object d = svg::object::create_group(id_);
0743 d._fill._sterile = true;
0744 d._stroke._sterile = true;
0745
0746
0747 auto volumes = d_._volumes;
0748 std::sort(volumes.begin(), volumes.end(),
0749 [](const auto& a_, const auto& b_) {
0750 return (a_._depth_level < b_._depth_level);
0751 });
0752
0753
0754 for (const auto& v : volumes) {
0755 d.add_object(volume(v._name, v, v_, false));
0756 }
0757
0758
0759
0760 std::map<std::string, typename detector_type::volume_type::portal_type>
0761 portals;
0762
0763 for (const auto& v : volumes) {
0764 for (const auto& p : v._portals) {
0765 portals[p._name] = p;
0766 }
0767 }
0768
0769 for (const auto& [n, p] : portals) {
0770 d.add_object(portal(n, p, v_));
0771 }
0772
0773 return d;
0774 }
0775
0776
0777
0778
0779
0780
0781
0782
0783
0784
0785
0786 svg::object eta_lines(
0787 const std::string& id_, scalar zr_, scalar rr_,
0788 const std::vector<std::tuple<std::vector<scalar>, style::stroke, bool,
0789 style::font>>& els_,
0790 const style::transform& tr_ = style::transform());
0791 }
0792
0793 }