File indexing completed on 2026-05-11 08:01:08
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Visualization/GeometryView3D.hpp"
0010
0011 #include "Acts/Geometry/BoundarySurfaceT.hpp"
0012 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0013 #include "Acts/Geometry/Extent.hpp"
0014 #include "Acts/Geometry/GeometryIdentifier.hpp"
0015 #include "Acts/Geometry/Layer.hpp"
0016 #include "Acts/Geometry/Polyhedron.hpp"
0017 #include "Acts/Geometry/TrackingVolume.hpp"
0018 #include "Acts/Geometry/Volume.hpp"
0019 #include "Acts/Surfaces/ConeBounds.hpp"
0020 #include "Acts/Surfaces/ConeSurface.hpp"
0021 #include "Acts/Surfaces/CylinderBounds.hpp"
0022 #include "Acts/Surfaces/CylinderSurface.hpp"
0023 #include "Acts/Surfaces/DiscSurface.hpp"
0024 #include "Acts/Surfaces/RadialBounds.hpp"
0025 #include "Acts/Surfaces/Surface.hpp"
0026 #include "Acts/Surfaces/SurfaceArray.hpp"
0027 #include "Acts/Utilities/BinningType.hpp"
0028 #include "Acts/Utilities/IAxis.hpp"
0029 #include "Acts/Utilities/UnitVectors.hpp"
0030 #include "Acts/Visualization/IVisualization3D.hpp"
0031
0032 #include <algorithm>
0033 #include <cmath>
0034 #include <filesystem>
0035 #include <memory>
0036 #include <utility>
0037 #include <vector>
0038
0039 void Acts::GeometryView3D::drawPolyhedron(IVisualization3D& helper,
0040 const Polyhedron& polyhedron,
0041 const ViewConfig& viewConfig) {
0042 polyhedron.visualize(helper, viewConfig);
0043 }
0044
0045 void Acts::GeometryView3D::drawSurface(IVisualization3D& helper,
0046 const Surface& surface,
0047 const GeometryContext& gctx,
0048 const Transform3& ,
0049 const ViewConfig& viewConfig) {
0050 surface.visualize(helper, gctx, viewConfig);
0051 }
0052
0053 void Acts::GeometryView3D::drawSurfaceArray(
0054 IVisualization3D& helper, const SurfaceArray& surfaceArray,
0055 const GeometryContext& gctx, const Transform3& transform,
0056 const ViewConfig& sensitiveConfig, const ViewConfig& passiveConfig,
0057 const ViewConfig& gridConfig, const std::filesystem::path& outputDir) {
0058
0059 Extent arrayExtent;
0060 for (const auto& sf : surfaceArray.surfaces()) {
0061 ViewConfig vConfig = sf->isSensitive() ? sensitiveConfig : passiveConfig;
0062 drawSurface(helper, *sf, gctx, transform, vConfig);
0063 auto sfExtent = sf->polyhedronRepresentation(gctx, 1).extent();
0064 arrayExtent.extend(sfExtent);
0065 }
0066
0067 if (!sensitiveConfig.outputName.empty()) {
0068 helper.write(outputDir / sensitiveConfig.outputName);
0069 helper.clear();
0070 }
0071
0072 double thickness = gridConfig.lineThickness;
0073
0074 auto binning = surfaceArray.binningValues();
0075 auto axes = surfaceArray.getAxes();
0076 if (!binning.empty() && binning.size() == 2 && axes.size() == 2) {
0077
0078 if (binning[0] == AxisDirection::AxisPhi &&
0079 binning[1] == AxisDirection::AxisZ) {
0080 double R = arrayExtent.medium(AxisDirection::AxisR) + gridConfig.offset;
0081 auto phiValues = axes[0]->getBinEdges();
0082 auto zValues = axes[1]->getBinEdges();
0083 ViewConfig gridRadConfig = gridConfig;
0084
0085 for (auto phi : phiValues) {
0086 double cphi = std::cos(phi);
0087 double sphi = std::sin(phi);
0088 Vector3 p1(R * cphi, R * sphi, axes[1]->getMin());
0089 Vector3 p0(R * cphi, R * sphi, axes[1]->getMax());
0090 drawSegment(helper, transform * p0, transform * p1, gridConfig);
0091 }
0092 CylinderVolumeBounds cvb(R - 0.5 * thickness, R + 0.5 * thickness,
0093 0.5 * thickness);
0094 auto cvbOrientedSurfaces = cvb.orientedSurfaces();
0095 for (auto z : zValues) {
0096 for (const auto& cvbSf : cvbOrientedSurfaces) {
0097 drawSurface(helper, *cvbSf.surface, gctx,
0098 Translation3(0., 0., z) * transform, gridRadConfig);
0099 }
0100 }
0101
0102 } else if (binning[0] == AxisDirection::AxisR &&
0103 binning[1] == AxisDirection::AxisPhi) {
0104 double z = arrayExtent.medium(AxisDirection::AxisZ) + gridConfig.offset;
0105 auto rValues = axes[0]->getBinEdges();
0106 auto phiValues = axes[1]->getBinEdges();
0107 ViewConfig gridRadConfig = gridConfig;
0108 gridRadConfig.quarterSegments = phiValues.size();
0109 for (auto r : rValues) {
0110 CylinderVolumeBounds cvb(r - 0.5 * thickness, r + 0.5 * thickness,
0111 0.5 * thickness);
0112 auto cvbOrientedSurfaces = cvb.orientedSurfaces();
0113 for (const auto& cvbSf : cvbOrientedSurfaces) {
0114 drawSurface(helper, *cvbSf.surface, gctx,
0115 Translation3(0., 0., z) * transform, gridRadConfig);
0116 }
0117 }
0118 double rMin = axes[0]->getMin();
0119 double rMax = axes[0]->getMax();
0120 for (auto phi : phiValues) {
0121 double cphi = std::cos(phi);
0122 double sphi = std::sin(phi);
0123 Vector3 p1(rMax * cphi, rMax * sphi, z);
0124 Vector3 p0(rMin * cphi, rMin * sphi, z);
0125 drawSegment(helper, transform * p0, transform * p1, gridConfig);
0126 }
0127 }
0128 }
0129
0130 if (!gridConfig.outputName.empty()) {
0131 helper.write(outputDir / gridConfig.outputName);
0132 helper.clear();
0133 }
0134 }
0135
0136 void Acts::GeometryView3D::drawVolume(IVisualization3D& helper,
0137 const Volume& volume,
0138 const GeometryContext& gctx,
0139 const Transform3& ,
0140 const ViewConfig& viewConfig) {
0141 volume.visualize(helper, gctx, viewConfig);
0142 }
0143
0144 void Acts::GeometryView3D::drawLayer(
0145 IVisualization3D& helper, const Layer& layer, const GeometryContext& gctx,
0146 const ViewConfig& layerConfig, const ViewConfig& sensitiveConfig,
0147 const ViewConfig& gridConfig, const std::filesystem::path& outputDir) {
0148 if (layerConfig.visible) {
0149 auto layerVolume = layer.representingVolume();
0150 if (layerVolume != nullptr) {
0151 drawVolume(helper, *layerVolume, gctx, Transform3::Identity(),
0152 layerConfig);
0153 } else {
0154 const auto& layerSurface = layer.surfaceRepresentation();
0155 drawSurface(helper, layerSurface, gctx, Transform3::Identity(),
0156 layerConfig);
0157 }
0158 if (!layerConfig.outputName.empty()) {
0159 helper.write(outputDir / layerConfig.outputName);
0160 helper.clear();
0161 }
0162 }
0163
0164 if (sensitiveConfig.visible || gridConfig.visible) {
0165 auto surfaceArray = layer.surfaceArray();
0166 if (surfaceArray != nullptr) {
0167 drawSurfaceArray(helper, *surfaceArray, gctx, Transform3::Identity(),
0168 sensitiveConfig, layerConfig, gridConfig, outputDir);
0169 }
0170 }
0171 }
0172
0173 void Acts::GeometryView3D::drawTrackingVolume(
0174 IVisualization3D& helper, const TrackingVolume& tVolume,
0175 const GeometryContext& gctx, const ViewConfig& containerView,
0176 const ViewConfig& volumeView, const ViewConfig& layerView,
0177 const ViewConfig& sensitiveView, const ViewConfig& gridView, bool writeIt,
0178 const std::string& tag, const std::filesystem::path& outputDir) {
0179 if (tVolume.confinedVolumes() != nullptr) {
0180 const auto& subVolumes = tVolume.confinedVolumes()->arrayObjects();
0181 for (const auto& tv : subVolumes) {
0182 drawTrackingVolume(helper, *tv, gctx, containerView, volumeView,
0183 layerView, sensitiveView, gridView, writeIt, tag,
0184 outputDir);
0185 }
0186 }
0187
0188 ViewConfig cConfig = containerView;
0189 ViewConfig vConfig = volumeView;
0190 ViewConfig lConfig = layerView;
0191 ViewConfig sConfig = sensitiveView;
0192 ViewConfig gConfig = gridView;
0193 gConfig.quarterSegments = 8;
0194
0195 ViewConfig vcConfig = cConfig;
0196 std::string vname = tVolume.volumeName();
0197 if (writeIt) {
0198 std::vector<std::string> repChar = {"::" };
0199 for (const auto& rchar : repChar) {
0200 while (vname.find(rchar) != std::string::npos) {
0201 vname.replace(vname.find(rchar), rchar.size(), std::string("_"));
0202 }
0203 }
0204 if (tVolume.confinedVolumes() == nullptr) {
0205 vcConfig = vConfig;
0206 vcConfig.outputName =
0207 std::filesystem::path(vname + std::string("_boundaries") + tag);
0208 } else {
0209 std::vector<GeometryIdentifier::Value> ids{tVolume.geometryId().volume()};
0210
0211 for (const auto* current = &tVolume; current->motherVolume() != nullptr;
0212 current = current->motherVolume()) {
0213 ids.push_back(current->motherVolume()->geometryId().volume());
0214 }
0215
0216 std::ranges::reverse(ids);
0217 vname = "Container";
0218 for (const auto& id : ids) {
0219 vname += "_v" + std::to_string(id);
0220 }
0221
0222 vcConfig.outputName =
0223 std::filesystem::path(vname + std::string("_boundaries") + tag);
0224 }
0225 }
0226
0227 auto bSurfaces = tVolume.boundarySurfaces();
0228 for (const auto& bs : bSurfaces) {
0229 drawSurface(helper, bs->surfaceRepresentation(), gctx,
0230 Transform3::Identity(), vcConfig);
0231 }
0232 if (writeIt) {
0233 const std::filesystem::path outputName = outputDir / vcConfig.outputName;
0234 helper.write(outputName);
0235 helper.clear();
0236 }
0237
0238 if (tVolume.confinedLayers() != nullptr) {
0239 const auto& layers = tVolume.confinedLayers()->arrayObjects();
0240 std::size_t il = 0;
0241 for (const auto& tl : layers) {
0242 if (writeIt) {
0243 lConfig.outputName = std::filesystem::path(
0244 vname + std::string("_passives_l") + std::to_string(il) + tag);
0245 sConfig.outputName = std::filesystem::path(
0246 vname + std::string("_sensitives_l") + std::to_string(il) + tag);
0247 gConfig.outputName = std::filesystem::path(
0248 vname + std::string("_grids_l") + std::to_string(il) + tag);
0249 }
0250 drawLayer(helper, *tl, gctx, lConfig, sConfig, gConfig, outputDir);
0251 ++il;
0252 }
0253 }
0254 }
0255
0256 void Acts::GeometryView3D::drawSegmentBase(IVisualization3D& helper,
0257 const Vector3& start,
0258 const Vector3& end, int arrows,
0259 double arrowLength,
0260 double arrowWidth,
0261 const ViewConfig& viewConfig) {
0262 double thickness = viewConfig.lineThickness;
0263
0264
0265 auto direction = Vector3(end - start).normalized();
0266 double hlength = 0.5 * Vector3(end - start).norm();
0267
0268 auto unitVectors = createCurvilinearUnitVectors(direction);
0269 RotationMatrix3 lrotation;
0270 lrotation.col(0) = unitVectors.first;
0271 lrotation.col(1) = unitVectors.second;
0272 lrotation.col(2) = direction;
0273
0274 Vector3 lcenter = 0.5 * (start + end);
0275 double alength = (thickness > 0.) ? arrowLength * thickness : 2.;
0276 if (alength > hlength) {
0277 alength = hlength;
0278 }
0279
0280 if (arrows == 2) {
0281 hlength -= alength;
0282 } else if (arrows != 0) {
0283 hlength -= 0.5 * alength;
0284 lcenter -= Vector3(arrows * 0.5 * alength * direction);
0285 }
0286
0287
0288 if (thickness > 0.) {
0289 auto ltransform = Transform3::Identity();
0290 ltransform.prerotate(lrotation);
0291 ltransform.pretranslate(lcenter);
0292
0293 auto lbounds = std::make_shared<CylinderBounds>(thickness, hlength);
0294 auto line = Surface::makeShared<CylinderSurface>(ltransform, lbounds);
0295
0296 drawSurface(helper, *line, GeometryContext::dangerouslyDefaultConstruct(),
0297 Transform3::Identity(), viewConfig);
0298 } else {
0299 helper.line(start, end, viewConfig.color);
0300 }
0301
0302
0303 if (arrows != 0) {
0304 double awith = thickness * arrowWidth;
0305 double alpha = std::atan2(thickness * arrowWidth, alength);
0306 auto plateBounds = std::make_shared<RadialBounds>(thickness, awith);
0307
0308 if (arrows > 0) {
0309 auto aetransform = Transform3::Identity();
0310 aetransform.prerotate(lrotation);
0311 aetransform.pretranslate(end);
0312
0313 auto coneBounds = std::make_shared<ConeBounds>(alpha, -alength, 0.);
0314 auto cone = Surface::makeShared<ConeSurface>(aetransform, coneBounds);
0315 drawSurface(helper, *cone, GeometryContext::dangerouslyDefaultConstruct(),
0316 Transform3::Identity(), viewConfig);
0317
0318 auto aptransform = Transform3::Identity();
0319 aptransform.prerotate(lrotation);
0320 aptransform.pretranslate(Vector3(end - alength * direction));
0321
0322 auto plate = Surface::makeShared<DiscSurface>(aptransform, plateBounds);
0323 drawSurface(helper, *plate,
0324 GeometryContext::dangerouslyDefaultConstruct(),
0325 Transform3::Identity(), viewConfig);
0326 }
0327 if (arrows < 0 || arrows == 2) {
0328 auto astransform = Transform3::Identity();
0329 astransform.prerotate(lrotation);
0330 astransform.pretranslate(start);
0331
0332
0333 auto coneBounds = std::make_shared<ConeBounds>(alpha, 0., alength);
0334 auto cone = Surface::makeShared<ConeSurface>(astransform, coneBounds);
0335 drawSurface(helper, *cone, GeometryContext::dangerouslyDefaultConstruct(),
0336 Transform3::Identity(), viewConfig);
0337
0338 auto aptransform = Transform3::Identity();
0339 aptransform.prerotate(lrotation);
0340 aptransform.pretranslate(Vector3(start + alength * direction));
0341
0342 auto plate = Surface::makeShared<DiscSurface>(aptransform, plateBounds);
0343 drawSurface(helper, *plate,
0344 GeometryContext::dangerouslyDefaultConstruct(),
0345 Transform3::Identity(), viewConfig);
0346 }
0347 }
0348 }
0349
0350 void Acts::GeometryView3D::drawSegment(IVisualization3D& helper,
0351 const Vector3& start, const Vector3& end,
0352 const ViewConfig& viewConfig) {
0353 drawSegmentBase(helper, start, end, 0, 0., 0., viewConfig);
0354 }
0355
0356 void Acts::GeometryView3D::drawArrowBackward(
0357 IVisualization3D& helper, const Vector3& start, const Vector3& end,
0358 double arrowLength, double arrowWidth, const ViewConfig& viewConfig) {
0359 drawSegmentBase(helper, start, end, -1, arrowLength, arrowWidth, viewConfig);
0360 }
0361
0362 void Acts::GeometryView3D::drawArrowForward(
0363 IVisualization3D& helper, const Vector3& start, const Vector3& end,
0364 double arrowLength, double arrowWidth, const ViewConfig& viewConfig) {
0365 drawSegmentBase(helper, start, end, 1, arrowLength, arrowWidth, viewConfig);
0366 }
0367
0368 void Acts::GeometryView3D::drawArrowsBoth(IVisualization3D& helper,
0369 const Vector3& start,
0370 const Vector3& end,
0371 double arrowLength, double arrowWidth,
0372 const ViewConfig& viewConfig) {
0373 drawSegmentBase(helper, start, end, 2, arrowLength, arrowWidth, viewConfig);
0374 }