File indexing completed on 2025-10-31 08:17:18
0001 
0002 
0003 
0004 
0005 
0006 
0007 
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 "ActsExamples/EventData/GeometryContainers.hpp"
0025 #include "ActsExamples/EventData/SimHit.hpp"
0026 #include "ActsExamples/EventData/SimSpacePoint.hpp"
0027 #include "ActsExamples/Io/Svg/SvgPointWriter.hpp"
0028 #include "ActsExamples/Io/Svg/SvgTrackingGeometryWriter.hpp"
0029 #include "ActsPlugins/ActSVG/DetectorSvgConverter.hpp"
0030 #include "ActsPlugins/ActSVG/DetectorVolumeSvgConverter.hpp"
0031 #include "ActsPlugins/ActSVG/IndexedSurfacesSvgConverter.hpp"
0032 #include "ActsPlugins/ActSVG/LayerSvgConverter.hpp"
0033 #include "ActsPlugins/ActSVG/PortalSvgConverter.hpp"
0034 #include "ActsPlugins/ActSVG/SurfaceArraySvgConverter.hpp"
0035 #include "ActsPlugins/ActSVG/SurfaceSvgConverter.hpp"
0036 #include "ActsPlugins/ActSVG/SvgUtils.hpp"
0037 #include "ActsPlugins/ActSVG/TrackingGeometrySvgConverter.hpp"
0038 #include "ActsPython/Utilities/Helpers.hpp"
0039 #include "ActsPython/Utilities/Macros.hpp"
0040 #include <actsvg/core/draw.hpp>
0041 
0042 #include <algorithm>
0043 #include <memory>
0044 #include <ranges>
0045 #include <sstream>
0046 #include <string>
0047 #include <tuple>
0048 #include <vector>
0049 
0050 #include <pybind11/pybind11.h>
0051 #include <pybind11/stl.h>
0052 
0053 namespace py = pybind11;
0054 using namespace pybind11::literals;
0055 
0056 using namespace Acts;
0057 using namespace Acts::Experimental;
0058 using namespace ActsExamples;
0059 using namespace ActsPlugins;
0060 
0061 namespace {
0062 
0063 
0064 using PortalCache = std::list<std::string>;
0065 
0066 
0067 bool viewRangeSel(const Svg::ProtoSurface& s, const Extent& vRange) {
0068   for (const auto& v : s._vertices) {
0069     if (vRange.contains(v)) {
0070       return true;
0071     }
0072   }
0073 
0074   return false;
0075 };
0076 
0077 
0078 
0079 
0080 
0081 using ViewAndRange = std::tuple<std::string, std::vector<std::string>, Extent>;
0082 
0083 
0084 
0085 
0086 
0087 
0088 
0089 
0090 
0091 actsvg::svg::object drawDetectorVolume(const Svg::ProtoVolume& pVolume,
0092                                        const std::string& identification,
0093                                        const ViewAndRange& viewAndRange,
0094                                        PortalCache& portalCache) {
0095   actsvg::svg::object svgDet;
0096   svgDet._id = identification;
0097   svgDet._tag = "g";
0098 
0099   const auto& [view, selection, viewRange] = viewAndRange;
0100 
0101   
0102   const bool all = rangeContainsValue(selection, "all");
0103   const bool sensitives = rangeContainsValue(selection, "sensitives");
0104   const bool portals = rangeContainsValue(selection, "portals");
0105   const bool materials = rangeContainsValue(selection, "materials");
0106 
0107   
0108   auto materialSel = [&](const Svg::ProtoSurface& s) -> bool {
0109     return (materials && s._decorations.contains("material"));
0110   };
0111 
0112   
0113   
0114   std::vector<Svg::ProtoVolume::surface_type> sSurfaces;
0115   sSurfaces.reserve(pVolume._v_surfaces.size());
0116   for (const auto& s : pVolume._v_surfaces) {
0117     if ((all || sensitives || materialSel(s)) && viewRangeSel(s, viewRange)) {
0118       sSurfaces.push_back(s);
0119     }
0120   }
0121 
0122   
0123   for (const auto& vs : sSurfaces) {
0124     if (view == "xy") {
0125       svgDet.add_object(Svg::View::xy(vs, identification));
0126     } else if (view == "zr") {
0127       svgDet.add_object(Svg::View::zr(vs, identification));
0128     } else {
0129       throw std::invalid_argument("Unknown view type");
0130     }
0131   }
0132 
0133   
0134   std::vector<Svg::ProtoPortal> sPortals;
0135   sPortals.reserve(pVolume._portals.size());
0136   for (const auto& vp : pVolume._portals) {
0137     if ((all || portals || materialSel(vp._surface)) &&
0138         viewRangeSel(vp._surface, viewRange)) {
0139       sPortals.push_back(vp);
0140     }
0141   }
0142 
0143   
0144   for (const auto& vp : sPortals) {
0145     auto pgID = vp._surface._decorations.find("geo_id");
0146     std::string gpIDs = "";
0147     if (pgID != vp._surface._decorations.end()) {
0148       gpIDs = pgID->second._id;
0149     }
0150 
0151     if (rangeContainsValue(portalCache, gpIDs)) {
0152       continue;
0153     }
0154 
0155     
0156     portalCache.insert(portalCache.begin(), gpIDs);
0157 
0158     if (view == "xy") {
0159       svgDet.add_object(Svg::View::xy(vp, identification));
0160     } else if (view == "zr") {
0161       svgDet.add_object(Svg::View::zr(vp, identification));
0162     } else {
0163       throw std::invalid_argument("Unknown view type");
0164     }
0165   }
0166   return svgDet;
0167 }
0168 
0169 
0170 std::vector<actsvg::svg::object> drawDetector(
0171     const GeometryContext& gctx, const Detector& detector,
0172     const std::string& identification,
0173     const std::vector<std::tuple<int, Svg::DetectorVolumeConverter::Options>>&
0174         volumeIdxOpts,
0175     const std::vector<ViewAndRange>& viewAndRanges) {
0176   PortalCache portalCache;
0177 
0178   
0179   std::vector<actsvg::svg::object> svgDetViews;
0180   svgDetViews.reserve(viewAndRanges.size());
0181   for (unsigned int i = 0; i < viewAndRanges.size(); ++i) {
0182     actsvg::svg::object svgDet;
0183     svgDet._id = identification;
0184     svgDet._tag = "g";
0185     svgDetViews.push_back(svgDet);
0186   }
0187 
0188   for (const auto& [vidx, vopts] : volumeIdxOpts) {
0189     
0190     const auto& v = detector.volumes()[vidx];
0191     auto [pVolume, pGrid] =
0192         Svg::DetectorVolumeConverter::convert(gctx, *v, vopts);
0193 
0194     for (auto [iv, var] : enumerate(viewAndRanges)) {
0195       auto [view, selection, range] = var;
0196       
0197       auto svgVolView = drawDetectorVolume(
0198           pVolume, identification + "_vol" + std::to_string(vidx) + "_" + view,
0199           var, portalCache);
0200       svgDetViews[iv].add_object(svgVolView);
0201     }
0202   }
0203   return svgDetViews;
0204 }
0205 
0206 }  
0207 
0208 namespace ActsPython {
0209 void addSvg(Context& ctx) {
0210   auto [m, mex] = ctx.get("main", "examples");
0211 
0212   auto svg = m.def_submodule("svg");
0213 
0214   { svg.def("toFile", &Svg::toFile, py::arg("objects"), py::arg("filename")); }
0215 
0216   py::class_<actsvg::svg::object>(svg, "object")
0217       .def_readwrite("id", &actsvg::svg::object::_id);
0218 
0219   py::class_<actsvg::svg::file>(svg, "file")
0220       .def(py::init<>())
0221       .def("add_object", &actsvg::svg::file::add_object)
0222       .def("add_objects", &actsvg::svg::file::add_objects)
0223       .def("clip",
0224            [](actsvg::svg::file& self, std::array<actsvg::scalar, 4> box) {
0225              self.set_view_box(box);
0226            })
0227       .def("write",
0228            [](const actsvg::svg::file& self, const std::string& filename) {
0229              std::ofstream file(filename);
0230              file << self;
0231              file.close();
0232            });
0233 
0234   { svg.def("toFile", &Svg::toFile, py::arg("objects"), py::arg("filename")); }
0235 
0236   
0237   {
0238     auto c = py::class_<Svg::Style>(svg, "Style").def(py::init<>());
0239     ACTS_PYTHON_STRUCT(c, fillColor, fillOpacity, highlightColor, highlights,
0240                        strokeWidth, strokeColor, highlightStrokeWidth,
0241                        highlightStrokeColor, fontSize, fontColor,
0242                        quarterSegments);
0243   }
0244 
0245   
0246   {
0247     auto c = py::class_<Svg::SurfaceConverter::Options>(svg, "SurfaceOptions")
0248                  .def(py::init<>());
0249     ACTS_PYTHON_STRUCT(c, style, templateSurface);
0250 
0251     
0252     py::class_<Svg::ProtoSurface>(svg, "ProtoSurface");
0253     
0254 
0255     svg.def("convertSurface", &Svg::SurfaceConverter::convert);
0256 
0257     
0258     svg.def("viewSurface", [](const Svg::ProtoSurface& pSurface,
0259                               const std::string& identification,
0260                               const std::string& view = "xy") {
0261       if (view == "xy") {
0262         return Svg::View::xy(pSurface, identification);
0263       } else if (view == "zr") {
0264         return Svg::View::zr(pSurface, identification);
0265       } else if (view == "zphi") {
0266         return Svg::View::zphi(pSurface, identification);
0267       } else if (view == "zrphi") {
0268         return Svg::View::zrphi(pSurface, identification);
0269       } else {
0270         throw std::invalid_argument("Unknown view type");
0271       }
0272     });
0273   }
0274 
0275   
0276   {
0277     auto c = py::class_<Svg::PortalConverter::Options>(svg, "PortalOptions")
0278                  .def(py::init<>());
0279 
0280     ACTS_PYTHON_STRUCT(c, surfaceOptions, linkLength, volumeIndices);
0281 
0282     
0283     py::class_<Svg::ProtoPortal>(svg, "ProtoPortal");
0284     
0285     
0286     svg.def("convertPortal", &Svg::PortalConverter::convert);
0287 
0288     
0289     svg.def("viewPortal", [](const Svg::ProtoPortal& pPortal,
0290                              const std::string& identification,
0291                              const std::string& view = "xy") {
0292       if (view == "xy") {
0293         return Svg::View::xy(pPortal, identification);
0294       } else if (view == "zr") {
0295         return Svg::View::zr(pPortal, identification);
0296       } else {
0297         throw std::invalid_argument("Unknown view type");
0298       }
0299     });
0300   }
0301 
0302   
0303   {
0304     svg.def("drawArrow", &actsvg::draw::arrow);
0305 
0306     svg.def("drawText", &actsvg::draw::text);
0307 
0308     svg.def("drawInfoBox", &Svg::infoBox);
0309   }
0310 
0311   
0312   {
0313     svg.def(
0314         "drawEtaLines",
0315         [](const std::string& id, actsvg ::scalar z, actsvg::scalar r,
0316            const std::vector<actsvg::scalar>& etaMain,
0317            actsvg::scalar strokeWidthMain, unsigned int sizeMain,
0318            bool labelMain, const std::vector<actsvg::scalar>& etaSub,
0319            actsvg::scalar strokeWidthSub, const std::vector<int> strokeDashSub,
0320            unsigned int sizeSub, bool labelSub) {
0321           
0322           actsvg::style::stroke strokeMain;
0323           strokeMain._width = strokeWidthMain;
0324           actsvg::style::font fontMain;
0325           fontMain._size = sizeMain;
0326 
0327           actsvg::style::stroke strokeSub;
0328           strokeSub._width = strokeWidthSub;
0329           strokeSub._dasharray = strokeDashSub;
0330           actsvg::style::font fontSub;
0331           fontSub._size = sizeSub;
0332 
0333           return actsvg::display::eta_lines(
0334               id, z, r,
0335               {std::tie(etaMain, strokeMain, labelMain, fontMain),
0336                std::tie(etaSub, strokeSub, labelSub, fontSub)});
0337         });
0338   }
0339 
0340   {
0341     auto gco = py::class_<Svg::GridConverter::Options>(svg, "GridOptions")
0342                    .def(py::init<>());
0343     ACTS_PYTHON_STRUCT(gco, style);
0344 
0345     auto isco = py::class_<Svg::IndexedSurfacesConverter::Options>(
0346                     svg, "IndexedSurfacesOptions")
0347                     .def(py::init<>());
0348     ACTS_PYTHON_STRUCT(isco, gridOptions);
0349   }
0350 
0351   
0352   {
0353     auto c = py::class_<Svg::DetectorVolumeConverter::Options>(
0354                  svg, "DetectorVolumeOptions")
0355                  .def(py::init<>());
0356 
0357     ACTS_PYTHON_STRUCT(c, portalIndices, portalOptions, surfaceOptions,
0358                        indexedSurfacesOptions);
0359 
0360     
0361     
0362     svg.def("convertDetectorVolume", &Svg::DetectorVolumeConverter::convert);
0363 
0364     
0365     svg.def("drawDetectorVolume", &drawDetectorVolume);
0366   }
0367 
0368   
0369   {
0370     svg.def("drawIndexedSurfaces",
0371             [](const Svg::ProtoIndexedSurfaceGrid& pIndexedSurfaceGrid,
0372                const std::string& identification) {
0373               return Svg::View::xy(pIndexedSurfaceGrid, identification);
0374             });
0375   }
0376 
0377   
0378   { svg.def("drawDetector", &drawDetector); }
0379 
0380   { svg.def("drawSurfaceArrays", &Svg::drawSurfaceArrays); }
0381 
0382   
0383   {
0384     using DefinedStyle = std::pair<GeometryIdentifier, Svg::Style>;
0385     using DefinedStyleSet = std::vector<DefinedStyle>;
0386 
0387     auto sm = py::class_<GeometryHierarchyMap<Svg::Style>>(svg, "StyleMap")
0388                   .def(py::init<DefinedStyleSet>(), py::arg("elements"));
0389 
0390     auto c = py::class_<Svg::LayerConverter::Options>(svg, "LayerOptions")
0391                  .def(py::init<>());
0392     ACTS_PYTHON_STRUCT(c, name, surfaceStyles, zRange, phiRange, gridInfo,
0393                        moduleInfo, projectionInfo, labelProjection, labelGauge);
0394   }
0395 
0396   {
0397     using DefinedLayerOptions =
0398         std::pair<GeometryIdentifier, Svg::LayerConverter::Options>;
0399     using DefinedLayerOptionsSet = std::vector<DefinedLayerOptions>;
0400 
0401     auto lom =
0402         py::class_<GeometryHierarchyMap<Svg::LayerConverter::Options>>(
0403             svg, "LayerOptionMap")
0404             .def(py::init<DefinedLayerOptionsSet>(), py::arg("elements"));
0405 
0406     auto c = py::class_<Svg::TrackingGeometryConverter::Options>(
0407                  svg, "TrackingGeometryOptions")
0408                  .def(py::init<>());
0409     ACTS_PYTHON_STRUCT(c, prefix, layerOptions);
0410   }
0411 
0412   
0413 
0414   {
0415     using Writer = SvgTrackingGeometryWriter;
0416     auto w =
0417         py::class_<Writer, std::shared_ptr<Writer>>(mex,
0418                                                     "SvgTrackingGeometryWriter")
0419             .def(py::init<const Writer::Config&, Logging::Level>(),
0420                  py::arg("config"), py::arg("level"))
0421             .def("write",
0422                  py::overload_cast<const AlgorithmContext&,
0423                                    const TrackingGeometry&>(&Writer::write));
0424 
0425     auto c = py::class_<Writer::Config>(w, "Config").def(py::init<>());
0426     ACTS_PYTHON_STRUCT(c, outputDir, converterOptions);
0427   }
0428   {
0429     using Writer = SvgPointWriter<SimSpacePoint>;
0430     auto w =
0431         py::class_<Writer, IWriter, std::shared_ptr<Writer>>(
0432             mex, "SvgSimSpacePointWriter")
0433             .def(py::init<const Writer::Config&, Logging::Level>(),
0434                  py::arg("config"), py::arg("level"))
0435             .def("write",
0436                  py::overload_cast<const AlgorithmContext&>(&Writer::write));
0437 
0438     auto c = py::class_<Writer::Config>(w, "Config").def(py::init<>());
0439     ACTS_PYTHON_STRUCT(c, writerName, trackingGeometry, inputCollection,
0440                        infoBoxTitle, outputDir);
0441   }
0442 
0443   {
0444     using Writer = SvgPointWriter<SimHit, AccessorPositionXYZ>;
0445     auto w =
0446         py::class_<Writer, IWriter, std::shared_ptr<Writer>>(mex,
0447                                                              "SvgSimHitWriter")
0448             .def(py::init<const Writer::Config&, Logging::Level>(),
0449                  py::arg("config"), py::arg("level"))
0450             .def("write",
0451                  py::overload_cast<const AlgorithmContext&>(&Writer::write));
0452 
0453     auto c = py::class_<Writer::Config>(w, "Config").def(py::init<>());
0454     ACTS_PYTHON_STRUCT(c, writerName, trackingGeometry, inputCollection,
0455                        infoBoxTitle, outputDir);
0456   }
0457 
0458   svg.def(
0459       "drawTrackingGeometry",
0460       [](const GeometryContext& gctx, const TrackingGeometry& tGeometry,
0461          const std::string& view, bool drawSurfaces, bool highlightMaterial) {
0462         std::variant<actsvg::views::x_y, actsvg::views::z_r> v;
0463         if (view == "xy") {
0464           v = actsvg::views::x_y();
0465         } else if (view == "zr") {
0466           v = actsvg::views::z_r();
0467         } else {
0468           throw std::invalid_argument("Unknown view type");
0469         }
0470 
0471         return Svg::drawTrackingGeometry(gctx, tGeometry, v, drawSurfaces,
0472                                          highlightMaterial);
0473       },
0474       py::arg("gctx"), py::arg("tGeometry"), py::arg("view"),
0475       py::arg("drawSurfaces") = true, py::arg("highlightMaterial") = false);
0476 }
0477 }