Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-09 07:50:43

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/Plugins/ActSVG/TrackingGeometrySvgConverter.hpp"
0010 
0011 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0012 #include "Acts/Geometry/GeometryContext.hpp"
0013 #include "Acts/Geometry/GridPortalLink.hpp"
0014 #include "Acts/Geometry/TrackingGeometry.hpp"
0015 #include "Acts/Geometry/TrackingVolume.hpp"
0016 #include "Acts/Plugins/ActSVG/DetectorVolumeSvgConverter.hpp"
0017 #include "Acts/Plugins/ActSVG/PortalSvgConverter.hpp"
0018 #include "Acts/Plugins/ActSVG/SurfaceSvgConverter.hpp"
0019 #include "Acts/Utilities/Zip.hpp"
0020 
0021 #include <sstream>
0022 #include <stdexcept>
0023 
0024 namespace Acts::Svg {
0025 
0026 std::vector<actsvg::svg::object> TrackingGeometryConverter::convert(
0027     const GeometryContext& gctx, const TrackingGeometry& tGeometry,
0028     const TrackingGeometryConverter::Options& cOptions) {
0029   // Get the world volume
0030   const TrackingVolume* world = tGeometry.highestTrackingVolume();
0031 
0032   // Initiate the cache
0033   TrackingGeometryConverter::State cState;
0034 
0035   // Run the conversion recursively
0036   convert(gctx, *world, cOptions, cState);
0037 
0038   // Digest the views and globals
0039   std::vector<actsvg::svg::object> finalViews = cState.finalViews;
0040   if (!cState.xyCrossSection.empty()) {
0041     finalViews.push_back(
0042         Acts::Svg::group(cState.xyCrossSection, cOptions.prefix + "layers_xy"));
0043   }
0044   if (!cState.zrCrossSection.empty()) {
0045     finalViews.push_back(
0046         Acts::Svg::group(cState.zrCrossSection, cOptions.prefix + "layers_zr"));
0047   }
0048   // return all final Views
0049   return finalViews;
0050 }
0051 
0052 void TrackingGeometryConverter::convert(
0053     const GeometryContext& gctx, const TrackingVolume& tVolume,
0054     const TrackingGeometryConverter::Options& cOptions,
0055     TrackingGeometryConverter::State& cState) {
0056   // Process confined layers first
0057   if (tVolume.confinedLayers() != nullptr) {
0058     for (const auto& layer : tVolume.confinedLayers()->arrayObjects()) {
0059       if (layer->surfaceArray() != nullptr) {
0060         GeometryIdentifier geoID = layer->geometryId();
0061         std::string layerName = cOptions.prefix + "vol_" +
0062                                 std::to_string(geoID.volume()) + "_layer_" +
0063                                 std::to_string(geoID.layer());
0064 
0065         LayerConverter::Options lOptions;
0066         // Search for predefined layer options
0067         auto deflOptions = cOptions.layerOptions.find(geoID);
0068         if (deflOptions != cOptions.layerOptions.end()) {
0069           lOptions = (*deflOptions);
0070           lOptions.name = layerName;
0071         }
0072         // Retrieve the layer sheets
0073         auto layerSheets = LayerConverter::convert(gctx, *layer, lOptions);
0074 
0075         // Record the sheets
0076         for (const auto& lSheet : layerSheets) {
0077           if (lSheet.is_defined()) {
0078             cState.finalViews.push_back(lSheet);
0079           }
0080         }
0081         // Collect the xy views
0082         if (layerSheets[LayerConverter::eCrossSectionXY].is_defined() &&
0083             layer->surfaceRepresentation().type() == Acts::Surface::Cylinder) {
0084           cState.xyCrossSection.push_back(
0085               layerSheets[LayerConverter::eCrossSectionXY]);
0086         }
0087         // Collect the zr views
0088         if (layerSheets[LayerConverter::eCrossSectionZR].is_defined()) {
0089           cState.zrCrossSection.push_back(
0090               layerSheets[LayerConverter::eCrossSectionZR]);
0091         }
0092       }
0093     }
0094   }
0095 
0096   // Run recursively over confined volumes
0097   if (tVolume.confinedVolumes() != nullptr) {
0098     for (const auto& volume : tVolume.confinedVolumes()->arrayObjects()) {
0099       convert(gctx, *volume, cOptions, cState);
0100     }
0101   }
0102 }
0103 
0104 std::array<actsvg::svg::object, 2> TrackingGeometryProjections::convert(
0105     const GeometryContext& gctx, const Acts::TrackingGeometry& tGeometry,
0106     const TrackingGeometryProjections::Options& cOptions) {
0107   // The projections
0108   actsvg::svg::object xyView;
0109   actsvg::svg::object zrView;
0110 
0111   // Get the world volume
0112   const Acts::TrackingVolume* world = tGeometry.highestTrackingVolume();
0113   if (world != nullptr) {
0114     // Initiate the cache
0115     Acts::Svg::TrackingGeometryConverter::State cState;
0116 
0117     // Run the conversion recursively
0118     Acts::Svg::TrackingGeometryConverter::convert(
0119         gctx, *world, cOptions.trackingGeometryOptions, cState);
0120 
0121     xyView = Acts::Svg::group(cState.xyCrossSection,
0122                               cOptions.prefix + "projection_xy");
0123     zrView = Acts::Svg::group(cState.zrCrossSection,
0124                               cOptions.prefix + "projection_zr");
0125   }
0126   return {xyView, zrView};
0127 }
0128 
0129 // Gen3
0130 
0131 namespace {
0132 void convertPortalLink(const GeometryContext& gctx,
0133                        const PortalLinkBase& portalLink, Direction direction,
0134                        std::vector<Svg::ProtoPortal::link>& links) {
0135   auto getCenters = [&](const GridPortalLink& grid, AxisDirection axis0) {
0136     assert((grid.dim() == 2 || grid.dim() == 1) &&
0137            "Grid has unexpected number of dimension");
0138 
0139     std::vector<Vector2> centers;
0140 
0141     const auto localBins = grid.grid().numLocalBinsAny();
0142     if (grid.dim() == 2) {
0143       for (std::size_t i = 1; i <= localBins[0]; ++i) {
0144         for (std::size_t j = 1; j <= localBins[1]; ++j) {
0145           auto center = grid.grid().binCenterAny({i, j});
0146           assert(center.size() == 2);
0147           centers.push_back(Vector2(center[0], center[1]));
0148         }
0149       }
0150     } else {
0151       for (std::size_t i = 1; i <= localBins[0]; ++i) {
0152         auto center = grid.grid().binCenterAny({i});
0153         assert(center.size() == 1);
0154         if (axis0 == grid.direction()) {
0155           centers.push_back(Vector2(center[0], 0.));
0156         } else {
0157           centers.push_back(Vector2(0., center[0]));
0158         }
0159       }
0160     }
0161 
0162     return centers;
0163   };
0164 
0165   if (const auto* discBounds =
0166           dynamic_cast<const DiscBounds*>(&portalLink.surface().bounds());
0167       discBounds != nullptr) {
0168     if (const auto* grid = dynamic_cast<const GridPortalLink*>(&portalLink);
0169         grid != nullptr) {
0170       std::vector<Vector2> centers = getCenters(*grid, AxisDirection::AxisR);
0171       for (const auto& center : centers) {
0172         Svg::ProtoPortal::link link;
0173         link._start = portalLink.surface().localToGlobal(gctx, center);
0174         Vector3 normal = portalLink.surface().normal(gctx, link._start);
0175         link._end = link._start + normal * 10. * direction.sign();
0176         links.push_back(link);
0177       }
0178     } else {
0179       double rMin = discBounds->rMin();
0180       double rMax = discBounds->rMax();
0181       double rMid = 0.5 * (rMin + rMax);
0182 
0183       Svg::ProtoPortal::link link;
0184       link._start = portalLink.surface().center(gctx) + Vector3(rMid, 0., 0.);
0185       Vector3 normal = portalLink.surface().normal(gctx, link._start);
0186       link._end = link._start + normal * 10. * direction.sign();
0187       links.push_back(link);
0188     }
0189 
0190   } else if (const auto* cylBounds = dynamic_cast<const CylinderBounds*>(
0191                  &portalLink.surface().bounds());
0192              cylBounds != nullptr) {
0193     if (const auto* grid = dynamic_cast<const GridPortalLink*>(&portalLink);
0194         grid != nullptr) {
0195       std::vector<Vector2> centers = getCenters(*grid, AxisDirection::AxisRPhi);
0196       for (const auto& center : centers) {
0197         Svg::ProtoPortal::link link;
0198         link._start = portalLink.surface().localToGlobal(gctx, center);
0199         Vector3 normal = portalLink.surface().normal(gctx, link._start);
0200         link._end = link._start + normal * 10. * direction.sign();
0201         links.push_back(link);
0202       }
0203     } else {
0204       double r = cylBounds->get(CylinderBounds::eR);
0205       Svg::ProtoPortal::link link;
0206       link._start = portalLink.surface().center(gctx) + Vector3(r, 0., 0.);
0207       Vector3 normal = portalLink.surface().normal(gctx, link._start);
0208       link._end = link._start + normal * 10. * direction.sign();
0209       links.push_back(link);
0210     }
0211   } else {
0212     throw std::invalid_argument("Unknown bounds type");
0213   }
0214 }
0215 
0216 struct Visitor : TrackingGeometryVisitor {
0217   explicit Visitor(const GeometryContext& gctxIn) : gctx(gctxIn) {}
0218 
0219   void visitSurface(const Surface& surface) override {
0220     auto proto = Svg::SurfaceConverter::convert(gctx, surface, {});
0221     surfaces.push_back(std::pair{&surface, proto});
0222   }
0223 
0224   void visitPortal(const Portal& portal) override {
0225     auto pIt = std::ranges::find_if(
0226         portals, [&](const auto& p) { return p.first == &portal; });
0227 
0228     Svg::ProtoPortal pPortal;
0229     if (pIt == portals.end()) {
0230       pPortal._name = "portal_" + std::to_string(portals.size());
0231       auto surface = Svg::SurfaceConverter::convert(gctx, portal.surface(), {});
0232       pPortal._surface = surface;
0233 
0234       if (auto link = portal.getLink(Direction::AlongNormal());
0235           link != nullptr) {
0236         convertPortalLink(gctx, *link, Direction::AlongNormal(),
0237                           pPortal._volume_links);
0238       }
0239 
0240       if (auto link = portal.getLink(Direction::OppositeNormal());
0241           link != nullptr) {
0242         convertPortalLink(gctx, *link, Direction::OppositeNormal(),
0243                           pPortal._volume_links);
0244       }
0245       portals.push_back(std::pair{&portal, pPortal});
0246     } else {
0247       pPortal = pIt->second;
0248     }
0249 
0250     auto& [volume, pVolume] = volumes.back();
0251     pVolume._portals.push_back(pPortal);
0252   }
0253 
0254   void visitVolume(const TrackingVolume& volume) override {
0255     Svg::ProtoVolume pVolume;
0256     pVolume._name = volume.volumeName();
0257 
0258     auto volumeTransform = volume.transform();
0259 
0260     // https://github.com/acts-project/actsvg/blob/2f1aaa58365a1dd1af62dc27aea5039459a65a38/meta/include/actsvg/display/geometry.hpp#L687-L692
0261     enum svgBv : unsigned int {
0262       rInner = 0u,
0263       rOuter = 1u,
0264       zPos = 2u,
0265       zHalf = 3u,
0266       phiSec = 4u,
0267       avgPhi = 5u,
0268     };
0269 
0270     using enum CylinderVolumeBounds::BoundValues;
0271     const auto& boundValues = volume.volumeBounds().values();
0272     if (volume.volumeBounds().type() == Acts::VolumeBounds::eCylinder) {
0273       pVolume._bound_values.resize(6);
0274       pVolume._bound_values.at(svgBv::rInner) =
0275           static_cast<actsvg::scalar>(boundValues[eMinR]);
0276       pVolume._bound_values.at(svgBv::rOuter) =
0277           static_cast<actsvg::scalar>(boundValues[eMaxR]);
0278       pVolume._bound_values.at(svgBv::zPos) =
0279           static_cast<actsvg::scalar>(volumeTransform.translation().z());
0280       pVolume._bound_values.at(svgBv::zHalf) =
0281           static_cast<actsvg::scalar>(boundValues[eHalfLengthZ]);
0282       pVolume._bound_values.at(svgBv::phiSec) =
0283           static_cast<actsvg::scalar>(boundValues[eHalfPhiSector]);
0284       pVolume._bound_values.at(svgBv::avgPhi) =
0285           static_cast<actsvg::scalar>(boundValues[eAveragePhi]);
0286     } else {
0287       throw std::invalid_argument("Unknown bounds type");
0288     }
0289 
0290     volumes.push_back(std::pair{&volume, pVolume});
0291   }
0292 
0293   const GeometryContext& gctx;
0294 
0295   std::vector<std::pair<const Portal*, Svg::ProtoPortal>> portals;
0296 
0297   std::vector<std::pair<const TrackingVolume*, Svg::ProtoVolume>> volumes;
0298 
0299   std::vector<std::pair<const Surface*, Svg::ProtoSurface>> surfaces;
0300 };
0301 }  // namespace
0302 
0303 std::vector<actsvg::svg::object> drawTrackingGeometry(
0304     const GeometryContext& gctx, const TrackingGeometry& tGeometry,
0305     std::variant<actsvg::views::x_y, actsvg::views::z_r> view,
0306     bool drawSurfaces, bool highlightMaterial) {
0307   if (tGeometry.geometryVersion() != TrackingGeometry::GeometryVersion::Gen3) {
0308     throw std::invalid_argument{
0309         "Input tracking geometry needs to have been built in Gen3 mode"};
0310   }
0311 
0312   Visitor visitor(gctx);
0313   tGeometry.apply(visitor);
0314 
0315   std::vector<actsvg::svg::object> objects;
0316 
0317   std::visit(
0318       [&](auto& _view) {
0319         for (const auto& [tv, volume] : visitor.volumes) {
0320           std::string id = tv->volumeName();
0321           auto object = actsvg::display::volume(id, volume, _view);
0322           objects.push_back(object);
0323 
0324           std::vector<std::string> lines;
0325 
0326           std::stringstream ss;
0327           ss << "ID: " << tv->geometryId();
0328           lines.push_back(ss.str());
0329 
0330           ss.str("");
0331           ss << tv->volumeBounds();
0332           std::string bounds = ss.str();
0333           // split at first space after 40 characters
0334           ss.str("");
0335           for (std::size_t i = 0; i < bounds.size(); ++i) {
0336             ss << bounds[i];
0337             if (ss.str().size() > 40 && bounds[i] == ' ') {
0338               lines.push_back(ss.str());
0339               ss.str("");
0340             }
0341           }
0342 
0343           auto text = actsvg::draw::connected_info_box(
0344               "info_volume_" + volume._name, {0, 0}, volume._name,
0345               {{._rgb{200, 200, 200}}}, {._fc{._rgb{0, 0, 0}}, ._size = 24},
0346               lines, {{._rgb{220, 220, 220}}},
0347               {._fc{._rgb{0, 0, 0}}, ._size = 24}, {}, object);
0348 
0349           objects.push_back(text);
0350         }
0351 
0352         if (drawSurfaces) {
0353           for (const auto& [surface, proto] : visitor.surfaces) {
0354             std::stringstream ss;
0355             ss << surface->geometryId();
0356             auto object = actsvg::display::surface(ss.str(), proto, _view);
0357 
0358             if (highlightMaterial && surface->surfaceMaterial() != nullptr) {
0359               object._stroke._sc._rgb = {255, 0, 0};
0360               object._stroke._width = 1.5;
0361             }
0362 
0363             objects.push_back(object);
0364           }
0365         }
0366       },
0367       view);
0368 
0369   return objects;
0370 }
0371 
0372 }  // namespace Acts::Svg