Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:32:37

0001 // This file is part of the actsvg packge.
0002 //
0003 // Copyright (C) 2022 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
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/proto/surface.hpp"
0019 
0020 namespace actsvg {
0021 
0022 using namespace defaults;
0023 
0024 namespace display {
0025 
0026 /** Draw the surface with a dedicated view
0027  *
0028  * @param id_ the identification of this surface
0029  * @param s_ the surface type
0030  * @param v_ the view type
0031  * @param b_ draw the boolean
0032  * @param fs_ draw as focus
0033  * @param sc_ draw at scale
0034  * @param dt_ draw as template
0035  *
0036  * @note template surfaces ignore the view_type::scene range restriction
0037  */
0038 template <typename surface_type, typename view_type>
0039 svg::object surface(const std::string& id_, const surface_type& s_,
0040                     const view_type& v_, bool _b = true, bool fs_ = false,
0041                     bool sc_ = false, bool dt_ = false) {
0042 
0043     svg::object s;
0044 
0045     // If the surface has a template and is it defined
0046     if (s_._template_object.is_defined()) {
0047 
0048         style::transform draw_transform = s_._transform;
0049         // No rotation nor shift as template
0050         if (dt_) {
0051             draw_transform._tr = {0., 0.};
0052             draw_transform._rot = {0., 0., 0.};
0053         }
0054         // Apply scale or not
0055         if (not sc_) {
0056             draw_transform._scale = {1., 1.};
0057         }
0058 
0059         // Create a surface object from the template
0060         s = draw::from_template(id_, s_._template_object, s_._fill, s_._stroke,
0061                                 draw_transform);
0062         return s;
0063     }
0064 
0065     style::transform draw_transform = fs_ ? style::transform{} : s_._transform;
0066     draw_transform._scale = s_._transform._scale;
0067 
0068     // Surface directly
0069     if (s_._type == surface_type::e_disc) {
0070 
0071         // x-y view for discs
0072         if constexpr (std::is_same_v<view_type, views::x_y>) {
0073 
0074             // view activation / deactivation
0075             if (not utils::inside_range(s_._zparameters[0u],
0076                                         v_._scene._range[1u])) {
0077                 s._active = false;
0078                 return s;
0079             }
0080             // Sector or circle
0081             if (std::abs((s_._opening[1u] - s_._opening[0u]) - 2 * M_PI) >
0082                 5 * std::numeric_limits<scalar>::epsilon()) {
0083                 auto view_vertices = generators::sector_contour(
0084                     s_._radii[0u], s_._radii[1u], s_._opening[0u],
0085                     s_._opening[1u]);
0086                 s = draw::polygon(id_, view_vertices, s_._fill, s_._stroke,
0087                                   draw_transform);
0088             } else {
0089 
0090                 s = draw::circle(id_, {0., 0.}, s_._radii[1u], s_._fill,
0091                                  s_._stroke, draw_transform);
0092 
0093                 // A ring is present
0094                 if (s_._radii[0u]) {
0095 
0096                     std::string mask_id = id_ + "_mask";
0097 
0098                     auto s_c_ = s_;
0099                     s_c_._radii = {0., s_._radii[1u]};
0100 
0101                     svg::object outer_mask =
0102                         surface(id_ + "_mask_surface_outer", s_c_, v_, false);
0103                     outer_mask._fill = style::fill{true};
0104                     outer_mask._stroke = style::stroke{true};
0105                     outer_mask._attribute_map["fill"] = "white";
0106 
0107                     s_c_._radii = {0., s_._radii[0u]};
0108                     svg::object inner_mask =
0109                         surface(id_ + "_mask_surface_inner", s_c_, v_, false);
0110                     inner_mask._fill = style::fill{true};
0111                     inner_mask._stroke = style::stroke{true};
0112                     inner_mask._attribute_map["fill"] = "black";
0113 
0114                     // Create the mask object
0115                     svg::object mask;
0116                     mask._fill = style::fill{true};
0117                     mask._stroke = style::stroke{true};
0118                     mask._id = mask_id;
0119                     mask._tag = "mask";
0120                     mask.add_object(outer_mask);
0121                     mask.add_object(inner_mask);
0122 
0123                     // Modify the surface
0124                     s._definitions.push_back(mask);
0125                     s._attribute_map["mask"] = utils::id_to_url(mask_id);
0126                 }
0127             }
0128         }
0129         // r_z view for discs
0130         if constexpr (std::is_same_v<view_type, views::z_r>) {
0131             scalar zpos = s_._zparameters[0u];
0132             point2 start = {zpos, s_._radii[0u]};
0133             point2 end = {zpos, s_._radii[1u]};
0134             s = draw::line(id_, start, end, s_._stroke, draw_transform);
0135         }
0136 
0137     } else if (s_._type == surface_type::e_cylinder) {
0138 
0139         // xy - view
0140         if constexpr (std::is_same_v<view_type, views::x_y>) {
0141 
0142             // view activation / deactivation
0143             if (not utils::inside_range(s_._zparameters[0u],
0144                                         v_._scene._range[1u])) {
0145                 s._active = false;
0146                 return s;
0147             }
0148 
0149             if (std::abs((s_._opening[1u] - s_._opening[0u]) - 2 * M_PI) >
0150                 5 * std::numeric_limits<scalar>::epsilon()) {
0151                 scalar r = s_._radii[1u];
0152                 point2 start = {r * std::cos(s_._opening[0u]),
0153                                 r * s_._opening[0u]};
0154                 point2 end = {r * std::cos(s_._opening[1u]),
0155                               r * s_._opening[1u]};
0156                 s = draw::arc(id_, r, start, end, style::fill({}), s_._stroke,
0157                               draw_transform);
0158             } else {
0159                 s = draw::circle(id_, {0., 0.}, s_._radii[1u], style::fill({}),
0160                                  s_._stroke, draw_transform);
0161             }
0162         }
0163 
0164         // z-r view
0165         if constexpr (std::is_same_v<view_type, views::z_r>) {
0166             scalar zpos = s_._zparameters[0u];
0167             scalar zhalf = s_._zparameters[1u];
0168             point2 start = {zpos - zhalf, s_._radii[1u]};
0169             point2 end = {zpos + zhalf, s_._radii[1u]};
0170             s = draw::line(id_, start, end, s_._stroke, draw_transform);
0171         }
0172 
0173     } else {
0174 
0175         // View activiation, deactivation
0176         // if only one vertex is within the view range, the surface is shown
0177         bool view_active = true;
0178         if constexpr (std::is_same_v<view_type, views::x_y>) {
0179             view_active = false;
0180             for (auto v : s_._vertices) {
0181                 if (utils::inside_range(v[2], v_._scene._range[1u])) {
0182                     view_active = true;
0183                     break;
0184                 }
0185             }
0186         }
0187         if (not view_active) {
0188             s._active = view_active;
0189             return s;
0190         }
0191 
0192         auto view_vertices = v_(s_._vertices);
0193         s = draw::polygon(id_, view_vertices, s_._fill, s_._stroke,
0194                           draw_transform);
0195     }
0196 
0197     if (_b) {
0198         /// Boolean surfaces only supported for x-y view so far
0199         if constexpr (std::is_same_v<view_type, views::x_y>) {
0200             if (s_._boolean_surface.size() == 1u and
0201                 s_._boolean_operation == surface_type::e_subtraction) {
0202                 std::string mask_id = id_ + "_mask";
0203                 // make a new boolean surface
0204                 svg::object outer_mask =
0205                     surface(id_ + "_mask_surface_outer", s_, v_, false);
0206                 outer_mask._fill = style::fill{true};
0207                 outer_mask._stroke = style::stroke{true};
0208                 outer_mask._attribute_map["fill"] = "white";
0209 
0210                 svg::object inner_mask = surface(id_ + "_mask_surface_inner",
0211                                                  s_._boolean_surface[0], v_);
0212                 inner_mask._fill = style::fill{true};
0213                 inner_mask._stroke = style::stroke{true};
0214                 inner_mask._attribute_map["fill"] = "black";
0215 
0216                 // Create the mask object
0217                 svg::object mask;
0218                 mask._fill = style::fill{true};
0219                 mask._stroke = s_._stroke;
0220                 mask._id = mask_id;
0221                 mask._tag = "mask";
0222                 mask.add_object(outer_mask);
0223                 mask.add_object(inner_mask);
0224 
0225                 // Modify the surface
0226                 s._definitions.push_back(mask);
0227                 s._attribute_map["mask"] = utils::id_to_url(mask_id);
0228             }
0229         }
0230     }
0231 
0232     return s;
0233 }
0234 
0235 /** Draw a portal link
0236  *
0237  * @param id_ the indentification of this portal link
0238  * @param p_ the portal for understanding the span
0239  * @param link_ the link itself
0240  * @param v_ the view type
0241  *
0242  * @return a single object containing the portal view
0243  **/
0244 template <typename portal_type, typename view_type>
0245 svg::object portal_link(const std::string& id_,
0246                         [[maybe_unused]] const portal_type& p_,
0247                         const typename portal_type::link& link_,
0248                         const view_type& v_) {
0249     svg::object l;
0250     l._tag = "g";
0251     l._id = id_;
0252 
0253     scalar d_z = link_._end[2u] - link_._start[2u];
0254     // View activation / deactivation
0255     if constexpr (std::is_same_v<view_type, views::x_y>) {
0256         if (v_._scene._strict and d_z * v_._scene._view[1] < 0) {
0257             l._active = false;
0258             return l;
0259         }
0260     }
0261 
0262     scalar d_x = link_._end[0u] - link_._start[0u];
0263     scalar d_y = link_._end[1u] - link_._start[1u];
0264     scalar d_r = std::sqrt(d_x * d_x + d_y * d_y);
0265     if (std::is_same_v<view_type, views::x_y> and
0266         d_r <= std::numeric_limits<scalar>::epsilon()) {
0267         svg::object arr_xy;
0268         arr_xy._tag = "g";
0269         arr_xy._id = id_ + "_arrow";
0270         arr_xy.add_object(draw::circle(
0271             id_ + "_arrow_top", {link_._start[0u], link_._start[1u]},
0272             link_._end_marker._size, link_._end_marker._fill));
0273         // Camera view onto the surface
0274         if (d_z * v_._scene._view[1] < 0) {
0275             arr_xy.add_object(draw::circle(
0276                 id_ + "_arrow_top_tip", {link_._start[0u], link_._start[1u]},
0277                 link_._end_marker._size * 0.1, __w_fill));
0278         } else {
0279             scalar d_l_x =
0280                 link_._end_marker._size * 0.9 * std::cos(0.25 * M_PI);
0281             scalar d_l_y =
0282                 link_._end_marker._size * 0.9 * std::sin(0.25 * M_PI);
0283             arr_xy.add_object(
0284                 draw::line(id_ + "_arrow_top_cl0",
0285                            {link_._start[0u] - d_l_x, link_._start[1u] - d_l_y},
0286                            {link_._start[0u] + d_l_x, link_._start[1u] + d_l_y},
0287                            __w_stroke));
0288             arr_xy.add_object(
0289                 draw::line(id_ + "_arrow_top_cl1",
0290                            {link_._start[0u] + d_l_x, link_._start[1u] - d_l_y},
0291                            {link_._start[0u] - d_l_x, link_._start[1u] + d_l_y},
0292                            __w_stroke));
0293         }
0294         l.add_object(arr_xy);
0295         // draw plot
0296     } else {
0297         typename portal_type::container_type start_end_3d = {link_._start,
0298                                                              link_._end};
0299         auto start_end = v_(start_end_3d);
0300 
0301         l.add_object(draw::arrow(id_ + "_arrow", start_end[0u], start_end[1u],
0302                                  link_._stroke, link_._start_marker,
0303                                  link_._end_marker));
0304     }
0305     return l;
0306 }
0307 
0308 /** Draw a portal with a dedicated view
0309  *
0310  * @param id_ the identification of this surface
0311  * @param s_ the surface type
0312  * @param v_ the view type
0313  *
0314  * @return a single object containing the portal view;
0315  **/
0316 template <typename portal_type, typename view_type>
0317 svg::object portal(const std::string& id_, const portal_type& p_,
0318                    const view_type& v_) {
0319     svg::object p;
0320     p._tag = "g";
0321     p._id = id_;
0322     p._fill._sterile = true;
0323     p._stroke._sterile = true;
0324 
0325     p.add_object(surface(id_ + "_surface", p_._surface, v_));
0326     for (auto [il, vl] : utils::enumerate(p_._volume_links)) {
0327         p.add_object(portal_link(id_ + "_volume_link_" + std::to_string(il), p_,
0328                                  vl, v_));
0329     }
0330 
0331     return p;
0332 }
0333 
0334 /** Draw a volume
0335  *
0336  * @param id_ the indentification of this portal link
0337  * @param dv_ the detector volume
0338  * @param v_ the view type
0339  * @param p_ draw the portals
0340  *
0341  * @return a single object containing the volume view
0342  **/
0343 template <typename volume_type, typename view_type>
0344 svg::object volume(const std::string& id_, const volume_type& dv_,
0345                    const view_type& v_, bool p_ = true) {
0346     svg::object v;
0347     v._tag = "g";
0348     v._id = id_;
0349     v._fill._sterile = true;
0350     v._stroke._sterile = true;
0351 
0352     // The volume shape
0353     if (not dv_._vertices.empty()) {
0354         auto view_vertices = v_(dv_._vertices);
0355         auto pv = draw::polygon(id_ + "_volume", view_vertices, dv_._fill,
0356                                 dv_._stroke, dv_._transform);
0357         v.add_object(pv);
0358     } else {
0359         if (dv_._type == volume_type::type::e_cylinder and
0360             dv_._bound_values.size() >= 6u) {
0361             scalar ri = dv_._bound_values[0u];
0362             scalar ro = dv_._bound_values[1u];
0363             scalar zp = dv_._bound_values[2u];
0364             scalar zh = dv_._bound_values[3u];
0365             scalar ps = dv_._bound_values[4u];
0366             scalar ap = dv_._bound_values[5u];
0367             if constexpr (std::is_same_v<view_type, views::x_y>) {
0368                 // Make a dummy surface and draw it
0369                 typename volume_type::surface_type s;
0370                 s._name = id_ + "_volume";
0371                 s._radii = {ri, ro};
0372                 s._opening = {ap - ps, ap + ps};
0373                 s._zparameters = {zp, zh};
0374                 s._fill = dv_._fill;
0375                 s._stroke = dv_._stroke;
0376                 v.add_object(surface(s._name, s, v_));
0377             }
0378             if constexpr (std::is_same_v<view_type, views::z_r>) {
0379                 std::vector<point2> view_vertices = {
0380                     {zp - zh, ri}, {zp + zh, ri}, {zp + zh, ro}, {zp - zh, ro}};
0381                 auto pv = draw::polygon(id_ + "_volume", view_vertices,
0382                                         dv_._fill, dv_._stroke, dv_._transform);
0383                 v.add_object(pv);
0384             }
0385         }
0386     }
0387 
0388     // Draw the portals
0389     if (p_) {
0390         for (auto [ip, p] : utils::enumerate(dv_._portals)) {
0391             v.add_object(portal(id_ + "_portal_" + std::to_string(ip), p, v_));
0392         }
0393     }
0394     return v;
0395 }
0396 
0397 /** Draw a detector
0398  *
0399  * @param id_ the indentification of this portal link
0400  * @param d_ the detector
0401  * @param v_ the view type
0402  *
0403  * @return a single object containing the volume view
0404  **/
0405 template <typename detector_type, typename view_type>
0406 svg::object detector(const std::string& id_, const detector_type& d_,
0407                      const view_type& v_) {
0408     svg::object d;
0409     d._tag = "g";
0410     d._id = id_;
0411     d._fill._sterile = true;
0412     d._stroke._sterile = true;
0413 
0414     // Sort the volumes after their depth level, local copy first
0415     auto volumes = d_._volumes;
0416     std::sort(volumes.begin(), volumes.end(),
0417               [](const auto& a_, const auto& b_) {
0418                   return (a_._depth_level < b_._depth_level);
0419               });
0420 
0421     // Draw the volume areas first
0422     for (const auto& v : volumes) {
0423         d.add_object(volume(v._name, v, v_, false));
0424     }
0425 
0426     // Collect all the portals (in a named map) - to avoid double drawing of
0427     // shared ones
0428     std::map<std::string, typename detector_type::volume_type::portal_type>
0429         portals;
0430 
0431     for (const auto& v : volumes) {
0432         for (const auto& p : v._portals) {
0433             portals[p._name] = p;
0434         }
0435     }
0436     // Now draw the portals
0437     for (const auto [n, p] : portals) {
0438         d.add_object(portal(n, p, v_));
0439     }
0440 
0441     return d;
0442 }
0443 
0444 /** Draw eta lines in a zr view
0445  *
0446  * @param id_ the identiier
0447  * @param zr_ the z range of the detector
0448  * @param rr_ the r range of the detector
0449  * @param els_ the stroked eta lines + boolean whether to label
0450  * @param tr_ a potential transform
0451  *
0452  * @return a single object containing the frame
0453  */
0454 static inline svg::object eta_lines(
0455     const std::string& id_, scalar zr_, scalar rr_,
0456     const std::vector<std::tuple<std::vector<scalar>, style::stroke, bool,
0457                                  style::font>>& els_,
0458     const style::transform& tr_ = style::transform()) {
0459 
0460     svg::object e;
0461     e._tag = "g";
0462     e._id = id_;
0463     e._transform = tr_;
0464 
0465     auto theta_from_eta = [](scalar eta) -> scalar {
0466         return static_cast<scalar>(2. * std::atan(std::exp(-eta)));
0467     };
0468 
0469     scalar theta_cut = std::atan2(rr_, zr_);
0470 
0471     for (auto [iet, elt] : utils::enumerate(els_)) {
0472         auto stroke = std::get<style::stroke>(elt);
0473         for (auto [ie, eta] :
0474              utils::enumerate(std::get<std::vector<scalar>>(elt))) {
0475             scalar theta = theta_from_eta(eta);
0476             std::array<scalar, 2> start = {0., 0.};
0477             std::array<scalar, 2> end;
0478             if (theta < theta_cut) {
0479                 end = {zr_, static_cast<scalar>(zr_ * std::tan(theta))};
0480             } else {
0481                 end = {static_cast<scalar>(rr_ * 1 / std::tan(theta)), rr_};
0482             }
0483             // Draw the line
0484             std::string uid = std::to_string(iet) + "_" + std::to_string(ie);
0485             auto e_line =
0486                 draw::line(id_ + "eta_line_" + uid, start, end, stroke);
0487             e.add_object(e_line);
0488             // Label it if told to do so
0489             if (std::get<bool>(elt)) {
0490                 auto font = std::get<style::font>(elt);
0491                 end[0] +=
0492                     static_cast<scalar>(std::cos(theta) * 0.5 * font._size);
0493                 end[1] +=
0494                     static_cast<scalar>(std::sin(theta) * 0.5 * font._size);
0495                 if (eta == 0.) {
0496                     end[0] -= static_cast<scalar>(0.5 * font._size);
0497                 }
0498                 auto e_text = utils::to_string(eta);
0499                 auto e_label =
0500                     draw::text(id_ + "eta_label_" + uid, end, {e_text}, font);
0501                 e.add_object(e_label);
0502             }
0503         }
0504     }
0505     return e;
0506 }
0507 
0508 }  // namespace display
0509 
0510 }  // namespace actsvg