Back to home page

EIC code displayed by LXR

 
 

    


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

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 <array>
0013 #include <limits>
0014 #include <map>
0015 #include <optional>
0016 #include <string>
0017 #include <vector>
0018 
0019 #include "defs.hpp"
0020 #include "style.hpp"
0021 
0022 namespace actsvg {
0023 
0024 namespace svg {
0025 /** An svg object
0026  *
0027  * It carries its attributes in a map, but can also have additional
0028  * animiations.
0029  *
0030  * To generate the final svg, it also records the barycenter and the view
0031  * range
0032  **/
0033 struct object {
0034 
0035     /** Nested summary struct
0036      *
0037      * This struct allows for defining some summary
0038      * information with which this object has been
0039      * create.
0040      *
0041      **/
0042     struct summary {
0043         std::array<scalar, 2> _scale = {1., 1.};
0044     };
0045 
0046     /// SVG tag of the objec
0047     std::string _tag = "";
0048 
0049     /// Identification string
0050     std::string _id = "";
0051 
0052     /// Auxiliary info for this object
0053     std::vector<std::string> _aux_info = {};
0054 
0055     /// Sterile - does not write _fill, stroke, transform
0056     bool _sterile = false;
0057 
0058     /// View is active - is set to false, it is not written out
0059     bool _active = true;
0060 
0061     /// In-field of the svg tag
0062     std::vector<std::string> _field = {};
0063     scalar _field_span = 12;
0064 
0065     /// Fill specifications
0066     style::fill _fill = style::fill();
0067 
0068     /// Stroke specifications
0069     style::stroke _stroke = style::stroke();
0070 
0071     /// Transform of the object
0072     style::transform _transform = style::transform{};
0073 
0074     /// Attribute map for writing
0075     std::map<std::string, std::string> _attribute_map = {};
0076 
0077     /// Contains sub objects ( group, animation, etc.)
0078     std::vector<object> _sub_objects = {};
0079 
0080     /// Contains definintions
0081     std::vector<object> _definitions = {};
0082 
0083     /// Barycenter from vertices
0084     std::array<scalar, 2> _barycenter = {0., 0.};
0085 
0086     /// Range in x - view frame
0087     std::array<scalar, 2> _x_range = {std::numeric_limits<scalar>::max(),
0088                                       std::numeric_limits<scalar>::lowest()};
0089     /// Range in y - view frame
0090     std::array<scalar, 2> _y_range = {std::numeric_limits<scalar>::max(),
0091                                       std::numeric_limits<scalar>::lowest()};
0092     /// Range in r  - view frame
0093     std::array<scalar, 2> _r_range = {std::numeric_limits<scalar>::max(),
0094                                       std::numeric_limits<scalar>::lowest()};
0095     /// Range in phi - view frame
0096     std::array<scalar, 2> _phi_range = {std::numeric_limits<scalar>::max(),
0097                                         std::numeric_limits<scalar>::lowest()};
0098 
0099     /// Summary object
0100     summary _summary = summary{};
0101 
0102     /** An object is defined if a tag is set */
0103     bool is_defined() const { return (not _tag.empty()); }
0104 
0105     /** Add a sub object and respect the min/max range
0106      *
0107      * @param o_ is the object in question
0108      **/
0109     void add_object(const svg::object &o_) {
0110         // Add the object
0111         _sub_objects.push_back(o_);
0112         // Collect eventual definitions
0113         _definitions.insert(_definitions.end(), o_._definitions.begin(),
0114                             o_._definitions.end());
0115         if (not _sterile) {
0116             // Re-measure, x/y/r/phi ranges include transforms already
0117             _x_range = {std::min(_x_range[0], o_._x_range[0]),
0118                         std::max(_x_range[1], o_._x_range[1])};
0119             _y_range = {std::min(_y_range[0], o_._y_range[0]),
0120                         std::max(_y_range[1], o_._y_range[1])};
0121             _r_range = {std::min(_r_range[0], o_._r_range[0]),
0122                         std::max(_r_range[1], o_._r_range[1])};
0123             _phi_range = {std::min(_phi_range[0], o_._phi_range[0]),
0124                           std::max(_phi_range[1], o_._phi_range[1])};
0125         }
0126     }
0127 
0128     /** Find an object by string,
0129      *
0130      * @param id_ is the identifier with which the sub object
0131      * is searched for
0132      *
0133      * @note std::optional is used and std::nullopt as @return if
0134      * the search was not successful
0135      */
0136     std::optional<svg::object> find_object(const std::string &id_) const {
0137 
0138         auto found_object =
0139             std::find_if(_sub_objects.begin(), _sub_objects.end(),
0140                          [&](const svg::object &candidate_) {
0141                              return (candidate_._id == id_);
0142                          });
0143 
0144         if (found_object != _sub_objects.end()) {
0145             return (*found_object);
0146         }
0147         return std::nullopt;
0148     }
0149 
0150     /** Add a sub object and respect the min/max range
0151      *
0152      * @param oc_ is the object container to be added
0153      **/
0154     template <typename object_container>
0155     void add_objects(const object_container &oc_) {
0156         // Add the object
0157         for (const auto &o : oc_) {
0158             add_object(o);
0159         }
0160     }
0161 
0162     friend std::ostream &operator<<(std::ostream &os_, const object &o_);
0163 };
0164 
0165 /** Stream operator with @param os_ the output stream and @param o_ the streamed
0166  * object */
0167 inline std::ostream &operator<<(std::ostream &os_, const object &o_) {
0168 
0169     // We make a temporary copy for writing, this way we can
0170     // write the same one with different attributes sets
0171     object o_copy = o_;
0172 
0173     // Write the file
0174     os_ << __l << o_copy._tag;
0175     if (not o_copy._id.empty()) {
0176         os_ << __blk << "id=\"" << o_copy._id << "\"";
0177     }
0178 
0179     // Attach the styles: fill, stroke, transform
0180     if (not o_._sterile) {
0181         o_._fill.attach_attributes(o_copy);
0182         o_._stroke.attach_attributes(o_copy);
0183         o_._transform.attach_attributes(o_copy);
0184     }
0185 
0186     // The attribute map
0187     for (auto [key, value] : o_copy._attribute_map) {
0188         os_ << __blk << key << "=\"" << value << "\"";
0189     }
0190 
0191     // This is done return
0192     if (o_copy._sub_objects.empty() and o_copy._field.empty()) {
0193         os_ << __er;
0194         return os_;
0195     }
0196 
0197     os_ << __r;
0198     for (const auto &a : o_copy._sub_objects) {
0199         os_ << a;
0200     }
0201     if (not o_copy._field.empty()) {
0202         if (o_copy._field.size() == 1) {
0203             os_ << o_copy._field[0];
0204         } else {
0205             for (const auto &fl : o_copy._field) {
0206                 os_ << "<tspan x=\"";
0207                 os_ << o_copy._x_range[0] << "\"";
0208                 os_ << " dy=\"" << o_copy._field_span << "\">";
0209                 os_ << fl;
0210                 os_ << "</tspan>" << __nl;
0211             }
0212         }
0213     }
0214     // Close-out
0215     os_ << __el << o_copy._tag << __r;
0216     return os_;
0217 }
0218 
0219 /** An svg file scope
0220  *
0221  * This contains objects, connections and groups for final writing
0222  * It can be optionally augmented with an html bracket.
0223  *
0224  **/
0225 struct file {
0226 
0227     /// Directive to add html header or not
0228     bool _add_html = false;
0229 
0230     /// Default header tail definitions
0231     std::string _html_head = "<html>\n<body>\n";
0232     std::string _svg_head = "<svg version=\"1.1\"";
0233 
0234     std::string _svg_def_end =
0235         " xmlns=\"http://www.w3.org/2000/svg\"  "
0236         "xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n";
0237 
0238     std::string _svg_tail = "</svg>\n";
0239     std::string _html_tail = "</body>\n</html>\n";
0240 
0241     scalar _width = 800;
0242     scalar _height = 800;
0243     scalar _border = 10;
0244 
0245     std::vector<object> _objects = {};
0246 
0247     /** Add an object and respect the min/max range
0248      *
0249      * @param o_ is the object in question
0250      **/
0251     void add_object(const svg::object &o_) {
0252         // Add the object
0253         if (o_._active) {
0254             _objects.push_back(o_);
0255         }
0256     }
0257 
0258     friend std::ostream &operator<<(std::ostream &os_, const file &f_);
0259 };
0260 
0261 /** Stream operator with @param os_ the output stream and @param o_ the streamed
0262  * object */
0263 inline std::ostream &operator<<(std::ostream &os_, const file &f_) {
0264     // Do the viewBox adjustment
0265     std::array<scalar, 2> x_range = {std::numeric_limits<scalar>::max(),
0266                                      std::numeric_limits<scalar>::min()};
0267     std::array<scalar, 2> y_range = {std::numeric_limits<scalar>::max(),
0268                                      std::numeric_limits<scalar>::min()};
0269 
0270     std::array<scalar, 4> viewBox = {-800, -800, 1600, 1600};
0271 
0272     std::map<std::string, svg::object> definitions;
0273 
0274     for (const auto &o : f_._objects) {
0275         x_range[0] = std::min(x_range[0], o._x_range[0]);
0276         x_range[1] = std::max(x_range[1], o._x_range[1]);
0277         y_range[0] = std::min(y_range[0], o._y_range[0]);
0278         y_range[1] = std::max(y_range[1], o._y_range[1]);
0279         for (const auto &d : o._definitions) {
0280             definitions[d._id] = d;
0281         }
0282     }
0283     // Enlarge the view box by 10 percent
0284     viewBox[2] = 1.2 * (x_range[1] - x_range[0]);
0285     viewBox[3] = 1.2 * (y_range[1] - y_range[0]);
0286 
0287     // Include a fixed size border
0288     viewBox[0] = (x_range[0] - 0.1 * viewBox[2]) - f_._border;
0289     viewBox[1] = (y_range[0] - 0.1 * viewBox[3]) - f_._border;
0290     viewBox[2] += f_._border;
0291     viewBox[3] += f_._border;
0292 
0293     if (f_._add_html) {
0294         os_ << f_._html_head;
0295     }
0296     os_ << f_._svg_head;
0297     os_ << " width=\"" << f_._width << "\" height=\"" << f_._height << "\"";
0298     os_ << " viewBox=\"" << viewBox[0] << " " << viewBox[1] << " " << viewBox[2]
0299         << " " << viewBox[3] << "\"";
0300     os_ << f_._svg_def_end;
0301     // Write the definitions first
0302     if (not definitions.empty()) {
0303         os_ << "<defs>";
0304         for (auto [key, value] : definitions) {
0305             os_ << value;
0306         }
0307         os_ << "</defs>";
0308     }
0309 
0310     // Now write the objects
0311     for (auto &o : f_._objects) {
0312         os_ << o;
0313     }
0314     os_ << f_._svg_tail;
0315     if (f_._add_html) {
0316         os_ << f_._html_tail;
0317     }
0318     return os_;
0319 }
0320 
0321 }  // namespace svg
0322 }  // namespace actsvg