File indexing completed on 2025-12-17 09:11:59
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Visualization/GeometryView3D.hpp"
0010
0011 #include "Acts/Detector/DetectorVolume.hpp"
0012 #include "Acts/Detector/Portal.hpp"
0013 #include "Acts/Geometry/BoundarySurfaceT.hpp"
0014 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0015 #include "Acts/Geometry/Extent.hpp"
0016 #include "Acts/Geometry/GeometryIdentifier.hpp"
0017 #include "Acts/Geometry/Layer.hpp"
0018 #include "Acts/Geometry/Polyhedron.hpp"
0019 #include "Acts/Geometry/TrackingVolume.hpp"
0020 #include "Acts/Geometry/Volume.hpp"
0021 #include "Acts/Surfaces/ConeBounds.hpp"
0022 #include "Acts/Surfaces/ConeSurface.hpp"
0023 #include "Acts/Surfaces/CylinderBounds.hpp"
0024 #include "Acts/Surfaces/CylinderSurface.hpp"
0025 #include "Acts/Surfaces/DiscSurface.hpp"
0026 #include "Acts/Surfaces/RadialBounds.hpp"
0027 #include "Acts/Surfaces/Surface.hpp"
0028 #include "Acts/Surfaces/SurfaceArray.hpp"
0029 #include "Acts/Utilities/BinnedArray.hpp"
0030 #include "Acts/Utilities/BinningType.hpp"
0031 #include "Acts/Utilities/IAxis.hpp"
0032 #include "Acts/Utilities/UnitVectors.hpp"
0033 #include "Acts/Visualization/IVisualization3D.hpp"
0034
0035 #include <algorithm>
0036 #include <cmath>
0037 #include <memory>
0038 #include <ostream>
0039 #include <utility>
0040 #include <vector>
0041
0042 #include <limits.h>
0043 #include <unistd.h>
0044
0045 namespace {
0046
0047 std::string joinPaths(const std::string& a, const std::string& b) {
0048 if (b.substr(0, 1) == "/" || a.empty()) {
0049 return b;
0050 }
0051
0052 if (a.substr(a.size() - 1) == "/") {
0053 return a.substr(a.size() - 1) + "/" + b;
0054 }
0055
0056 return a + "/" + b;
0057 }
0058
0059 std::string getWorkingDirectory() {
0060 char buffer[PATH_MAX];
0061 return (getcwd(buffer, sizeof(buffer)) != nullptr ? std::string(buffer)
0062 : std::string(""));
0063 }
0064
0065 }
0066
0067 namespace Acts::Experimental {
0068 ViewConfig s_viewSensitive = ViewConfig({0, 180, 240});
0069 ViewConfig s_viewPassive = ViewConfig({240, 280, 0});
0070 ViewConfig s_viewVolume = ViewConfig({220, 220, 0});
0071 ViewConfig s_viewGrid = ViewConfig({220, 0, 0});
0072 ViewConfig s_viewLine = ViewConfig({0, 0, 220});
0073 }
0074
0075 void Acts::GeometryView3D::drawPolyhedron(IVisualization3D& helper,
0076 const Polyhedron& polyhedron,
0077 const ViewConfig& viewConfig) {
0078 if (viewConfig.visible) {
0079 if (!viewConfig.triangulate) {
0080 helper.faces(polyhedron.vertices, polyhedron.faces, viewConfig.color);
0081 } else {
0082 helper.faces(polyhedron.vertices, polyhedron.triangularMesh,
0083 viewConfig.color);
0084 }
0085 }
0086 }
0087
0088 void Acts::GeometryView3D::drawSurface(IVisualization3D& helper,
0089 const Surface& surface,
0090 const GeometryContext& gctx,
0091 const Transform3& transform,
0092 const ViewConfig& viewConfig) {
0093 Polyhedron surfaceHedron =
0094 surface.polyhedronRepresentation(gctx, viewConfig.nSegments);
0095 if (!transform.isApprox(Transform3::Identity())) {
0096 surfaceHedron.move(transform);
0097 }
0098 drawPolyhedron(helper, surfaceHedron, viewConfig);
0099 }
0100
0101 void Acts::GeometryView3D::drawSurfaceArray(
0102 IVisualization3D& helper, const SurfaceArray& surfaceArray,
0103 const GeometryContext& gctx, const Transform3& transform,
0104 const ViewConfig& sensitiveConfig, const ViewConfig& passiveConfig,
0105 const ViewConfig& gridConfig, const std::string& _outputDir) {
0106 std::string outputDir =
0107 _outputDir == "." ? getWorkingDirectory() : _outputDir;
0108
0109 Extent arrayExtent;
0110 for (const auto& sf : surfaceArray.surfaces()) {
0111 ViewConfig vConfig = sf->associatedDetectorElement() != nullptr
0112 ? sensitiveConfig
0113 : passiveConfig;
0114 drawSurface(helper, *sf, gctx, transform, vConfig);
0115 auto sfExtent = sf->polyhedronRepresentation(gctx, 1).extent();
0116 arrayExtent.extend(sfExtent);
0117 }
0118
0119 if (!sensitiveConfig.outputName.empty()) {
0120 helper.write(joinPaths(outputDir, sensitiveConfig.outputName));
0121 helper.clear();
0122 }
0123
0124 double thickness = gridConfig.lineThickness;
0125
0126 auto binning = surfaceArray.binningValues();
0127 auto axes = surfaceArray.getAxes();
0128 if (!binning.empty() && binning.size() == 2 && axes.size() == 2) {
0129
0130 if (binning[0] == binPhi && binning[1] == binZ) {
0131 double R = arrayExtent.medium(binR) + gridConfig.offset;
0132 auto phiValues = axes[0]->getBinEdges();
0133 auto zValues = axes[1]->getBinEdges();
0134 ViewConfig gridRadConfig = gridConfig;
0135 gridRadConfig.nSegments = phiValues.size();
0136
0137 for (auto phi : phiValues) {
0138 double cphi = std::cos(phi);
0139 double sphi = std::sin(phi);
0140 Vector3 p1(R * cphi, R * sphi, axes[1]->getMin());
0141 Vector3 p0(R * cphi, R * sphi, axes[1]->getMax());
0142 drawSegment(helper, transform * p0, transform * p1, gridConfig);
0143 }
0144 CylinderVolumeBounds cvb(R - 0.5 * thickness, R + 0.5 * thickness,
0145 0.5 * thickness);
0146 auto cvbOrientedSurfaces = cvb.orientedSurfaces();
0147 for (auto z : zValues) {
0148 for (const auto& cvbSf : cvbOrientedSurfaces) {
0149 drawSurface(helper, *cvbSf.surface, gctx,
0150 Translation3(0., 0., z) * transform, gridRadConfig);
0151 }
0152 }
0153
0154 } else if (binning[0] == binR && binning[1] == binPhi) {
0155 double z = arrayExtent.medium(binZ) + gridConfig.offset;
0156 auto rValues = axes[0]->getBinEdges();
0157 auto phiValues = axes[1]->getBinEdges();
0158 ViewConfig gridRadConfig = gridConfig;
0159 gridRadConfig.nSegments = phiValues.size();
0160 for (auto r : rValues) {
0161 CylinderVolumeBounds cvb(r - 0.5 * thickness, r + 0.5 * thickness,
0162 0.5 * thickness);
0163 auto cvbOrientedSurfaces = cvb.orientedSurfaces();
0164 for (const auto& cvbSf : cvbOrientedSurfaces) {
0165 drawSurface(helper, *cvbSf.surface, gctx,
0166 Translation3(0., 0., z) * transform, gridRadConfig);
0167 }
0168 }
0169 double rMin = axes[0]->getMin();
0170 double rMax = axes[0]->getMax();
0171 for (auto phi : phiValues) {
0172 double cphi = std::cos(phi);
0173 double sphi = std::sin(phi);
0174 Vector3 p1(rMax * cphi, rMax * sphi, z);
0175 Vector3 p0(rMin * cphi, rMin * sphi, z);
0176 drawSegment(helper, transform * p0, transform * p1, gridConfig);
0177 }
0178 }
0179 }
0180
0181 if (!gridConfig.outputName.empty()) {
0182 helper.write(joinPaths(outputDir, gridConfig.outputName));
0183 helper.clear();
0184 }
0185 }
0186
0187 void Acts::GeometryView3D::drawVolume(IVisualization3D& helper,
0188 const Volume& volume,
0189 const GeometryContext& gctx,
0190 const Transform3& transform,
0191 const ViewConfig& viewConfig) {
0192 auto bSurfaces = volume.volumeBounds().orientedSurfaces(volume.transform());
0193 for (const auto& bs : bSurfaces) {
0194 drawSurface(helper, *bs.surface, gctx, transform, viewConfig);
0195 }
0196 }
0197
0198 void Acts::GeometryView3D::drawPortal(IVisualization3D& helper,
0199 const Experimental::Portal& portal,
0200 const GeometryContext& gctx,
0201 const Transform3& transform,
0202 const ViewConfig& connected,
0203 const ViewConfig& disconnected) {
0204
0205
0206 auto surface = &(portal.surface());
0207 auto links = &(portal.detectorVolumeUpdaters());
0208 if (links->size() == 2) {
0209 drawSurface(helper, *surface, gctx, transform, connected);
0210 } else {
0211 drawSurface(helper, *surface, gctx, transform, disconnected);
0212 }
0213 }
0214
0215 void Acts::GeometryView3D::drawDetectorVolume(
0216 IVisualization3D& helper, const Experimental::DetectorVolume& volume,
0217 const GeometryContext& gctx, const Transform3& transform,
0218 const ViewConfig& connected, const ViewConfig& unconnected,
0219 const ViewConfig& viewConfig) {
0220
0221 for (auto surface : volume.surfaces()) {
0222 drawSurface(helper, *surface, gctx, transform, viewConfig);
0223 }
0224
0225
0226 for (auto portal : volume.portals()) {
0227 drawPortal(helper, *portal, gctx, transform, connected, unconnected);
0228 }
0229
0230
0231 for (auto subvolume : volume.volumes()) {
0232 drawDetectorVolume(helper, *subvolume, gctx, transform, connected,
0233 unconnected, viewConfig);
0234 }
0235 }
0236
0237 void Acts::GeometryView3D::drawLayer(
0238 IVisualization3D& helper, const Layer& layer, const GeometryContext& gctx,
0239 const ViewConfig& layerConfig, const ViewConfig& sensitiveConfig,
0240 const ViewConfig& gridConfig, const std::string& _outputDir) {
0241 std::string outputDir =
0242 _outputDir == "." ? getWorkingDirectory() : _outputDir;
0243
0244 if (layerConfig.visible) {
0245 auto layerVolume = layer.representingVolume();
0246 if (layerVolume != nullptr) {
0247 drawVolume(helper, *layerVolume, gctx, Transform3::Identity(),
0248 layerConfig);
0249 } else {
0250 const auto& layerSurface = layer.surfaceRepresentation();
0251 drawSurface(helper, layerSurface, gctx, Transform3::Identity(),
0252 layerConfig);
0253 }
0254 if (!layerConfig.outputName.empty()) {
0255 helper.write(joinPaths(outputDir, layerConfig.outputName));
0256 helper.clear();
0257 }
0258 }
0259
0260 if (sensitiveConfig.visible || gridConfig.visible) {
0261 auto surfaceArray = layer.surfaceArray();
0262 if (surfaceArray != nullptr) {
0263 drawSurfaceArray(helper, *surfaceArray, gctx, Transform3::Identity(),
0264 sensitiveConfig, layerConfig, gridConfig, outputDir);
0265 }
0266 }
0267 }
0268
0269 void Acts::GeometryView3D::drawTrackingVolume(
0270 IVisualization3D& helper, const TrackingVolume& tVolume,
0271 const GeometryContext& gctx, const ViewConfig& containerView,
0272 const ViewConfig& volumeView, const ViewConfig& layerView,
0273 const ViewConfig& sensitiveView, const ViewConfig& gridView, bool writeIt,
0274 const std::string& tag, const std::string& _outputDir) {
0275 std::string outputDir =
0276 _outputDir == "." ? getWorkingDirectory() : _outputDir;
0277 if (tVolume.confinedVolumes() != nullptr) {
0278 const auto& subVolumes = tVolume.confinedVolumes()->arrayObjects();
0279 for (const auto& tv : subVolumes) {
0280 drawTrackingVolume(helper, *tv, gctx, containerView, volumeView,
0281 layerView, sensitiveView, gridView, writeIt, tag,
0282 outputDir);
0283 }
0284 }
0285
0286 ViewConfig cConfig = containerView;
0287 ViewConfig vConfig = volumeView;
0288 ViewConfig lConfig = layerView;
0289 ViewConfig sConfig = sensitiveView;
0290 ViewConfig gConfig = gridView;
0291 gConfig.nSegments = 8;
0292
0293 ViewConfig vcConfig = cConfig;
0294 std::string vname = tVolume.volumeName();
0295 if (writeIt) {
0296 std::vector<std::string> repChar = {"::" };
0297
0298 for (const auto& rchar : repChar) {
0299 while (vname.find(rchar) != std::string::npos) {
0300 vname.replace(vname.find(rchar), rchar.size(), std::string("_"));
0301 }
0302 }
0303 if (tVolume.confinedVolumes() == nullptr) {
0304 vcConfig = vConfig;
0305 vcConfig.outputName = vname + std::string("_boundaries") + tag;
0306 } else {
0307 std::stringstream vs;
0308 vs << "Container";
0309 std::vector<GeometryIdentifier::Value> ids{tVolume.geometryId().volume()};
0310
0311 for (const auto* current = &tVolume; current->motherVolume() != nullptr;
0312 current = current->motherVolume()) {
0313 ids.push_back(current->motherVolume()->geometryId().volume());
0314 }
0315
0316 for (std::size_t i = ids.size() - 1; i < ids.size(); --i) {
0317 vs << "_v" << ids[i];
0318 }
0319 vname = vs.str();
0320 vcConfig.outputName = vname + std::string("_boundaries") + tag;
0321 }
0322 }
0323
0324 auto bSurfaces = tVolume.boundarySurfaces();
0325 for (const auto& bs : bSurfaces) {
0326 drawSurface(helper, bs->surfaceRepresentation(), gctx,
0327 Transform3::Identity(), vcConfig);
0328 }
0329 if (writeIt) {
0330 std::string outputName = joinPaths(outputDir, vcConfig.outputName);
0331 helper.write(outputName);
0332 helper.clear();
0333 }
0334
0335 if (tVolume.confinedLayers() != nullptr) {
0336 const auto& layers = tVolume.confinedLayers()->arrayObjects();
0337 std::size_t il = 0;
0338 for (const auto& tl : layers) {
0339 if (writeIt) {
0340 lConfig.outputName =
0341 vname + std::string("_passives_l") + std::to_string(il) + tag;
0342 sConfig.outputName =
0343 vname + std::string("_sensitives_l") + std::to_string(il) + tag;
0344 gConfig.outputName =
0345 vname + std::string("_grids_l") + std::to_string(il) + tag;
0346 }
0347 drawLayer(helper, *tl, gctx, lConfig, sConfig, gConfig, outputDir);
0348 ++il;
0349 }
0350 }
0351 }
0352
0353 void Acts::GeometryView3D::drawSegmentBase(IVisualization3D& helper,
0354 const Vector3& start,
0355 const Vector3& end, int arrows,
0356 double arrowLength,
0357 double arrowWidth,
0358 const ViewConfig& viewConfig) {
0359 double thickness = viewConfig.lineThickness;
0360
0361
0362 auto direction = Vector3(end - start).normalized();
0363 double hlength = 0.5 * Vector3(end - start).norm();
0364
0365 auto unitVectors = makeCurvilinearUnitVectors(direction);
0366 RotationMatrix3 lrotation;
0367 lrotation.col(0) = unitVectors.first;
0368 lrotation.col(1) = unitVectors.second;
0369 lrotation.col(2) = direction;
0370
0371 Vector3 lcenter = 0.5 * (start + end);
0372 double alength = (thickness > 0.) ? arrowLength * thickness : 2.;
0373 if (alength > hlength) {
0374 alength = hlength;
0375 }
0376
0377 if (arrows == 2) {
0378 hlength -= alength;
0379 } else if (arrows != 0) {
0380 hlength -= 0.5 * alength;
0381 lcenter -= Vector3(arrows * 0.5 * alength * direction);
0382 }
0383
0384
0385 if (thickness > 0.) {
0386 auto ltransform = Transform3::Identity();
0387 ltransform.prerotate(lrotation);
0388 ltransform.pretranslate(lcenter);
0389
0390 auto lbounds = std::make_shared<CylinderBounds>(thickness, hlength);
0391 auto line = Surface::makeShared<CylinderSurface>(ltransform, lbounds);
0392
0393 drawSurface(helper, *line, GeometryContext(), Transform3::Identity(),
0394 viewConfig);
0395 } else {
0396 helper.line(start, end, viewConfig.color);
0397 }
0398
0399
0400 if (arrows != 0) {
0401 double awith = thickness * arrowWidth;
0402 double alpha = atan2(thickness * arrowWidth, alength);
0403 auto plateBounds = std::make_shared<RadialBounds>(thickness, awith);
0404
0405 if (arrows > 0) {
0406 auto aetransform = Transform3::Identity();
0407 aetransform.prerotate(lrotation);
0408 aetransform.pretranslate(end);
0409
0410 auto coneBounds = std::make_shared<ConeBounds>(alpha, -alength, 0.);
0411 auto cone = Surface::makeShared<ConeSurface>(aetransform, coneBounds);
0412 drawSurface(helper, *cone, GeometryContext(), Transform3::Identity(),
0413 viewConfig);
0414
0415 auto aptransform = Transform3::Identity();
0416 aptransform.prerotate(lrotation);
0417 aptransform.pretranslate(Vector3(end - alength * direction));
0418
0419 auto plate = Surface::makeShared<DiscSurface>(aptransform, plateBounds);
0420 drawSurface(helper, *plate, GeometryContext(), Transform3::Identity(),
0421 viewConfig);
0422 }
0423 if (arrows < 0 || arrows == 2) {
0424 auto astransform = Transform3::Identity();
0425 astransform.prerotate(lrotation);
0426 astransform.pretranslate(start);
0427
0428
0429 auto coneBounds = std::make_shared<ConeBounds>(alpha, 0., alength);
0430 auto cone = Surface::makeShared<ConeSurface>(astransform, coneBounds);
0431 drawSurface(helper, *cone, GeometryContext(), Transform3::Identity(),
0432 viewConfig);
0433
0434 auto aptransform = Transform3::Identity();
0435 aptransform.prerotate(lrotation);
0436 aptransform.pretranslate(Vector3(start + alength * direction));
0437
0438 auto plate = Surface::makeShared<DiscSurface>(aptransform, plateBounds);
0439 drawSurface(helper, *plate, GeometryContext(), Transform3::Identity(),
0440 viewConfig);
0441 }
0442 }
0443 }
0444
0445 void Acts::GeometryView3D::drawSegment(IVisualization3D& helper,
0446 const Vector3& start, const Vector3& end,
0447 const ViewConfig& viewConfig) {
0448 drawSegmentBase(helper, start, end, 0, 0., 0., viewConfig);
0449 }
0450
0451 void Acts::GeometryView3D::drawArrowBackward(
0452 IVisualization3D& helper, const Vector3& start, const Vector3& end,
0453 double arrowLength, double arrowWidth, const ViewConfig& viewConfig) {
0454 drawSegmentBase(helper, start, end, -1, arrowLength, arrowWidth, viewConfig);
0455 }
0456
0457 void Acts::GeometryView3D::drawArrowForward(
0458 IVisualization3D& helper, const Vector3& start, const Vector3& end,
0459 double arrowLength, double arrowWidth, const ViewConfig& viewConfig) {
0460 drawSegmentBase(helper, start, end, 1, arrowLength, arrowWidth, viewConfig);
0461 }
0462
0463 void Acts::GeometryView3D::drawArrowsBoth(IVisualization3D& helper,
0464 const Vector3& start,
0465 const Vector3& end,
0466 double arrowLength, double arrowWidth,
0467 const ViewConfig& viewConfig) {
0468 drawSegmentBase(helper, start, end, 2, arrowLength, arrowWidth, viewConfig);
0469 }