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 <utility>
0014 #include <vector>
0015 
0016 #include "actsvg/core.hpp"
0017 #include "actsvg/proto/cluster.hpp"
0018 
0019 namespace actsvg {
0020 
0021 using namespace defaults;
0022 
0023 namespace display {
0024 
0025 /** Make a cluster  in 2D
0026  *
0027  * @param grid_ is the grid in which this cluster sits
0028  * @param id_ is the identification tag
0029  * @param cluster_ is the cluster to be drawn
0030  * @param fill_low_ is the low color band
0031  * @param fill_high is hte high color band
0032  * @param fill_m_ is the measurement fill color
0033  * @param expand_ is the focus expansion
0034  *
0035  * @return a cluster display and the focussed grid area
0036  *
0037  **/
0038 template <size_t DIM>
0039 static inline std::pair<svg::object, svg::object> cluster(
0040     const svg::object& grid_, const std::string& id_,
0041     const proto::cluster<DIM>& cluster_,
0042     const style::fill& fill_low_ = style::fill{style::color{{255, 255, 0}}},
0043     const style::fill& fill_high_ = style::fill{style::color{{250, 0, 0}}},
0044     const style::fill& fill_m_ = style::fill{style::color{{0, 0, 255}}},
0045     const std::array<unsigned int, 2>& expand_ = {2, 2}) {
0046 
0047     svg::object cluster_group;
0048     cluster_group._tag = "g";
0049     cluster_group._id = id_;
0050 
0051     // Grid indices
0052     unsigned int i_min = std::numeric_limits<unsigned int>::max();
0053     unsigned int i_max = std::numeric_limits<unsigned int>::min();
0054     unsigned int j_min = std::numeric_limits<unsigned int>::max();
0055     unsigned int j_max = std::numeric_limits<unsigned int>::min();
0056 
0057     // Cluster low/high data
0058     scalar low_data = std::numeric_limits<scalar>::max();
0059     scalar high_data = std::numeric_limits<scalar>::lowest();
0060 
0061     for (const auto& c : cluster_._channels) {
0062         low_data = std::min(low_data, c._data);
0063         high_data = std::max(high_data, c._data);
0064     }
0065     // Cluster data scaling
0066     scalar delta_data = high_data - low_data;
0067 
0068     int low_r = fill_low_._fc._rgb[0];
0069     int low_g = fill_low_._fc._rgb[1];
0070     int low_b = fill_low_._fc._rgb[2];
0071     int delta_r = (fill_high_._fc._rgb[0] - low_r);
0072     int delta_g = (fill_high_._fc._rgb[1] - low_g);
0073     int delta_b = (fill_high_._fc._rgb[2] - low_b);
0074 
0075     // Measurement, covariance, truth
0076     scalar t_x = 0.;
0077     scalar t_y = 0.;
0078     scalar t_r = 0.;
0079     scalar t_phi = 0.;
0080 
0081     scalar m_x = 0.;
0082     scalar m_y = 0.;
0083     scalar m_r = 0.;
0084     scalar m_phi = 0.;
0085 
0086     scalar d_r = 0.;
0087     scalar d_phi = 0.;
0088 
0089     scalar c_x = 0.;
0090     scalar c_y = 0.;
0091     scalar c_r = 0.;
0092 
0093     std::array<scalar, 2> x_range = {std::numeric_limits<scalar>::max(),
0094                                      std::numeric_limits<scalar>::lowest()};
0095     std::array<scalar, 2> y_range = x_range;
0096     std::array<scalar, 2> r_range = x_range;
0097     std::array<scalar, 2> phi_range = x_range;
0098 
0099     /// Helper function to generate a fill color
0100     ///
0101     /// @param channel_ the channel in question
0102     auto generate_fill =
0103         [&](const proto::channel<DIM>& channel_) -> style::fill {
0104         // Scale the colors for the filling
0105         scalar rel_delta_data = (channel_._data - low_data) / delta_data;
0106 
0107         int ch_r = low_r + rel_delta_data * delta_r;
0108         int ch_g = low_g + rel_delta_data * delta_g;
0109         int ch_b = low_b + rel_delta_data * delta_b;
0110 
0111         return style::fill{style::color{{ch_r, ch_g, ch_b}}};
0112     };
0113 
0114     if constexpr (DIM == 2) {
0115 
0116         if (cluster_._type == proto::cluster<DIM>::e_polar) {
0117             // Polar case, measurement and truth
0118             m_r = cluster_._measurement[DIM - 2];
0119             m_phi = cluster_._measurement[DIM - 1];
0120 
0121             d_r = cluster_._variance[DIM - 2];
0122             d_phi = cluster_._variance[DIM - 1];
0123 
0124             t_r = cluster_._measurement[DIM - 2];
0125             t_phi = cluster_._measurement[DIM - 1];
0126 
0127         } else {
0128             // Cartesian/Fan case
0129             m_x = cluster_._measurement[DIM - 2];
0130             m_y = cluster_._measurement[DIM - 1];
0131             c_x = cluster_._variance[DIM - 2];
0132             c_y = cluster_._variance[DIM - 1];
0133             t_x = cluster_._truth[DIM - 2];
0134             t_y = cluster_._truth[DIM - 1];
0135 
0136             // Correlation between -1 (-45 deg)and 1 (45 deg)
0137             scalar corr = cluster_._correlation;
0138             corr = corr < -1. ? -1. : (corr > 1. ? 1. : corr);
0139             c_r = -corr * 45;
0140         }
0141     }
0142 
0143     // Loop over the cluster channels and draw them
0144     for (const auto& c : cluster_._channels) {
0145         // Conventional indices
0146         size_t i = 0;
0147         size_t j = 0;
0148         // 2-Dimensional clusters: trivial
0149         if constexpr (DIM == 2) {
0150             i = c._cid[DIM - 2];
0151             j = c._cid[DIM - 1];
0152         }
0153         // 1-Dimensional cluster
0154         if constexpr (DIM == 1) {
0155             if (cluster_._coords[DIM - 1] == proto::cluster<DIM>::e_r or
0156                 cluster_._coords[DIM - 1] == proto::cluster<DIM>::e_x) {
0157                 i = c._cid[DIM - 1];
0158             } else {
0159                 j = c._cid[DIM - 1];
0160             }
0161         }
0162         // Remember for the focus point
0163         i_min = i < i_min ? i : i_min;
0164         i_max = i_max < i ? i : i_max;
0165         j_min = j < j_min ? j : j_min;
0166         j_max = j_max < j ? j : j_max;
0167 
0168         // Get the corresponding grid tile (will be a copy already if
0169         // successful)
0170         std::string tile_id =
0171             grid_._id + "_" + std::to_string(i) + "_" + std::to_string(j);
0172         std::optional<svg::object> grid_tile = grid_.find_object(tile_id);
0173 
0174         if (grid_tile.has_value()) {
0175             // Get the tile
0176             svg::object tile = grid_tile.value();
0177             // Record the x/y/r/phi area
0178             x_range[0] = std::min(x_range[0], tile._x_range[0]);
0179             x_range[1] = std::max(x_range[1], tile._x_range[1]);
0180             y_range[0] = std::min(y_range[0], tile._y_range[0]);
0181             y_range[1] = std::max(y_range[1], tile._y_range[1]);
0182             r_range[0] = std::min(r_range[0], tile._r_range[0]);
0183             r_range[1] = std::max(r_range[1], tile._r_range[1]);
0184             phi_range[0] = std::min(phi_range[0], tile._phi_range[0]);
0185             phi_range[1] = std::max(phi_range[1], tile._phi_range[1]);
0186 
0187             auto points = tile._attribute_map.find("points");
0188             if (points != tile._attribute_map.end()) {
0189                 svg::object cell;
0190                 cell._tag = "polygon";
0191                 cell._id = id_ + "_cell_" + std::to_string(i) + "_" +
0192                            std::to_string(j);
0193                 cell._fill = generate_fill(c);
0194                 cell._stroke = style::stroke();
0195                 cell._attribute_map["points"] = points->second;
0196                 // Add the cell object
0197                 cluster_group.add_object(cell);
0198             }
0199         }
0200     }
0201 
0202     // Need to fill the remaining parameters for measurement & truth
0203     if constexpr (DIM == 1) {
0204         if (cluster_._coords[DIM - 1] == proto::cluster<DIM>::e_r) {
0205             m_r = cluster_._measurement[DIM - 1];
0206             t_r = cluster_._truth[DIM - 1];
0207 
0208             d_r = cluster_._variance[DIM - 1];
0209             d_phi = std::abs(phi_range[1] - phi_range[0]) / std::sqrt(12.);
0210 
0211             m_phi = 0.5 * (phi_range[0] + phi_range[1]);
0212             t_phi = m_phi;
0213         } else if (cluster_._coords[DIM - 1] == proto::cluster<DIM>::e_phi) {
0214             m_phi = cluster_._measurement[DIM - 1];
0215             t_phi = cluster_._truth[DIM - 1];
0216 
0217             d_r = std::abs(r_range[1] - r_range[0]) / std::sqrt(12.);
0218             d_phi = cluster_._variance[DIM - 1];
0219 
0220             m_r = 0.5 * (r_range[0] + r_range[1]);
0221             t_r = m_r;
0222         } else if (cluster_._coords[DIM - 1] == proto::cluster<DIM>::e_x) {
0223             m_x = cluster_._measurement[DIM - 1];
0224             t_x = cluster_._truth[DIM - 1];
0225             c_x = cluster_._variance[DIM - 1];
0226             c_y = std::abs(y_range[1] - y_range[0]) / std::sqrt(12.);
0227             m_y = 0.5 * (y_range[0] + y_range[1]);
0228             t_y = m_y;
0229 
0230             // Correlation between -1 (-45 deg)and 1 (45 deg)
0231             scalar corr = cluster_._correlation;
0232             corr = corr < -1. ? -1. : (corr > 1. ? 1. : corr);
0233             c_r = -corr * 45;
0234 
0235         } else {
0236             m_y = cluster_._measurement[DIM - 1];
0237             t_y = cluster_._truth[DIM - 1];
0238             c_y = cluster_._variance[DIM - 1];
0239             c_x = std::abs(x_range[1] - x_range[0]) / std::sqrt(12.);
0240             m_x = 0.5 * (x_range[0] + x_range[1]);
0241             t_x = m_x;
0242 
0243             // Correlation between -1 (-45 deg)and 1 (45 deg)
0244             scalar corr = cluster_._correlation;
0245             corr = corr < -1. ? -1. : (corr > 1. ? 1. : corr);
0246             c_r = corr * 45;
0247         }
0248     }
0249 
0250     // Finally, if the cluster is polar, transform
0251     if (cluster_._type == proto::cluster<DIM>::e_polar) {
0252 
0253         m_x = m_r * std::cos(m_phi);
0254         m_y = m_r * std::sin(m_phi);
0255 
0256         scalar cos_phi = std::cos(m_phi);
0257         scalar sin_phi = std::sin(m_phi);
0258 
0259         c_x = std::abs(cos_phi * d_r - m_r * sin_phi * d_phi);
0260         c_y = std::abs(sin_phi * d_r + m_r * cos_phi * d_phi);
0261 
0262         c_r = m_phi * 180 / M_PI;
0263 
0264         t_x = t_r * std::cos(t_phi);
0265         t_y = t_r * std::sin(t_phi);
0266     }
0267 
0268     // Add an error ellipse
0269     style::fill fill_c = fill_m_;
0270     fill_c._fc._opacity = 0.5;
0271     style::stroke stroke_c;
0272     style::transform t_c;
0273     t_c._rot = {c_r, m_x, m_y};
0274     cluster_group.add_object(
0275         draw::ellipse("c", {m_x, m_y}, {c_x, c_y}, fill_c, stroke_c, t_c));
0276 
0277     // Add the marker
0278     style::marker marker{"o"};
0279     marker._fill = fill_m_;
0280     cluster_group.add_object(draw::marker("m", {m_x, m_y}, marker));
0281 
0282     if (cluster_._mc) {
0283         style::marker truth_marker{"o"};
0284         cluster_group.add_object(draw::marker("t", {t_x, t_y}, truth_marker));
0285     }
0286 
0287     // Get the focussed grid
0288     svg::object grid_area;
0289     grid_area._tag = "g";
0290     grid_area._id = id_ + std::string("_focussed_grid");
0291 
0292     // Expand
0293     i_min = (i_min >= expand_[0]) ? i_min - expand_[0] : 0;
0294     i_max += expand_[0];
0295     j_min = (j_min >= expand_[1]) ? j_min - expand_[1] : 0;
0296     j_max += expand_[1];
0297 
0298     for (unsigned int i = i_min; i <= i_max; ++i) {
0299         for (unsigned int j = j_min; j < j_max; ++j) {
0300             std::string tile_id =
0301                 grid_._id + "_" + std::to_string(i) + "_" + std::to_string(j);
0302             std::optional<svg::object> grid_tile = grid_.find_object(tile_id);
0303             if (grid_tile.has_value()) {
0304                 grid_area.add_object(grid_tile.value());
0305             }
0306         }
0307     }
0308 
0309     return {cluster_group, grid_area};
0310 }
0311 
0312 }  // namespace display
0313 }  // namespace actsvg