Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-01-04 09:10:53

0001 // This file is part of the actsvg package.
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 <exception>
0013 #include <limits>
0014 #include <string>
0015 #include <vector>
0016 
0017 #include "actsvg/core.hpp"
0018 #include "actsvg/display/geometry.hpp"
0019 #include "actsvg/display/helpers.hpp"
0020 #include "actsvg/display/tools.hpp"
0021 #include "actsvg/proto/cluster.hpp"
0022 #include "actsvg/proto/surface.hpp"
0023 #include "actsvg/proto/volume.hpp"
0024 
0025 namespace actsvg {
0026 
0027 using namespace defaults;
0028 
0029 namespace display {
0030 
0031 enum layer_type { e_endcap = 0, e_barrel = 1 };
0032 
0033 enum sheet_type { e_module_info = 0, e_grid_info = 1 };
0034 
0035 /** Make a surface sheet
0036  *
0037  * @tparam volume3_container is the type of the 3D point container
0038  *
0039  * @param id_ is the identification tag
0040  * @param s_ is the surface to be displayed
0041  * @param sh_ is the sheet size for displaying
0042  * @param fs_ is a directive to focus on the object (instead of axis)
0043  *
0044  * @return a surface sheet svg object
0045  **/
0046 template <typename point3_container>
0047 svg::object surface_sheet_xy(const std::string& id_,
0048                              const proto::surface<point3_container>& s_,
0049                              const std::array<scalar, 2>& sh_ = {400., 400.},
0050                              bool fs_ = false) noexcept(false) {
0051     svg::object so = svg::object::create_group(id_);
0052 
0053     views::x_y x_y_view;
0054 
0055     std::vector<views::contour> contours = {range_contour(s_, fs_)};
0056     auto [x_axis, y_axis] = display::view_range(contours);
0057 
0058     // Stitch when disc - and make x and y axis similarly long
0059     bool full = (s_._type == proto::surface<point3_container>::type::e_disc and
0060                  s_._opening == std::array<scalar, 2>({-pi, pi}));
0061 
0062     bool draw_axes = true;
0063     if (s_._type == proto::surface<point3_container>::type::e_disc or
0064         s_._type == proto::surface<point3_container>::type::e_annulus) {
0065         draw_axes = not fs_;
0066     }
0067 
0068     scalar s_x = sh_[0] / (x_axis[1] - x_axis[0]);
0069     scalar s_y = sh_[1] / (y_axis[1] - y_axis[0]);
0070 
0071     // Harmonize the view window with equal scales
0072     s_x = s_x < s_y ? s_x : s_y;
0073     s_y = s_y < s_x ? s_y : s_x;
0074 
0075     // Create the scale transform
0076     style::transform scale_transform;
0077     scale_transform._scale = {s_x, s_y};
0078 
0079     // Remember the scale this has been done with
0080     so._summary._scale = scale_transform._scale;
0081 
0082     // Copy in order to modify the transform
0083     proto::surface<point3_container> draw_surface = s_;
0084     draw_surface._transform._scale = {s_x, s_y};
0085     // If this is a disc surface, clear the vertices (will be re-generated)
0086     if (draw_surface._type == decltype(draw_surface)::type::e_disc) {
0087         draw_surface._vertices.clear();
0088     }
0089     // The boolean parameters are : we draw the surface with booleans, focusses,
0090     // scaled, and as template
0091     auto surface = display::surface(s_._name + "_in_sheet", draw_surface,
0092                                     x_y_view, {true, true, true, true});
0093     so.add_object(surface);
0094 
0095     // disc/annulus may overwrite axis drawing
0096     if (draw_axes) {
0097         display::prepare_axes(x_axis, y_axis, s_x, s_y, 30., 30.);
0098         auto axis_font = __a_font;
0099         axis_font._size = static_cast<unsigned int>(0.035 * sh_[0]);
0100 
0101         so.add_object(draw::x_y_axes(id_ + "_axes_xy", x_axis, y_axis,
0102                                      __a_stroke, "x", "y", axis_font));
0103     }
0104 
0105     // The measures, markers & stroke with scaling
0106     style::font m_font = __m_font;
0107     m_font._size = static_cast<unsigned int>(0.035 * sh_[0]);
0108     style::marker m_marker = __m_marker;
0109     m_marker._size = static_cast<scalar>(0.015 * sh_[0]);
0110 
0111     // - Trapezoid
0112     if (s_._type == proto::surface<point3_container>::type::e_trapez and
0113         s_._measures.size() == 3u) {
0114 
0115         scalar hlx_min = s_._measures[0] * s_x;
0116         scalar hlx_max = s_._measures[1] * s_x;
0117         scalar hly = s_._measures[2] * s_y;
0118         scalar ms = 2._scalar * m_marker._size;
0119         // Measure names
0120         std::string h_x_min = "h_x_min";
0121         std::string h_x_max = "h_x_max";
0122         std::string h_y = "h_y";
0123 
0124         scalar hly_x = hlx_max + 2 * m_marker._size;
0125 
0126         auto measure_hlx_min = draw::measure(
0127             id_ + "_hlx_min", {0, -hly - ms}, {hlx_min, -hly - ms}, __m_stroke,
0128             m_marker, m_marker, m_font,
0129             h_x_min + " = " + utils::to_string(s_._measures[0]),
0130             {static_cast<scalar>(0.5 * hlx_min),
0131              static_cast<scalar>((-hly - ms - 2.2 * m_font._size))});
0132         auto measure_hlx_max = draw::measure(
0133             id_ + "_hlx_max", {0, hly + ms}, {hlx_max, hly + ms}, __m_stroke,
0134             m_marker, m_marker, m_font,
0135             h_x_max + " = " + utils::to_string(s_._measures[1]),
0136             {static_cast<scalar>(0.5 * hlx_max),
0137              static_cast<scalar>(1.2 * m_font._size + (hly + ms))});
0138         auto measure_hly = draw::measure(
0139             id_ + "_hly", {hly_x, 0}, {hly_x, hly}, __m_stroke, m_marker,
0140             m_marker, m_font, h_y + " = " + utils::to_string(s_._measures[2]),
0141             {static_cast<scalar>(m_font._size + hly_x),
0142              static_cast<scalar>(0.5 * hly)});
0143         so.add_object(measure_hlx_min);
0144         so.add_object(measure_hlx_max);
0145         so.add_object(measure_hly);
0146     } else if (s_._type ==
0147                    proto::surface<point3_container>::type::e_rectangle and
0148                s_._measures.size() == 2u) {
0149 
0150         scalar hlx = s_._measures[0] * s_x;
0151         scalar hly = s_._measures[1] * s_y;
0152         scalar ms = 2._scalar * m_marker._size;
0153         // Measure names
0154         std::string h_x = "h_x";
0155         std::string h_y = "h_y";
0156 
0157         auto measure_hlx = draw::measure(
0158             id_ + "_hlx", {0, hly + ms}, {hlx, hly + ms}, __m_stroke, m_marker,
0159             m_marker, m_font, h_x + " = " + utils::to_string(s_._measures[0]),
0160             {static_cast<scalar>(0.5 * hlx),
0161              static_cast<scalar>((hly + ms + 1.2 * m_font._size))});
0162         auto measure_hly = draw::measure(
0163             id_ + "_hly", {hlx + ms, 0}, {hlx + ms, hly}, __m_stroke, m_marker,
0164             m_marker, m_font, h_y + " = " + utils::to_string(s_._measures[1]),
0165             {static_cast<scalar>(hlx + 1.2 * m_font._size),
0166              static_cast<scalar>(0.5 * hly)});
0167         so.add_object(measure_hlx);
0168         so.add_object(measure_hly);
0169     } else if (s_._type == proto::surface<point3_container>::type::e_diamond and
0170                s_._measures.size() == 5u) {
0171 
0172         scalar hlx_ymin = s_._measures[0] * s_x;
0173         scalar hlx_y0 = s_._measures[1] * s_x;
0174         scalar hlx_ymax = s_._measures[2] * s_x;
0175         scalar hly_xmin = s_._measures[3] * s_y;
0176         scalar hly_xmax = s_._measures[4] * s_y;
0177         scalar ms = 2._scalar * m_marker._size;
0178 
0179         // Measure names
0180         std::string h_x_n = "h_x_ny";
0181         std::string h_x_0 = "h_x_0y";
0182         std::string h_x_p = "h_x_py";
0183         std::string h_y_n = "h_y_nx";
0184         std::string h_y_p = "h_y_px";
0185 
0186         auto measure_hlx_ny = draw::measure(
0187             id_ + "_hlx_ny", {0, -hly_xmin - ms}, {hlx_ymin, -hly_xmin - ms},
0188             __m_stroke, m_marker, m_marker, m_font,
0189             h_x_n + " = " + utils::to_string(s_._measures[0]),
0190             {static_cast<scalar>(0.5 * hlx_ymin),
0191              static_cast<scalar>((-hly_xmin - ms - 1.2 * m_font._size))});
0192 
0193         auto measure_hlx_0y = draw::measure(
0194             id_ + "_hlx_ny", {0., 0.}, {hlx_y0, 0.}, __m_stroke, m_marker,
0195             m_marker, m_font, h_x_0 + " = " + utils::to_string(s_._measures[1]),
0196             {static_cast<scalar>(0.5 * hlx_y0),
0197              static_cast<scalar>((-ms - 1.2 * m_font._size))});
0198 
0199         auto measure_hlx_py = draw::measure(
0200             id_ + "_hlx_py", {0, hly_xmax + ms}, {hlx_ymax, hly_xmax + ms},
0201             __m_stroke, m_marker, m_marker, m_font,
0202             h_x_p + " = " + utils::to_string(s_._measures[2]),
0203             {static_cast<scalar>(0.5 * hlx_ymax),
0204              static_cast<scalar>((hly_xmax + ms + 1.2 * m_font._size))});
0205 
0206         auto measure_hly_nx = draw::measure(
0207             id_ + "_hly_nx", {static_cast<scalar>(-0.5 * hlx_ymin), -hly_xmin},
0208             {static_cast<scalar>(-0.5 * hlx_ymin), 0.}, __m_stroke, m_marker,
0209             m_marker, m_font, h_y_n + " = " + utils::to_string(s_._measures[3]),
0210             {static_cast<scalar>(-0.5 * hlx_ymin + ms),
0211              static_cast<scalar>(-0.5 * hly_xmin)});
0212 
0213         auto measure_hly_px = draw::measure(
0214             id_ + "_hly_px", {static_cast<scalar>(-0.5 * hlx_ymax), 0.},
0215             {static_cast<scalar>(-0.5 * hlx_ymax), hly_xmax}, __m_stroke,
0216             m_marker, m_marker, m_font,
0217             h_y_p + " = " + utils::to_string(s_._measures[4]),
0218             {static_cast<scalar>(-0.5 * hlx_ymax + ms),
0219              static_cast<scalar>(0.5 * hly_xmax)});
0220 
0221         so.add_object(measure_hlx_ny);
0222         so.add_object(measure_hlx_0y);
0223         so.add_object(measure_hlx_py);
0224         so.add_object(measure_hly_nx);
0225         so.add_object(measure_hly_px);
0226 
0227     } else if (s_._type == proto::surface<point3_container>::type::e_polygon and
0228                s_._measures.size() == 2 * s_._vertices.size()) {
0229 
0230         point2 gcenter = {0., 0.};
0231 
0232         for (unsigned int iv = 0; iv < s_._vertices.size(); ++iv) {
0233             auto v = draw::marker(
0234                 id_ + "_vertex_" + std::to_string(iv),
0235                 {static_cast<scalar>(s_x * s_._measures[2 * iv]),
0236                  static_cast<scalar>(s_y * s_._measures[2 * iv + 1u])},
0237                 style::marker({"o"}));
0238 
0239             gcenter[0] += s_x * s_._measures[2 * iv];
0240             gcenter[1] += s_y * s_._measures[2 * iv + 1u];
0241 
0242             so.add_object(v);
0243         }
0244 
0245         gcenter[0] /= s_._vertices.size();
0246         gcenter[1] /= s_._vertices.size();
0247 
0248         for (unsigned int iv = 0; iv < s_._vertices.size(); ++iv) {
0249             scalar x = s_x * s_._measures[2 * iv];
0250             scalar y = s_x * s_._measures[2 * iv + 1];
0251 
0252             scalar dx = x - gcenter[0];
0253             scalar dy = y - gcenter[1];
0254 
0255             scalar dnorm = std::sqrt(dx * dx + dy * dy);
0256             dx /= dnorm;
0257             dy /= dnorm;
0258 
0259             std::string label_v = "v" + std::to_string(iv) + " = ";
0260             label_v += utils::to_string(std::array<scalar, 2>{
0261                 s_._measures[2 * iv], s_._measures[2 * iv + 1]});
0262 
0263             scalar offx = dx > 0
0264                               ? 2._scalar * m_font._size * dx
0265                               : 0.5_scalar * label_v.size() * m_font._size * dx;
0266             scalar offy = 2 * m_font._size * dy;
0267 
0268             so.add_object(draw::text(id_ + "_label_v" + std::to_string(iv),
0269                                      {x + offx, y + offy}, {label_v}));
0270         }
0271 
0272     } else if (s_._type == proto::surface<point3_container>::type::e_disc and
0273                not s_._measures.empty()) {
0274 
0275         std::string dphi = "h_phi";
0276         std::string aphi = "avg_phi";
0277 
0278         // Where to set the labels and how to label them
0279         std::vector<scalar> r_label;
0280         scalar phi_span = 2 * pi;
0281 
0282         if (full) {
0283             r_label = {pi * 0.25, -pi * 0.25};
0284         } else {
0285             phi_span = s_._opening[1] - s_._opening[0];
0286             r_label = {static_cast<scalar>(s_._opening[0] - 0.1),
0287                        static_cast<scalar>(s_._opening[1] + 0.1)};
0288         }
0289         // Start/end parameters of the labels
0290         scalar r_min = std::numeric_limits<scalar>::max();
0291         scalar r_max = 0.;
0292         for (auto [ir, r] : utils::enumerate(s_._measures)) {
0293 
0294             std::array<scalar, 2> xs = {
0295                 static_cast<scalar>(s_x * r * std::cos(r_label[0])),
0296                 static_cast<scalar>(s_x * r * std::cos(r_label[1]))};
0297             std::array<scalar, 2> ys = {
0298                 static_cast<scalar>(s_y * r * std::sin(r_label[0])),
0299                 static_cast<scalar>(s_y * r * std::sin(r_label[1]))};
0300 
0301             // Radial labelling
0302             if (ir < 2 and
0303                 std::abs(r) > std::numeric_limits<scalar>::epsilon()) {
0304                 // Create a measurement helper
0305                 if (not full) {
0306                     auto helper_r = draw::arc(id_ + "_arc_helper", s_x * r,
0307                                               {xs[0], ys[0]}, {xs[1], ys[1]},
0308                                               style::fill(), __m_stroke_guide);
0309                     so.add_object(helper_r);
0310                 }
0311 
0312                 // Record the maximum radius
0313                 r_max = r > r_max ? r : r_max;
0314                 r_min = r < r_min ? r : r_min;
0315                 std::string r_o = ir == 0 ? "r" : "R";
0316 
0317                 scalar rfs =
0318                     (fs_ and not full) ? s_x * 0.85_scalar * r_min : 0._scalar;
0319                 scalar rfs_phi = std::atan2(ys[ir], xs[ir]);
0320 
0321                 point2 rstart = {static_cast<scalar>(rfs * std::cos(rfs_phi)),
0322                                  static_cast<scalar>(rfs * std::sin(rfs_phi))};
0323 
0324                 auto measure_r =
0325                     draw::measure(id_ + "_r", rstart, {xs[ir], ys[ir]},
0326                                   __m_stroke, style::marker(), m_marker, m_font,
0327                                   r_o + " = " + utils::to_string(r),
0328                                   {static_cast<scalar>(m_font._size + xs[ir]),
0329                                    static_cast<scalar>(m_font._size + ys[ir])});
0330                 so.add_object(measure_r);
0331             }
0332             // Phi labelling
0333             if (ir == 2 and not full) {
0334 
0335                 // Focal or not focal
0336                 scalar rfs =
0337                     (fs_ and not full) ? s_x * 0.85_scalar * r_min : 0._scalar;
0338 
0339                 // Place it outside
0340                 scalar lr = s_x * r_max + 2._scalar * m_marker._size;
0341 
0342                 point2 start = {
0343                     static_cast<scalar>(lr * std::cos(s_._opening[0])),
0344                     static_cast<scalar>(lr * std::sin(s_._opening[0]))};
0345 
0346                 // Medium phi arc and line
0347                 scalar mphi = 0.5_scalar * (s_._opening[0] + s_._opening[1]);
0348                 point2 end = {static_cast<scalar>(lr * std::cos(mphi)),
0349                               static_cast<scalar>(lr * std::sin(mphi))};
0350 
0351                 scalar mmphi = 0.5_scalar * (s_._opening[0] + mphi);
0352                 point2 mend = {
0353                     static_cast<scalar>(m_font._size + lr * std::cos(mmphi)),
0354                     static_cast<scalar>(m_font._size + lr * std::sin(mmphi))};
0355 
0356                 auto maesure_arc = draw::arc_measure(
0357                     id_ + "_arc", lr, start, end, __m_stroke, m_marker,
0358                     m_marker, m_font, dphi + " = " + utils::to_string(phi_span),
0359                     mend);
0360 
0361                 so.add_object(maesure_arc);
0362 
0363                 point2 mstart = {static_cast<scalar>(rfs * std::cos(mphi)),
0364                                  static_cast<scalar>(rfs * std::sin(mphi))};
0365 
0366                 auto medium_phi_line = draw::line(id_ + "medium_phi", mstart,
0367                                                   end, __m_stroke_guide);
0368                 so.add_object(medium_phi_line);
0369 
0370                 // Measure to the medium phi
0371                 scalar r_avg_phi = 0.2_scalar * s_x * r_max;
0372                 std::array<scalar, 2> r_avg_start = {r_avg_phi, 0.};
0373                 std::array<scalar, 2> r_avg_end = {
0374                     static_cast<scalar>(r_avg_phi * std::cos(mphi)),
0375                     static_cast<scalar>(r_avg_phi * std::sin(mphi))};
0376 
0377                 std::array<scalar, 2> r_avg_mend = {
0378                     static_cast<scalar>(m_font._size +
0379                                         r_avg_phi * std::cos(0.5 * mphi)),
0380                     static_cast<scalar>(m_font._size +
0381                                         r_avg_phi * std::sin(0.5 * mphi))};
0382 
0383                 if (std::abs(mphi) >
0384                     5 * std::numeric_limits<scalar>::epsilon()) {
0385                     auto measure_avg_phi = draw::arc_measure(
0386                         id_ + "_avg_phi", r_avg_phi, r_avg_start, r_avg_end,
0387                         __m_stroke, style::marker(), m_marker, m_font,
0388                         aphi + " = " + utils::to_string(mphi), r_avg_mend);
0389                     so.add_object(measure_avg_phi);
0390                 }
0391             }
0392         }
0393     } else if (s_._type == proto::surface<point3_container>::type::e_annulus and
0394                not s_._measures.empty()) {
0395 
0396         if (s_._measures.size() != 7u) {
0397             throw std::invalid_argument(
0398                 "surface_sheet_xy(...) - incorrect length of <measures> for "
0399                 "annulus shape.");
0400         }
0401 
0402         // Special annulus bounds code
0403         scalar min_r = s_x * s_._measures[0];
0404         scalar max_r = s_x * s_._measures[1];
0405         scalar min_phi_rel = s_._measures[2];
0406         scalar max_phi_rel = s_._measures[3];
0407         // scalar average_phi = s_._measures[4];
0408         scalar origin_x = s_x * s_._measures[5];
0409         scalar origin_y = s_x * s_._measures[6];
0410 
0411         point2 cart_origin = {origin_x, origin_y};
0412 
0413         auto out_left_s_xy =
0414             annulusCircleIx(origin_x, origin_y, max_r, max_phi_rel);
0415         auto in_left_s_xy =
0416             annulusCircleIx(origin_x, origin_y, min_r, max_phi_rel);
0417         auto out_right_s_xy =
0418             annulusCircleIx(origin_x, origin_y, max_r, min_phi_rel);
0419         auto in_right_s_xy =
0420             annulusCircleIx(origin_x, origin_y, min_r, min_phi_rel);
0421 
0422         std::vector<point2> corners = {in_right_s_xy, in_left_s_xy,
0423                                        out_right_s_xy, out_left_s_xy};
0424 
0425         so.add_object(draw::line(id_ + "_phi_line_l", {0, 0}, in_left_s_xy,
0426                                  __m_stroke_guide));
0427 
0428         so.add_object(draw::line(id_ + "_phi_line_r", {0, 0}, in_right_s_xy,
0429                                  __m_stroke_guide));
0430 
0431         std::vector<std::string> phi_labels = {"phi_min_rel", "phi_max_rel"};
0432         // Measure the phi values
0433         for (unsigned int ic = 0; ic < 2; ++ic) {
0434 
0435             const auto& corner = corners[ic];
0436             scalar in_phi = static_cast<scalar>(atan2(corner[1], corner[0]));
0437 
0438             scalar in_r =
0439                 (0.2_scalar + ic * 0.2_scalar) * utils::perp(corners[ic]);
0440             std::array<scalar, 2> in_start = {in_r, 0.};
0441             std::array<scalar, 2> in_end = {
0442                 static_cast<scalar>(in_r * std::cos(in_phi)),
0443                 static_cast<scalar>(in_r * std::sin(in_phi))};
0444 
0445             std::array<scalar, 2> in_mend = {
0446                 static_cast<scalar>(m_font._size +
0447                                     in_r * std::cos(0.5 * in_phi)),
0448                 static_cast<scalar>(m_font._size +
0449                                     in_r * std::sin(0.5 * in_phi))};
0450 
0451             auto in_phi_arc = draw::arc_measure(
0452                 id_ + "_in_phi_" + std::to_string(ic), in_r, in_start, in_end,
0453                 __m_stroke, style::marker(), m_marker, m_font,
0454                 phi_labels[ic] + " = " + utils::to_string(in_phi), in_mend);
0455             so.add_object(in_phi_arc);
0456         }
0457 
0458         style::marker cart_origin_marker = style::marker({"o"});
0459         cart_origin_marker._fill = style::fill(style::color{{255, 0, 0}});
0460 
0461         style::font cart_font = m_font;
0462         cart_font._fc = style::color{{255, 0, 0}};
0463 
0464         style::stroke cart_stroke = __m_stroke;
0465         cart_stroke._sc = style::color{{255, 0, 0}};
0466 
0467         style::marker cart_marker = m_marker;
0468         cart_marker._fill = style::fill(style::color{{255, 0, 0}});
0469         cart_marker._stroke = cart_stroke;
0470 
0471         // The origin of the cartesian system
0472         so.add_object(draw::marker(id_ + "_origin_cart", {origin_x, origin_y},
0473                                    cart_origin_marker));
0474 
0475         so.add_object(draw::text(
0476             id_ + "_origin_label",
0477             {origin_x - 2 * cart_font._size, origin_y - 2 * cart_font._size},
0478             {"cart_origin = " + utils::to_string(std::array<scalar, 2>{
0479                                     s_._measures[5], s_._measures[6]})},
0480             cart_font));
0481 
0482         style::stroke cart_guide = __m_stroke_guide;
0483         cart_guide._sc = style::color{{255, 0, 0}};
0484 
0485         std::vector<std::string> rs = {"r", "R"};
0486         // Draw the circles
0487         for (unsigned int idc = 0; idc < 2; ++idc) {
0488 
0489             const auto& c0 = corners[2 * idc];
0490             const auto& c1 = corners[2 * idc + 1];
0491 
0492             point2 cart_c0 = point2{c0[0] - origin_x, c0[1] - origin_y};
0493             point2 cart_c1 = point2{c1[0] - origin_x, c1[1] - origin_y};
0494             scalar cart_r = utils::perp(cart_c0);
0495 
0496             scalar cart_phi0 =
0497                 static_cast<scalar>(atan2(cart_c0[1], cart_c0[0])) - 0.2_scalar;
0498             scalar cart_phi1 =
0499                 static_cast<scalar>(atan2(cart_c1[1], cart_c1[0])) +
0500                 0.25_scalar;
0501             scalar cart_phir =
0502                 cart_phi1 - (2_scalar * idc + 1_scalar) * 0.05_scalar;
0503 
0504             point2 start_arc = {
0505                 static_cast<scalar>(origin_x + cart_r * std::cos(cart_phi0)),
0506                 static_cast<scalar>(origin_y + cart_r * std::sin(cart_phi0))};
0507             point2 end_arc = {
0508                 static_cast<scalar>(origin_x + cart_r * std::cos(cart_phi1)),
0509                 static_cast<scalar>(origin_y + cart_r * std::sin(cart_phi1))};
0510 
0511             point2 end_r = {
0512                 static_cast<scalar>(origin_x + cart_r * std::cos(cart_phir)),
0513                 static_cast<scalar>(origin_y + cart_r * std::sin(cart_phir))};
0514 
0515             auto helper_r =
0516                 draw::arc(id_ + "_arc_helper_" + std::to_string(idc), cart_r,
0517                           start_arc, end_arc, style::fill(), cart_guide);
0518             so.add_object(helper_r);
0519 
0520             auto measure_r = draw::measure(
0521                 id_ + "_measure_r_" + std::to_string(idc), cart_origin, end_r,
0522                 cart_stroke, style::marker(), cart_marker, cart_font,
0523                 rs[idc] + " = " +
0524                     utils::to_string(static_cast<scalar>(cart_r / s_x)),
0525                 {static_cast<scalar>(m_font._size + end_r[0]),
0526                  static_cast<scalar>(m_font._size + end_r[1])});
0527             so.add_object(measure_r);
0528         }
0529     }
0530     return so;
0531 }
0532 
0533 /** Make a summary sheet for an endcap type volume
0534  *
0535  * @tparam volume3_container is the type of the 3D point container
0536  *
0537  * @param id_ is the identification tag of this sheet
0538  * @param v_ is the volume to be displayed
0539  * @param sh_ is the sheet size for displaying
0540  * @param t_ is the sheet type
0541  * @param s_sh_ is the surface sheet sub display size
0542  *
0543  **/
0544 template <typename point3_container, typename view_type, layer_type lT>
0545 svg::object sheet(const std::string& id_,
0546                   const proto::volume<point3_container>& v_,
0547                   const std::array<scalar, 2>& sh_ = {600., 600.},
0548                   sheet_type t_ = e_module_info,
0549                   const std::array<scalar, 2>& s_sh_ = {600., 600.}) {
0550 
0551     svg::object o_sheet = svg::object::create_group(id_);
0552 
0553     view_type view;
0554 
0555     scalar sub_offset = 0.25;
0556 
0557     // The modules are drawn and axes set
0558     auto [modules, scale_transform, axes] = process_modules(
0559         v_, view, sh_, lT == e_endcap, t_ == e_module_info, lT == e_endcap);
0560     auto x_axis = axes[0];
0561     auto y_axis = axes[1];
0562 
0563     // Remember the scale with which this has been done
0564     o_sheet._summary._scale = scale_transform._scale;
0565 
0566     // Set the info according to the sheet type
0567     for (auto [ib, surface_batch] : utils::enumerate(v_._surfaces)) {
0568         for (auto [is, s] : utils::enumerate(surface_batch)) {
0569             auto& m = modules[ib][is];
0570             if (t_ == e_module_info and
0571                 s._aux_info.find("module_info") != s._aux_info.end()) {
0572                 m._aux_info = s._aux_info.find("module_info")->second;
0573             } else if (t_ == e_grid_info and
0574                        s._aux_info.find("grid_info") != s._aux_info.end()) {
0575                 m._aux_info = s._aux_info.find("grid_info")->second;
0576             }
0577         }
0578     }
0579 
0580     // Eventual extra objects to be added
0581     std::vector<svg::object> extra_objects;
0582     // Surface sheets to be added (after the main batches)
0583     std::vector<std::vector<svg::object>> all_s_sheets;
0584 
0585     // Draw the template module surfaces
0586     if (t_ == e_module_info) {
0587         // The sheets per module
0588         for (auto [ib, surface_batch] : utils::enumerate(v_._surfaces)) {
0589             std::vector<svg::object> s_sheets;
0590             for (auto s : surface_batch) {
0591                 // Use a local copy of the surface to modify color
0592                 style::fill s_fill;
0593                 s_fill._fc._rgb = s._fill._fc._hl_rgb;
0594                 s_fill._fc._opacity = s._fill._fc._opacity;
0595                 s._fill = s_fill;
0596                 // The template sheet
0597                 svg::object s_sheet =
0598                     svg::object::create_group(s._name + "_surface_sheet");
0599                 // Background panel
0600                 auto bg_panel =
0601                     draw::rectangle(s._name + "_surface_sheet_bg", {0, 0},
0602                                     static_cast<scalar>(0.7 * s_sh_[0]),
0603                                     static_cast<scalar>(0.7 * s_sh_[1]),
0604                                     __bg_fill, __bg_stroke);
0605                 s_sheet.add_object(bg_panel);
0606                 s_sheet.add_object(display::surface_sheet_xy(
0607                     s._name + "_surface_sheet_display", s, s_sh_,
0608                     lT == e_endcap));
0609                 // Shift to the right
0610                 style::transform(
0611                     {{static_cast<scalar>((ib + 1) *
0612                                           (sh_[0] + sub_offset * s_sh_[0])),
0613                       0., 0.}})
0614                     .attach_attributes(s_sheet);
0615                 s_sheets.push_back(s_sheet);
0616             }
0617             all_s_sheets.push_back(s_sheets);
0618         }
0619     } else if (t_ == e_grid_info and not v_._surface_grid._edges_0.empty() and
0620                not v_._surface_grid._edges_1.empty()) {
0621 
0622         svg::object sub_sheet;
0623         // Draw the grid with the appropriate scale transform
0624         if (lT == e_endcap) {
0625             extra_objects =
0626                 draw::tiled_polar_grid(id_, v_._surface_grid._edges_0,
0627                                        v_._surface_grid._edges_1, __g_fill,
0628                                        __g_stroke, scale_transform)
0629                     ._sub_objects;
0630         } else if (lT == e_barrel) {
0631             extra_objects =
0632                 draw::tiled_cartesian_grid(id_, v_._surface_grid._edges_0,
0633                                            v_._surface_grid._edges_1, __g_fill,
0634                                            __g_stroke, scale_transform)
0635                     ._sub_objects;
0636         }
0637 
0638         // The associations need to be made before the modules are added
0639         for (auto [ib, module_batch] : utils::enumerate(modules)) {
0640             // Connect grid and surfaces
0641             style::font info_font;
0642             info_font._size = static_cast<unsigned int>(0.03 * s_sh_[0]);
0643             auto c_objects = connectors::connect_action(
0644                 extra_objects, module_batch, v_._grid_associations[ib],
0645                 ib == 0u, {"mouseover", "mouseout"},
0646                 {connectors::e_highlight, connectors::e_associate_info},
0647                 info_font);
0648             scalar offsx =
0649                 static_cast<scalar>((ib * (1 + sub_offset) + 0.7) * sh_[0]);
0650             scalar offsy = std::abs(0.1_scalar * y_axis[1]);
0651             // Let's set them to the right side outside the view
0652             std::for_each(
0653                 c_objects.begin(), c_objects.end(), [&](svg::object& o_) {
0654                     o_._attribute_map["x"] = utils::to_string(offsx);
0655                     o_._attribute_map["y"] = utils::to_string(offsy);
0656                     o_._x_range = {static_cast<scalar>(offsx - 100.), offsy};
0657                 });
0658             o_sheet.add_objects(c_objects);
0659         }
0660     }
0661 
0662     // Add the modules & eventual extra objects
0663     for (auto [ib, module_batch] : utils::enumerate(modules)) {
0664 
0665         svg::object sub_sheet =
0666             svg::object::create_group(id_ + "_sub_sheet" + std::to_string(ib));
0667         sub_sheet.add_objects(module_batch);
0668         // Add the axes on top @todo add autmated font size adaption
0669         auto axis_font = __a_font;
0670         axis_font._size = static_cast<unsigned int>(0.035 * s_sh_[0]);
0671         sub_sheet.add_object(draw::x_y_axes(id_ + "_xy", x_axis, y_axis,
0672                                             __a_stroke, view._axis_names[0],
0673                                             view._axis_names[1], axis_font));
0674         // Shift to the right
0675         style::transform(
0676             {{static_cast<scalar>(ib * (sh_[0] + sub_offset * s_sh_[0])), 0.,
0677               0.}})
0678             .attach_attributes(sub_sheet);
0679 
0680         o_sheet.add_object(sub_sheet);
0681     }
0682 
0683     // Now eventually add & connect the surface sheets
0684     if (not all_s_sheets.empty()) {
0685         // Connect the surface sheets
0686         connect_surface_sheets(v_, all_s_sheets, o_sheet);
0687     }
0688 
0689     // Now eventually add the extra objects
0690     if (not extra_objects.empty()) {
0691         o_sheet.add_objects(extra_objects);
0692     }
0693 
0694     //  Add the title text
0695     auto title_font = __t_font;
0696     title_font._size = static_cast<unsigned int>(0.05 * sh_[0]);
0697     auto title = draw::text(id_ + "sheet_title",
0698                             {static_cast<scalar>(-0.55 * sh_[0]),
0699                              static_cast<scalar>(0.6 * sh_[1])},
0700                             {v_._name}, title_font);
0701     o_sheet.add_object(title);
0702 
0703     return o_sheet;
0704 }
0705 
0706 /** Make a summary sheet for an endcap type volume
0707  *
0708  * @tparam volume3_container is the type of the 3D point container
0709  *
0710  * @param id_ is the idenfication tag of this sheet
0711  * @param v_ is the volume to be displayed
0712  * @param sh_ is the sheet size for displaying
0713  * @param t_ is the sheet type
0714  * @param s_sh_ is the surface sheet sub display size
0715  **/
0716 template <typename point3_container>
0717 svg::object endcap_sheet(const std::string& id_,
0718                          const proto::volume<point3_container>& v_,
0719                          const std::array<scalar, 2>& sh_ = {600., 600.},
0720                          sheet_type t_ = e_module_info,
0721                          const std::array<scalar, 2>& s_sh_ = {600., 600.}) {
0722 
0723     return sheet<point3_container, views::x_y, e_endcap>(id_, v_, sh_, t_,
0724                                                          s_sh_);
0725 }
0726 
0727 /** Make a summary sheet for an barrel type volume
0728  *
0729  * @tparam volume3_container is the type of the 3D point container
0730  *
0731  * @param id_ is the idenfication tag of this sheet
0732  * @param v_ is the volume to be displayed
0733  * @param sh_ is the sheet size for displaying
0734  * @param t_ is the sheet type
0735  * @param s_sh_ is the surface sheet sub display size
0736  **/
0737 template <typename point3_container>
0738 svg::object barrel_sheet(const std::string& id_,
0739                          const proto::volume<point3_container>& v_,
0740                          const std::array<scalar, 2>& sh_ = {600., 600.},
0741                          sheet_type t_ = e_module_info,
0742                          const std::array<scalar, 2>& s_sh_ = {600., 600.}) {
0743 
0744     return sheet<point3_container, views::z_phi, e_barrel>(id_, v_, sh_, t_,
0745                                                            s_sh_);
0746 }
0747 
0748 }  // namespace display
0749 }  // namespace actsvg