File indexing completed on 2025-08-05 08:09:40
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Surfaces/ConeSurface.hpp"
0010
0011 #include "Acts/Geometry/GeometryObject.hpp"
0012 #include "Acts/Surfaces/SurfaceError.hpp"
0013 #include "Acts/Surfaces/detail/AlignmentHelper.hpp"
0014 #include "Acts/Surfaces/detail/FacesHelper.hpp"
0015 #include "Acts/Surfaces/detail/VerticesHelper.hpp"
0016 #include "Acts/Utilities/Helpers.hpp"
0017 #include "Acts/Utilities/Intersection.hpp"
0018 #include "Acts/Utilities/ThrowAssert.hpp"
0019 #include "Acts/Utilities/detail/RealQuadraticEquation.hpp"
0020
0021 #include <algorithm>
0022 #include <cmath>
0023 #include <limits>
0024 #include <stdexcept>
0025 #include <utility>
0026 #include <vector>
0027
0028 using Acts::VectorHelpers::perp;
0029 using Acts::VectorHelpers::phi;
0030
0031 Acts::ConeSurface::ConeSurface(const ConeSurface& other)
0032 : GeometryObject(), RegularSurface(other), m_bounds(other.m_bounds) {}
0033
0034 Acts::ConeSurface::ConeSurface(const GeometryContext& gctx,
0035 const ConeSurface& other,
0036 const Transform3& shift)
0037 : GeometryObject(),
0038 RegularSurface(gctx, other, shift),
0039 m_bounds(other.m_bounds) {}
0040
0041 Acts::ConeSurface::ConeSurface(const Transform3& transform, double alpha,
0042 bool symmetric)
0043 : GeometryObject(),
0044 RegularSurface(transform),
0045 m_bounds(std::make_shared<const ConeBounds>(alpha, symmetric)) {}
0046
0047 Acts::ConeSurface::ConeSurface(const Transform3& transform, double alpha,
0048 double zmin, double zmax, double halfPhi)
0049 : GeometryObject(),
0050 RegularSurface(transform),
0051 m_bounds(std::make_shared<const ConeBounds>(alpha, zmin, zmax, halfPhi)) {
0052 }
0053
0054 Acts::ConeSurface::ConeSurface(const Transform3& transform,
0055 std::shared_ptr<const ConeBounds> cbounds)
0056 : GeometryObject(),
0057 RegularSurface(transform),
0058 m_bounds(std::move(cbounds)) {
0059 throw_assert(m_bounds, "ConeBounds must not be nullptr");
0060 }
0061
0062 Acts::Vector3 Acts::ConeSurface::binningPosition(
0063 const GeometryContext& gctx, Acts::BinningValue bValue) const {
0064 const Vector3& sfCenter = center(gctx);
0065
0066
0067 if (bValue == Acts::binR || bValue == Acts::binRPhi) {
0068 return Vector3(sfCenter.x() + bounds().r(sfCenter.z()), sfCenter.y(),
0069 sfCenter.z());
0070 }
0071
0072
0073 return sfCenter;
0074 }
0075
0076 Acts::Surface::SurfaceType Acts::ConeSurface::type() const {
0077 return Surface::Cone;
0078 }
0079
0080 Acts::ConeSurface& Acts::ConeSurface::operator=(const ConeSurface& other) {
0081 if (this != &other) {
0082 Surface::operator=(other);
0083 m_bounds = other.m_bounds;
0084 }
0085 return *this;
0086 }
0087
0088 Acts::Vector3 Acts::ConeSurface::rotSymmetryAxis(
0089 const GeometryContext& gctx) const {
0090 return transform(gctx).matrix().block<3, 1>(0, 2);
0091 }
0092
0093 Acts::RotationMatrix3 Acts::ConeSurface::referenceFrame(
0094 const GeometryContext& gctx, const Vector3& position,
0095 const Vector3& ) const {
0096 RotationMatrix3 mFrame;
0097
0098
0099 Vector3 measY = rotSymmetryAxis(gctx);
0100
0101 Vector3 measDepth = Vector3(position.x(), position.y(), 0.).normalized();
0102
0103 Acts::Vector3 measX(measY.cross(measDepth).normalized());
0104
0105 mFrame.col(0) = measX;
0106 mFrame.col(1) = measY;
0107 mFrame.col(2) = measDepth;
0108
0109
0110
0111 return mFrame;
0112 }
0113
0114 Acts::Vector3 Acts::ConeSurface::localToGlobal(const GeometryContext& gctx,
0115 const Vector2& lposition) const {
0116
0117 double r = lposition[Acts::eBoundLoc1] * bounds().tanAlpha();
0118 double phi = lposition[Acts::eBoundLoc0] / r;
0119 Vector3 loc3Dframe(r * cos(phi), r * sin(phi), lposition[Acts::eBoundLoc1]);
0120 return transform(gctx) * loc3Dframe;
0121 }
0122
0123 Acts::Result<Acts::Vector2> Acts::ConeSurface::globalToLocal(
0124 const GeometryContext& gctx, const Vector3& position,
0125 double tolerance) const {
0126 Vector3 loc3Dframe = transform(gctx).inverse() * position;
0127 double r = loc3Dframe.z() * bounds().tanAlpha();
0128 if (std::abs(perp(loc3Dframe) - r) > tolerance) {
0129 return Result<Vector2>::failure(SurfaceError::GlobalPositionNotOnSurface);
0130 }
0131 return Result<Acts::Vector2>::success(
0132 Vector2(r * atan2(loc3Dframe.y(), loc3Dframe.x()), loc3Dframe.z()));
0133 }
0134
0135 double Acts::ConeSurface::pathCorrection(const GeometryContext& gctx,
0136 const Vector3& position,
0137 const Vector3& direction) const {
0138
0139 Vector3 posLocal = transform(gctx).inverse() * position;
0140 double phi = VectorHelpers::phi(posLocal);
0141 double sgn = posLocal.z() > 0. ? -1. : +1.;
0142 double cosAlpha = std::cos(bounds().get(ConeBounds::eAlpha));
0143 double sinAlpha = std::sin(bounds().get(ConeBounds::eAlpha));
0144 Vector3 normalC(cos(phi) * cosAlpha, sin(phi) * cosAlpha, sgn * sinAlpha);
0145 normalC = transform(gctx) * normalC;
0146
0147 double cAlpha = normalC.dot(direction);
0148 return std::abs(1. / cAlpha);
0149 }
0150
0151 std::string Acts::ConeSurface::name() const {
0152 return "Acts::ConeSurface";
0153 }
0154
0155 Acts::Vector3 Acts::ConeSurface::normal(const GeometryContext& gctx,
0156 const Acts::Vector2& lposition) const {
0157
0158 double phi = lposition[Acts::eBoundLoc0] /
0159 (bounds().r(lposition[Acts::eBoundLoc1])),
0160 sgn = lposition[Acts::eBoundLoc1] > 0 ? -1. : +1.;
0161 double cosAlpha = std::cos(bounds().get(ConeBounds::eAlpha));
0162 double sinAlpha = std::sin(bounds().get(ConeBounds::eAlpha));
0163 Vector3 localNormal(cos(phi) * cosAlpha, sin(phi) * cosAlpha, sgn * sinAlpha);
0164 return Vector3(transform(gctx).linear() * localNormal);
0165 }
0166
0167 Acts::Vector3 Acts::ConeSurface::normal(const GeometryContext& gctx,
0168 const Acts::Vector3& position) const {
0169
0170
0171 Vector3 pos3D = transform(gctx).inverse() * position;
0172 pos3D.z() = 0;
0173 return pos3D.normalized();
0174 }
0175
0176 const Acts::ConeBounds& Acts::ConeSurface::bounds() const {
0177
0178 return (*m_bounds.get());
0179 }
0180
0181 Acts::Polyhedron Acts::ConeSurface::polyhedronRepresentation(
0182 const GeometryContext& gctx, std::size_t lseg) const {
0183
0184 std::vector<Vector3> vertices;
0185 std::vector<Polyhedron::FaceType> faces;
0186 std::vector<Polyhedron::FaceType> triangularMesh;
0187
0188 double minZ = bounds().get(ConeBounds::eMinZ);
0189 double maxZ = bounds().get(ConeBounds::eMaxZ);
0190
0191 if (minZ == -std::numeric_limits<double>::infinity() ||
0192 maxZ == std::numeric_limits<double>::infinity()) {
0193 throw std::domain_error(
0194 "Polyhedron repr of boundless surface not possible");
0195 }
0196
0197 auto ctransform = transform(gctx);
0198
0199
0200 bool tipExists = false;
0201 if (minZ * maxZ <= s_onSurfaceTolerance) {
0202 vertices.push_back(ctransform * Vector3(0., 0., 0.));
0203 tipExists = true;
0204 }
0205
0206
0207 double hPhiSec = bounds().get(ConeBounds::eHalfPhiSector);
0208 double avgPhi = bounds().get(ConeBounds::eAveragePhi);
0209 bool fullCone = (hPhiSec == M_PI);
0210
0211
0212 auto phiSegs = fullCone ? detail::VerticesHelper::phiSegments()
0213 : detail::VerticesHelper::phiSegments(
0214 avgPhi - hPhiSec, avgPhi + hPhiSec,
0215 {static_cast<ActsScalar>(avgPhi)});
0216
0217
0218 std::vector<double> coneSides;
0219 if (std::abs(minZ) > s_onSurfaceTolerance) {
0220 coneSides.push_back(minZ);
0221 }
0222 if (std::abs(maxZ) > s_onSurfaceTolerance) {
0223 coneSides.push_back(maxZ);
0224 }
0225 for (auto& z : coneSides) {
0226
0227 std::size_t firstIv = vertices.size();
0228
0229 double r = std::abs(z) * bounds().tanAlpha();
0230 Vector3 zoffset(0., 0., z);
0231 for (unsigned int iseg = 0; iseg < phiSegs.size() - 1; ++iseg) {
0232 int addon = (iseg == phiSegs.size() - 2 && !fullCone) ? 1 : 0;
0233 detail::VerticesHelper::createSegment(vertices, {r, r}, phiSegs[iseg],
0234 phiSegs[iseg + 1], lseg, addon,
0235 zoffset, ctransform);
0236 }
0237
0238 if (tipExists) {
0239 for (std::size_t iv = firstIv + 2; iv < vertices.size() + 1; ++iv) {
0240 std::size_t one = 0, two = iv - 1, three = iv - 2;
0241 if (z < 0.) {
0242 std::swap(two, three);
0243 }
0244 faces.push_back({one, two, three});
0245 }
0246
0247 if (fullCone) {
0248 if (z > 0.) {
0249 faces.push_back({0, firstIv, vertices.size() - 1});
0250 } else {
0251 faces.push_back({0, vertices.size() - 1, firstIv});
0252 }
0253 }
0254 }
0255 }
0256
0257 if (tipExists) {
0258 triangularMesh = faces;
0259 } else {
0260 auto facesMesh =
0261 detail::FacesHelper::cylindricalFaceMesh(vertices, fullCone);
0262 faces = facesMesh.first;
0263 triangularMesh = facesMesh.second;
0264 }
0265 return Polyhedron(vertices, faces, triangularMesh, false);
0266 }
0267
0268 Acts::detail::RealQuadraticEquation Acts::ConeSurface::intersectionSolver(
0269 const GeometryContext& gctx, const Vector3& position,
0270 const Vector3& direction) const {
0271
0272 Transform3 invTrans = transform(gctx).inverse();
0273 Vector3 point1 = invTrans * position;
0274 Vector3 dir1 = invTrans.linear() * direction;
0275
0276
0277 double tan2Alpha = bounds().tanAlpha() * bounds().tanAlpha(),
0278 A = dir1.x() * dir1.x() + dir1.y() * dir1.y() -
0279 tan2Alpha * dir1.z() * dir1.z(),
0280 B = 2 * (dir1.x() * point1.x() + dir1.y() * point1.y() -
0281 tan2Alpha * dir1.z() * point1.z()),
0282 C = point1.x() * point1.x() + point1.y() * point1.y() -
0283 tan2Alpha * point1.z() * point1.z();
0284 if (A == 0.) {
0285 A += 1e-16;
0286 }
0287
0288 return detail::RealQuadraticEquation(A, B, C);
0289 }
0290
0291 Acts::SurfaceMultiIntersection Acts::ConeSurface::intersect(
0292 const GeometryContext& gctx, const Vector3& position,
0293 const Vector3& direction, const BoundaryCheck& bcheck,
0294 ActsScalar tolerance) const {
0295
0296 auto qe = intersectionSolver(gctx, position, direction);
0297
0298
0299 if (qe.solutions == 0) {
0300 return {{Intersection3D::invalid(), Intersection3D::invalid()}, this};
0301 }
0302
0303
0304 Vector3 solution1 = position + qe.first * direction;
0305 Intersection3D::Status status1 = std::abs(qe.first) < std::abs(tolerance)
0306 ? Intersection3D::Status::onSurface
0307 : Intersection3D::Status::reachable;
0308
0309 if (bcheck.isEnabled() && !isOnSurface(gctx, solution1, direction, bcheck)) {
0310 status1 = Intersection3D::Status::missed;
0311 }
0312
0313
0314 Vector3 solution2 = position + qe.first * direction;
0315 Intersection3D::Status status2 = std::abs(qe.second) < std::abs(tolerance)
0316 ? Intersection3D::Status::onSurface
0317 : Intersection3D::Status::reachable;
0318 if (bcheck.isEnabled() && !isOnSurface(gctx, solution2, direction, bcheck)) {
0319 status2 = Intersection3D::Status::missed;
0320 }
0321
0322 const auto& tf = transform(gctx);
0323
0324 Intersection3D first(tf * solution1, qe.first, status1);
0325 Intersection3D second(tf * solution2, qe.second, status2);
0326
0327 if (first.pathLength() <= second.pathLength()) {
0328 return {{first, second}, this};
0329 }
0330 return {{second, first}, this};
0331 }
0332
0333 Acts::AlignmentToPathMatrix Acts::ConeSurface::alignmentToPathDerivative(
0334 const GeometryContext& gctx, const Vector3& position,
0335 const Vector3& direction) const {
0336 assert(isOnSurface(gctx, position, direction, BoundaryCheck(false)));
0337
0338
0339 const auto pcRowVec = (position - center(gctx)).transpose().eval();
0340
0341 const auto& rotation = transform(gctx).rotation();
0342
0343 const auto& localXAxis = rotation.col(0);
0344 const auto& localYAxis = rotation.col(1);
0345 const auto& localZAxis = rotation.col(2);
0346
0347 const auto localPos = (rotation.transpose() * position).eval();
0348 const auto dx = direction.dot(localXAxis);
0349 const auto dy = direction.dot(localYAxis);
0350 const auto dz = direction.dot(localZAxis);
0351
0352 const auto tanAlpha2 = bounds().tanAlpha() * bounds().tanAlpha();
0353 const auto norm = 1 / (1 - dz * dz * (1 + tanAlpha2));
0354
0355 const auto& dirRowVec = direction.transpose();
0356
0357
0358
0359 const auto localXAxisToPath =
0360 (-2 * norm * (dx * pcRowVec + localPos.x() * dirRowVec)).eval();
0361 const auto localYAxisToPath =
0362 (-2 * norm * (dy * pcRowVec + localPos.y() * dirRowVec)).eval();
0363 const auto localZAxisToPath =
0364 (2 * norm * tanAlpha2 * (dz * pcRowVec + localPos.z() * dirRowVec) -
0365 4 * norm * norm * (1 + tanAlpha2) *
0366 (dx * localPos.x() + dy * localPos.y() -
0367 dz * localPos.z() * tanAlpha2) *
0368 dz * dirRowVec)
0369 .eval();
0370
0371 const auto [rotToLocalXAxis, rotToLocalYAxis, rotToLocalZAxis] =
0372 detail::rotationToLocalAxesDerivative(rotation);
0373
0374
0375 AlignmentToPathMatrix alignToPath = AlignmentToPathMatrix::Zero();
0376 alignToPath.segment<3>(eAlignmentCenter0) =
0377 2 * norm * (dx * localXAxis.transpose() + dy * localYAxis.transpose());
0378 alignToPath.segment<3>(eAlignmentRotation0) =
0379 localXAxisToPath * rotToLocalXAxis + localYAxisToPath * rotToLocalYAxis +
0380 localZAxisToPath * rotToLocalZAxis;
0381
0382 return alignToPath;
0383 }
0384
0385 Acts::ActsMatrix<2, 3> Acts::ConeSurface::localCartesianToBoundLocalDerivative(
0386 const GeometryContext& gctx, const Vector3& position) const {
0387 using VectorHelpers::perp;
0388 using VectorHelpers::phi;
0389
0390 const auto& sTransform = transform(gctx);
0391
0392 const Vector3 localPos = sTransform.inverse() * position;
0393 const double lr = perp(localPos);
0394 const double lphi = phi(localPos);
0395 const double lcphi = std::cos(lphi);
0396 const double lsphi = std::sin(lphi);
0397
0398 const double R = localPos.z() * bounds().tanAlpha();
0399 ActsMatrix<2, 3> loc3DToLocBound = ActsMatrix<2, 3>::Zero();
0400 loc3DToLocBound << -R * lsphi / lr, R * lcphi / lr,
0401 lphi * bounds().tanAlpha(), 0, 0, 1;
0402
0403 return loc3DToLocBound;
0404 }