Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:28:07

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