Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /acts/Python/Plugins/src/Svg.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Detector/Detector.hpp"
0010 #include "Acts/Detector/DetectorVolume.hpp"
0011 #include "Acts/Detector/Portal.hpp"
0012 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0013 #include "Acts/Geometry/GeometryContext.hpp"
0014 #include "Acts/Geometry/GridPortalLink.hpp"
0015 #include "Acts/Geometry/Layer.hpp"
0016 #include "Acts/Geometry/PortalLinkBase.hpp"
0017 #include "Acts/Geometry/TrackingGeometry.hpp"
0018 #include "Acts/Geometry/TrackingVolume.hpp"
0019 #include "Acts/Navigation/SurfaceArrayNavigationPolicy.hpp"
0020 #include "Acts/Surfaces/CylinderBounds.hpp"
0021 #include "Acts/Surfaces/DiscBounds.hpp"
0022 #include "Acts/Utilities/Enumerate.hpp"
0023 #include "Acts/Utilities/Helpers.hpp"
0024 #include "ActsPlugins/ActSVG/DetectorSvgConverter.hpp"
0025 #include "ActsPlugins/ActSVG/DetectorVolumeSvgConverter.hpp"
0026 #include "ActsPlugins/ActSVG/IndexedSurfacesSvgConverter.hpp"
0027 #include "ActsPlugins/ActSVG/LayerSvgConverter.hpp"
0028 #include "ActsPlugins/ActSVG/PortalSvgConverter.hpp"
0029 #include "ActsPlugins/ActSVG/SurfaceArraySvgConverter.hpp"
0030 #include "ActsPlugins/ActSVG/SurfaceSvgConverter.hpp"
0031 #include "ActsPlugins/ActSVG/SvgUtils.hpp"
0032 #include "ActsPlugins/ActSVG/TrackingGeometrySvgConverter.hpp"
0033 #include "ActsPython/Utilities/Helpers.hpp"
0034 #include "ActsPython/Utilities/Macros.hpp"
0035 #include <actsvg/core/draw.hpp>
0036 
0037 #include <algorithm>
0038 #include <memory>
0039 #include <ranges>
0040 #include <sstream>
0041 #include <string>
0042 #include <tuple>
0043 #include <vector>
0044 
0045 #include <pybind11/pybind11.h>
0046 #include <pybind11/stl.h>
0047 
0048 namespace py = pybind11;
0049 using namespace pybind11::literals;
0050 
0051 using namespace Acts;
0052 using namespace Acts::Experimental;
0053 using namespace ActsExamples;
0054 using namespace ActsPlugins;
0055 
0056 namespace {
0057 
0058 // A cache object
0059 using PortalCache = std::list<std::string>;
0060 
0061 // Helper lambda for view range selection
0062 bool viewRangeSel(const Svg::ProtoSurface& s, const Extent& vRange) {
0063   for (const auto& v : s._vertices) {
0064     if (vRange.contains(v)) {
0065       return true;
0066     }
0067   }
0068 
0069   return false;
0070 };
0071 
0072 /// @brief helper tuple to define which views and which range to be used
0073 ///
0074 /// @param view is the view type, e.g. 'xy', 'zr', ...
0075 /// @param selection is the selection of the volume, e.g. 'all', 'sensitives', 'portals', 'materials'
0076 using ViewAndRange = std::tuple<std::string, std::vector<std::string>, Extent>;
0077 
0078 /// Helper function to be picked in different access patterns
0079 ///
0080 /// @param pVolume is the proto volume to be drawn
0081 /// @param identification is the identification of the volume
0082 /// @param viewAndRange is the view, selection and range to be drawn
0083 /// @param portalCache is a portal cache to avoid multiple drawings of the same portal
0084 ///
0085 /// Returns an svg object in the right view
0086 actsvg::svg::object drawDetectorVolume(const Svg::ProtoVolume& pVolume,
0087                                        const std::string& identification,
0088                                        const ViewAndRange& viewAndRange,
0089                                        PortalCache& portalCache) {
0090   actsvg::svg::object svgDet;
0091   svgDet._id = identification;
0092   svgDet._tag = "g";
0093 
0094   const auto& [view, selection, viewRange] = viewAndRange;
0095 
0096   // Translate selection into booleans
0097   const bool all = rangeContainsValue(selection, "all");
0098   const bool sensitives = rangeContainsValue(selection, "sensitives");
0099   const bool portals = rangeContainsValue(selection, "portals");
0100   const bool materials = rangeContainsValue(selection, "materials");
0101 
0102   // Helper lambda for material selection
0103   auto materialSel = [&](const Svg::ProtoSurface& s) -> bool {
0104     return (materials && s._decorations.contains("material"));
0105   };
0106 
0107   // -------------------- surface section
0108   // The surfaces to be drawn
0109   std::vector<Svg::ProtoVolume::surface_type> sSurfaces;
0110   sSurfaces.reserve(pVolume._v_surfaces.size());
0111   for (const auto& s : pVolume._v_surfaces) {
0112     if ((all || sensitives || materialSel(s)) && viewRangeSel(s, viewRange)) {
0113       sSurfaces.push_back(s);
0114     }
0115   }
0116 
0117   // Now draw all the surfaces
0118   for (const auto& vs : sSurfaces) {
0119     if (view == "xy") {
0120       svgDet.add_object(Svg::View::xy(vs, identification));
0121     } else if (view == "zr") {
0122       svgDet.add_object(Svg::View::zr(vs, identification));
0123     } else {
0124       throw std::invalid_argument("Unknown view type");
0125     }
0126   }
0127 
0128   // -------------------- portal section
0129   std::vector<Svg::ProtoPortal> sPortals;
0130   sPortals.reserve(pVolume._portals.size());
0131   for (const auto& vp : pVolume._portals) {
0132     if ((all || portals || materialSel(vp._surface)) &&
0133         viewRangeSel(vp._surface, viewRange)) {
0134       sPortals.push_back(vp);
0135     }
0136   }
0137 
0138   // Now draw all the portals - if not already in the cache
0139   for (const auto& vp : sPortals) {
0140     auto pgID = vp._surface._decorations.find("geo_id");
0141     std::string gpIDs = "";
0142     if (pgID != vp._surface._decorations.end()) {
0143       gpIDs = pgID->second._id;
0144     }
0145 
0146     if (rangeContainsValue(portalCache, gpIDs)) {
0147       continue;
0148     }
0149 
0150     // Register this portal to the cache
0151     portalCache.insert(portalCache.begin(), gpIDs);
0152 
0153     if (view == "xy") {
0154       svgDet.add_object(Svg::View::xy(vp, identification));
0155     } else if (view == "zr") {
0156       svgDet.add_object(Svg::View::zr(vp, identification));
0157     } else {
0158       throw std::invalid_argument("Unknown view type");
0159     }
0160   }
0161   return svgDet;
0162 }
0163 
0164 // Helper function to be picked in different access patterns
0165 std::vector<actsvg::svg::object> drawDetector(
0166     const GeometryContext& gctx, const Detector& detector,
0167     const std::string& identification,
0168     const std::vector<std::tuple<int, Svg::DetectorVolumeConverter::Options>>&
0169         volumeIdxOpts,
0170     const std::vector<ViewAndRange>& viewAndRanges) {
0171   PortalCache portalCache;
0172 
0173   // The svg object to be returned
0174   std::vector<actsvg::svg::object> svgDetViews;
0175   svgDetViews.reserve(viewAndRanges.size());
0176   for (unsigned int i = 0; i < viewAndRanges.size(); ++i) {
0177     actsvg::svg::object svgDet;
0178     svgDet._id = identification;
0179     svgDet._tag = "g";
0180     svgDetViews.push_back(svgDet);
0181   }
0182 
0183   for (const auto& [vidx, vopts] : volumeIdxOpts) {
0184     // Get the volume and convert it
0185     const auto& v = detector.volumes()[vidx];
0186     auto [pVolume, pGrid] =
0187         Svg::DetectorVolumeConverter::convert(gctx, *v, vopts);
0188 
0189     for (auto [iv, var] : enumerate(viewAndRanges)) {
0190       auto [view, selection, range] = var;
0191       // Get the view and the range
0192       auto svgVolView = drawDetectorVolume(
0193           pVolume, identification + "_vol" + std::to_string(vidx) + "_" + view,
0194           var, portalCache);
0195       svgDetViews[iv].add_object(svgVolView);
0196     }
0197   }
0198   return svgDetViews;
0199 }
0200 
0201 }  // namespace
0202 
0203 PYBIND11_MODULE(ActsPluginsPythonBindingsSvg, svg) {
0204   using namespace Acts;
0205   using namespace ActsPlugins;
0206 
0207   // Primitives, should be dropped in favour of actsvg pybind11 bindings
0208   {
0209     py::class_<actsvg::svg::object>(svg, "object")
0210         .def_readwrite("id", &actsvg::svg::object::_id);
0211 
0212     py::class_<actsvg::svg::file>(svg, "file")
0213         .def(py::init<>())
0214         .def("add_object", &actsvg::svg::file::add_object)
0215         .def("add_objects", &actsvg::svg::file::add_objects)
0216         .def("clip",
0217              [](actsvg::svg::file& self, std::array<actsvg::scalar, 4> box) {
0218                self.set_view_box(box);
0219              })
0220         .def("write",
0221              [](const actsvg::svg::file& self, const std::string& filename) {
0222                std::ofstream file(filename);
0223                file << self;
0224                file.close();
0225              });
0226 
0227     svg.def("toFile", &Svg::toFile, py::arg("objects"), py::arg("filename"));
0228   }
0229 
0230   // Core components, added as an acts.svg submodule
0231   {
0232     auto c = py::class_<Svg::Style>(svg, "Style").def(py::init<>());
0233     ACTS_PYTHON_STRUCT(c, fillColor, fillOpacity, highlightColor, highlights,
0234                        strokeWidth, strokeColor, highlightStrokeWidth,
0235                        highlightStrokeColor, fontSize, fontColor,
0236                        quarterSegments);
0237   }
0238 
0239   // How surfaces should be drawn: Svg Surface options & drawning
0240   {
0241     auto c = py::class_<Svg::SurfaceConverter::Options>(svg, "SurfaceOptions")
0242                  .def(py::init<>());
0243     ACTS_PYTHON_STRUCT(c, style, templateSurface);
0244 
0245     // Define the proto surface
0246     py::class_<Svg::ProtoSurface>(svg, "ProtoSurface");
0247     // Convert an Surface object into an svg::proto::surface
0248 
0249     svg.def("convertSurface", &Svg::SurfaceConverter::convert);
0250 
0251     // Define the view functions
0252     svg.def("viewSurface", [](const Svg::ProtoSurface& pSurface,
0253                               const std::string& identification,
0254                               const std::string& view = "xy") {
0255       if (view == "xy") {
0256         return Svg::View::xy(pSurface, identification);
0257       } else if (view == "zr") {
0258         return Svg::View::zr(pSurface, identification);
0259       } else if (view == "zphi") {
0260         return Svg::View::zphi(pSurface, identification);
0261       } else if (view == "zrphi") {
0262         return Svg::View::zrphi(pSurface, identification);
0263       } else {
0264         throw std::invalid_argument("Unknown view type");
0265       }
0266     });
0267   }
0268 
0269   // How portals should be drawn: Svg Portal options & drawning
0270   {
0271     auto c = py::class_<Svg::PortalConverter::Options>(svg, "PortalOptions")
0272                  .def(py::init<>());
0273 
0274     ACTS_PYTHON_STRUCT(c, surfaceOptions, linkLength, volumeIndices);
0275 
0276     // Define the proto portal
0277     py::class_<Svg::ProtoPortal>(svg, "ProtoPortal");
0278     // Convert an Portal object into an
0279     // svg::proto::portal
0280     svg.def("convertPortal", &Svg::PortalConverter::convert);
0281 
0282     // Define the view functions
0283     svg.def("viewPortal", [](const Svg::ProtoPortal& pPortal,
0284                              const std::string& identification,
0285                              const std::string& view = "xy") {
0286       if (view == "xy") {
0287         return Svg::View::xy(pPortal, identification);
0288       } else if (view == "zr") {
0289         return Svg::View::zr(pPortal, identification);
0290       } else {
0291         throw std::invalid_argument("Unknown view type");
0292       }
0293     });
0294   }
0295 
0296   // Draw primitives
0297   {
0298     svg.def("drawArrow", &actsvg::draw::arrow);
0299 
0300     svg.def("drawText", &actsvg::draw::text);
0301 
0302     svg.def("drawInfoBox", &Svg::infoBox);
0303   }
0304 
0305   // Draw Eta Lines
0306   {
0307     svg.def(
0308         "drawEtaLines",
0309         [](const std::string& id, actsvg ::scalar z, actsvg::scalar r,
0310            const std::vector<actsvg::scalar>& etaMain,
0311            actsvg::scalar strokeWidthMain, unsigned int sizeMain,
0312            bool labelMain, const std::vector<actsvg::scalar>& etaSub,
0313            actsvg::scalar strokeWidthSub, const std::vector<int> strokeDashSub,
0314            unsigned int sizeSub, bool labelSub) {
0315           // The main eta lines
0316           actsvg::style::stroke strokeMain;
0317           strokeMain._width = strokeWidthMain;
0318           actsvg::style::font fontMain;
0319           fontMain._size = sizeMain;
0320 
0321           actsvg::style::stroke strokeSub;
0322           strokeSub._width = strokeWidthSub;
0323           strokeSub._dasharray = strokeDashSub;
0324           actsvg::style::font fontSub;
0325           fontSub._size = sizeSub;
0326 
0327           return actsvg::display::eta_lines(
0328               id, z, r,
0329               {std::tie(etaMain, strokeMain, labelMain, fontMain),
0330                std::tie(etaSub, strokeSub, labelSub, fontSub)});
0331         });
0332   }
0333 
0334   {
0335     auto gco = py::class_<Svg::GridConverter::Options>(svg, "GridOptions")
0336                    .def(py::init<>());
0337     ACTS_PYTHON_STRUCT(gco, style);
0338 
0339     auto isco = py::class_<Svg::IndexedSurfacesConverter::Options>(
0340                     svg, "IndexedSurfacesOptions")
0341                     .def(py::init<>());
0342     ACTS_PYTHON_STRUCT(isco, gridOptions);
0343   }
0344 
0345   // How detector volumes are drawn: Svg DetectorVolume options & drawning
0346   {
0347     auto c = py::class_<Svg::DetectorVolumeConverter::Options>(
0348                  svg, "DetectorVolumeOptions")
0349                  .def(py::init<>());
0350 
0351     ACTS_PYTHON_STRUCT(c, portalIndices, portalOptions, surfaceOptions,
0352                        indexedSurfacesOptions);
0353 
0354     // Convert an DetectorVolume object into an
0355     // svg::proto::volume
0356     svg.def("convertDetectorVolume", &Svg::DetectorVolumeConverter::convert);
0357 
0358     // Define the view functions
0359     svg.def("drawDetectorVolume", &drawDetectorVolume);
0360   }
0361 
0362   // Draw the ProtoIndexedSurfaceGrid
0363   {
0364     svg.def("drawIndexedSurfaces",
0365             [](const Svg::ProtoIndexedSurfaceGrid& pIndexedSurfaceGrid,
0366                const std::string& identification) {
0367               return Svg::View::xy(pIndexedSurfaceGrid, identification);
0368             });
0369   }
0370 
0371   // How a detector is drawn: Svg Detector options & drawning
0372   { svg.def("drawDetector", &drawDetector); }
0373 
0374   { svg.def("drawSurfaceArrays", &Svg::drawSurfaceArrays); }
0375 
0376   // Legacy geometry drawing
0377   {
0378     using DefinedStyle = std::pair<GeometryIdentifier, Svg::Style>;
0379     using DefinedStyleSet = std::vector<DefinedStyle>;
0380 
0381     auto sm = py::class_<GeometryHierarchyMap<Svg::Style>>(svg, "StyleMap")
0382                   .def(py::init<DefinedStyleSet>(), py::arg("elements"));
0383 
0384     auto c = py::class_<Svg::LayerConverter::Options>(svg, "LayerOptions")
0385                  .def(py::init<>());
0386     ACTS_PYTHON_STRUCT(c, name, surfaceStyles, zRange, phiRange, gridInfo,
0387                        moduleInfo, projectionInfo, labelProjection, labelGauge);
0388   }
0389 
0390   {
0391     using DefinedLayerOptions =
0392         std::pair<GeometryIdentifier, Svg::LayerConverter::Options>;
0393     using DefinedLayerOptionsSet = std::vector<DefinedLayerOptions>;
0394 
0395     auto lom =
0396         py::class_<GeometryHierarchyMap<Svg::LayerConverter::Options>>(
0397             svg, "LayerOptionMap")
0398             .def(py::init<DefinedLayerOptionsSet>(), py::arg("elements"));
0399 
0400     auto c = py::class_<Svg::TrackingGeometryConverter::Options>(
0401                  svg, "TrackingGeometryOptions")
0402                  .def(py::init<>());
0403     ACTS_PYTHON_STRUCT(c, prefix, layerOptions);
0404   }
0405 
0406   // Tracking geometry drawing
0407   {
0408     svg.def(
0409         "drawTrackingGeometry",
0410         [](const GeometryContext& gctx, const TrackingGeometry& tGeometry,
0411            const std::string& view, bool drawSurfaces, bool highlightMaterial) {
0412           std::variant<actsvg::views::x_y, actsvg::views::z_r> v;
0413           if (view == "xy") {
0414             v = actsvg::views::x_y();
0415           } else if (view == "zr") {
0416             v = actsvg::views::z_r();
0417           } else {
0418             throw std::invalid_argument("Unknown view type");
0419           }
0420 
0421           return Svg::drawTrackingGeometry(gctx, tGeometry, v, drawSurfaces,
0422                                            highlightMaterial);
0423         },
0424         py::arg("gctx"), py::arg("tGeometry"), py::arg("view"),
0425         py::arg("drawSurfaces") = true, py::arg("highlightMaterial") = false);
0426   }
0427 }