File indexing completed on 2025-07-05 08:12:08
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/PortalLinkBase.hpp"
0016 #include "Acts/Geometry/TrackingGeometry.hpp"
0017 #include "Acts/Geometry/TrackingVolume.hpp"
0018 #include "Acts/Plugins/ActSVG/DetectorSvgConverter.hpp"
0019 #include "Acts/Plugins/ActSVG/DetectorVolumeSvgConverter.hpp"
0020 #include "Acts/Plugins/ActSVG/IndexedSurfacesSvgConverter.hpp"
0021 #include "Acts/Plugins/ActSVG/LayerSvgConverter.hpp"
0022 #include "Acts/Plugins/ActSVG/PortalSvgConverter.hpp"
0023 #include "Acts/Plugins/ActSVG/SurfaceSvgConverter.hpp"
0024 #include "Acts/Plugins/ActSVG/SvgUtils.hpp"
0025 #include "Acts/Plugins/ActSVG/TrackingGeometrySvgConverter.hpp"
0026 #include "Acts/Plugins/Python/Utilities.hpp"
0027 #include "Acts/Surfaces/CylinderBounds.hpp"
0028 #include "Acts/Surfaces/DiscBounds.hpp"
0029 #include "Acts/Utilities/Enumerate.hpp"
0030 #include "Acts/Utilities/Helpers.hpp"
0031 #include "ActsExamples/EventData/GeometryContainers.hpp"
0032 #include "ActsExamples/EventData/SimHit.hpp"
0033 #include "ActsExamples/EventData/SimSpacePoint.hpp"
0034 #include "ActsExamples/Io/Svg/SvgPointWriter.hpp"
0035 #include "ActsExamples/Io/Svg/SvgTrackingGeometryWriter.hpp"
0036 #include <actsvg/core/draw.hpp>
0037
0038 #include <algorithm>
0039 #include <memory>
0040 #include <ranges>
0041 #include <sstream>
0042 #include <string>
0043 #include <tuple>
0044 #include <vector>
0045
0046 #include <pybind11/pybind11.h>
0047 #include <pybind11/stl.h>
0048
0049 namespace py = pybind11;
0050 using namespace pybind11::literals;
0051
0052 using namespace Acts;
0053 using namespace ActsExamples;
0054
0055 namespace {
0056
0057
0058 using PortalCache = std::list<std::string>;
0059
0060
0061 bool viewRangeSel(const Svg::ProtoSurface& s, const Extent& vRange) {
0062 for (const auto& v : s._vertices) {
0063 if (vRange.contains(v)) {
0064 return true;
0065 }
0066 }
0067
0068 return false;
0069 };
0070
0071
0072
0073
0074
0075 using ViewAndRange =
0076 std::tuple<std::string, std::vector<std::string>, Acts::Extent>;
0077
0078
0079
0080
0081
0082
0083
0084
0085
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
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
0103 auto materialSel = [&](const Svg::ProtoSurface& s) -> bool {
0104 return (materials && s._decorations.contains("material"));
0105 };
0106
0107
0108
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
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
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
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
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
0165 std::vector<actsvg::svg::object> drawDetector(
0166 const Acts::GeometryContext& gctx,
0167 const Acts::Experimental::Detector& detector,
0168 const std::string& identification,
0169 const std::vector<std::tuple<int, Svg::DetectorVolumeConverter::Options>>&
0170 volumeIdxOpts,
0171 const std::vector<ViewAndRange>& viewAndRanges) {
0172 PortalCache portalCache;
0173
0174
0175 std::vector<actsvg::svg::object> svgDetViews;
0176 svgDetViews.reserve(viewAndRanges.size());
0177 for (unsigned int i = 0; i < viewAndRanges.size(); ++i) {
0178 actsvg::svg::object svgDet;
0179 svgDet._id = identification;
0180 svgDet._tag = "g";
0181 svgDetViews.push_back(svgDet);
0182 }
0183
0184 for (const auto& [vidx, vopts] : volumeIdxOpts) {
0185
0186 const auto& v = detector.volumes()[vidx];
0187 auto [pVolume, pGrid] =
0188 Svg::DetectorVolumeConverter::convert(gctx, *v, vopts);
0189
0190 for (auto [iv, var] : Acts::enumerate(viewAndRanges)) {
0191 auto [view, selection, range] = var;
0192
0193 auto svgVolView = drawDetectorVolume(
0194 pVolume, identification + "_vol" + std::to_string(vidx) + "_" + view,
0195 var, portalCache);
0196 svgDetViews[iv].add_object(svgVolView);
0197 }
0198 }
0199 return svgDetViews;
0200 }
0201
0202 }
0203
0204 namespace Acts::Python {
0205 void addSvg(Context& ctx) {
0206 auto [m, mex] = ctx.get("main", "examples");
0207
0208 auto svg = m.def_submodule("svg");
0209
0210 svg.def("toFile", &Svg::toFile);
0211
0212
0213 {
0214 auto c = py::class_<Svg::Style>(svg, "Style").def(py::init<>());
0215 ACTS_PYTHON_STRUCT(c, fillColor, fillOpacity, highlightColor, highlights,
0216 strokeWidth, strokeColor, highlightStrokeWidth,
0217 highlightStrokeColor, fontSize, fontColor,
0218 quarterSegments);
0219 }
0220
0221
0222 {
0223 auto c = py::class_<Svg::SurfaceConverter::Options>(svg, "SurfaceOptions")
0224 .def(py::init<>());
0225 ACTS_PYTHON_STRUCT(c, style, templateSurface);
0226
0227
0228 py::class_<Svg::ProtoSurface>(svg, "ProtoSurface");
0229
0230
0231 svg.def("convertSurface", &Svg::SurfaceConverter::convert);
0232
0233
0234 svg.def("viewSurface", [](const Svg::ProtoSurface& pSurface,
0235 const std::string& identification,
0236 const std::string& view = "xy") {
0237 if (view == "xy") {
0238 return Svg::View::xy(pSurface, identification);
0239 } else if (view == "zr") {
0240 return Svg::View::zr(pSurface, identification);
0241 } else if (view == "zphi") {
0242 return Svg::View::zphi(pSurface, identification);
0243 } else if (view == "zrphi") {
0244 return Svg::View::zrphi(pSurface, identification);
0245 } else {
0246 throw std::invalid_argument("Unknown view type");
0247 }
0248 });
0249 }
0250
0251
0252 {
0253 auto c = py::class_<Svg::PortalConverter::Options>(svg, "PortalOptions")
0254 .def(py::init<>());
0255
0256 ACTS_PYTHON_STRUCT(c, surfaceOptions, linkLength, volumeIndices);
0257
0258
0259 py::class_<Svg::ProtoPortal>(svg, "ProtoPortal");
0260
0261
0262 svg.def("convertPortal", &Svg::PortalConverter::convert);
0263
0264
0265 svg.def("viewPortal", [](const Svg::ProtoPortal& pPortal,
0266 const std::string& identification,
0267 const std::string& view = "xy") {
0268 if (view == "xy") {
0269 return Svg::View::xy(pPortal, identification);
0270 } else if (view == "zr") {
0271 return Svg::View::zr(pPortal, identification);
0272 } else {
0273 throw std::invalid_argument("Unknown view type");
0274 }
0275 });
0276 }
0277
0278
0279 {
0280 svg.def("drawArrow", &actsvg::draw::arrow);
0281
0282 svg.def("drawText", &actsvg::draw::text);
0283
0284 svg.def("drawInfoBox", &Svg::infoBox);
0285 }
0286
0287
0288 {
0289 svg.def(
0290 "drawEtaLines",
0291 [](const std::string& id, actsvg ::scalar z, actsvg::scalar r,
0292 const std::vector<actsvg::scalar>& etaMain,
0293 actsvg::scalar strokeWidthMain, unsigned int sizeMain,
0294 bool labelMain, const std::vector<actsvg::scalar>& etaSub,
0295 actsvg::scalar strokeWidthSub, const std::vector<int> strokeDashSub,
0296 unsigned int sizeSub, bool labelSub) {
0297
0298 actsvg::style::stroke strokeMain;
0299 strokeMain._width = strokeWidthMain;
0300 actsvg::style::font fontMain;
0301 fontMain._size = sizeMain;
0302
0303 actsvg::style::stroke strokeSub;
0304 strokeSub._width = strokeWidthSub;
0305 strokeSub._dasharray = strokeDashSub;
0306 actsvg::style::font fontSub;
0307 fontSub._size = sizeSub;
0308
0309 return actsvg::display::eta_lines(
0310 id, z, r,
0311 {std::tie(etaMain, strokeMain, labelMain, fontMain),
0312 std::tie(etaSub, strokeSub, labelSub, fontSub)});
0313 });
0314 }
0315
0316 {
0317 auto gco = py::class_<Svg::GridConverter::Options>(svg, "GridOptions")
0318 .def(py::init<>());
0319 ACTS_PYTHON_STRUCT(gco, style);
0320
0321 auto isco = py::class_<Svg::IndexedSurfacesConverter::Options>(
0322 svg, "IndexedSurfacesOptions")
0323 .def(py::init<>());
0324 ACTS_PYTHON_STRUCT(isco, gridOptions);
0325 }
0326
0327
0328 {
0329 auto c = py::class_<Svg::DetectorVolumeConverter::Options>(
0330 svg, "DetectorVolumeOptions")
0331 .def(py::init<>());
0332
0333 ACTS_PYTHON_STRUCT(c, portalIndices, portalOptions, surfaceOptions,
0334 indexedSurfacesOptions);
0335
0336
0337
0338 svg.def("convertDetectorVolume", &Svg::DetectorVolumeConverter::convert);
0339
0340
0341 svg.def("drawDetectorVolume", &drawDetectorVolume);
0342 }
0343
0344
0345 {
0346 svg.def("drawIndexedSurfaces",
0347 [](const Svg::ProtoIndexedSurfaceGrid& pIndexedSurfaceGrid,
0348 const std::string& identification) {
0349 return Svg::View::xy(pIndexedSurfaceGrid, identification);
0350 });
0351 }
0352
0353
0354 { svg.def("drawDetector", &drawDetector); }
0355
0356
0357 {
0358 using DefinedStyle = std::pair<GeometryIdentifier, Svg::Style>;
0359 using DefinedStyleSet = std::vector<DefinedStyle>;
0360
0361 auto sm = py::class_<GeometryHierarchyMap<Svg::Style>>(svg, "StyleMap")
0362 .def(py::init<DefinedStyleSet>(), py::arg("elements"));
0363
0364 auto c = py::class_<Svg::LayerConverter::Options>(svg, "LayerOptions")
0365 .def(py::init<>());
0366 ACTS_PYTHON_STRUCT(c, name, surfaceStyles, zRange, phiRange, gridInfo,
0367 moduleInfo, projectionInfo, labelProjection, labelGauge);
0368 }
0369
0370 {
0371 using DefinedLayerOptions =
0372 std::pair<GeometryIdentifier, Svg::LayerConverter::Options>;
0373 using DefinedLayerOptionsSet = std::vector<DefinedLayerOptions>;
0374
0375 auto lom =
0376 py::class_<GeometryHierarchyMap<Svg::LayerConverter::Options>>(
0377 svg, "LayerOptionMap")
0378 .def(py::init<DefinedLayerOptionsSet>(), py::arg("elements"));
0379
0380 auto c = py::class_<Svg::TrackingGeometryConverter::Options>(
0381 svg, "TrackingGeometryOptions")
0382 .def(py::init<>());
0383 ACTS_PYTHON_STRUCT(c, prefix, layerOptions);
0384 }
0385
0386
0387
0388 {
0389 using Writer = ActsExamples::SvgTrackingGeometryWriter;
0390 auto w = py::class_<Writer, std::shared_ptr<Writer>>(
0391 mex, "SvgTrackingGeometryWriter")
0392 .def(py::init<const Writer::Config&, Acts::Logging::Level>(),
0393 py::arg("config"), py::arg("level"))
0394 .def("write", py::overload_cast<const AlgorithmContext&,
0395 const Acts::TrackingGeometry&>(
0396 &Writer::write));
0397
0398 auto c = py::class_<Writer::Config>(w, "Config").def(py::init<>());
0399 ACTS_PYTHON_STRUCT(c, outputDir, converterOptions);
0400 }
0401 {
0402 using Writer = ActsExamples::SvgPointWriter<ActsExamples::SimSpacePoint>;
0403 auto w =
0404 py::class_<Writer, ActsExamples::IWriter, std::shared_ptr<Writer>>(
0405 mex, "SvgSimSpacePointWriter")
0406 .def(py::init<const Writer::Config&, Acts::Logging::Level>(),
0407 py::arg("config"), py::arg("level"))
0408 .def("write",
0409 py::overload_cast<const AlgorithmContext&>(&Writer::write));
0410
0411 auto c = py::class_<Writer::Config>(w, "Config").def(py::init<>());
0412 ACTS_PYTHON_STRUCT(c, writerName, trackingGeometry, inputCollection,
0413 infoBoxTitle, outputDir);
0414 }
0415
0416 {
0417 using Writer =
0418 ActsExamples::SvgPointWriter<ActsExamples::SimHit,
0419 ActsExamples::AccessorPositionXYZ>;
0420 auto w =
0421 py::class_<Writer, ActsExamples::IWriter, std::shared_ptr<Writer>>(
0422 mex, "SvgSimHitWriter")
0423 .def(py::init<const Writer::Config&, Acts::Logging::Level>(),
0424 py::arg("config"), py::arg("level"))
0425 .def("write",
0426 py::overload_cast<const AlgorithmContext&>(&Writer::write));
0427
0428 auto c = py::class_<Writer::Config>(w, "Config").def(py::init<>());
0429 ACTS_PYTHON_STRUCT(c, writerName, trackingGeometry, inputCollection,
0430 infoBoxTitle, outputDir);
0431 }
0432
0433 svg.def(
0434 "drawTrackingGeometry",
0435 [](const GeometryContext& gctx, const TrackingGeometry& tGeometry,
0436 const std::string& view, bool drawSurfaces, bool highlightMaterial) {
0437 std::variant<actsvg::views::x_y, actsvg::views::z_r> v;
0438 if (view == "xy") {
0439 v = actsvg::views::x_y();
0440 } else if (view == "zr") {
0441 v = actsvg::views::z_r();
0442 } else {
0443 throw std::invalid_argument("Unknown view type");
0444 }
0445
0446 return Svg::drawTrackingGeometry(gctx, tGeometry, v, drawSurfaces,
0447 highlightMaterial);
0448 },
0449 py::arg("gctx"), py::arg("tGeometry"), py::arg("view"),
0450 py::arg("drawSurfaces") = true, py::arg("highlightMaterial") = false);
0451 }
0452 }