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