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