File indexing completed on 2026-05-24 07:34:17
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Surfaces/PlaneSurface.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Geometry/GeometryObject.hpp"
0013 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0014 #include "Acts/Surfaces/CurvilinearSurface.hpp"
0015 #include "Acts/Surfaces/EllipseBounds.hpp"
0016 #include "Acts/Surfaces/InfiniteBounds.hpp"
0017 #include "Acts/Surfaces/PlanarBounds.hpp"
0018 #include "Acts/Surfaces/RectangleBounds.hpp"
0019 #include "Acts/Surfaces/SurfaceBounds.hpp"
0020 #include "Acts/Surfaces/SurfaceError.hpp"
0021 #include "Acts/Surfaces/SurfaceMergingException.hpp"
0022 #include "Acts/Surfaces/detail/FacesHelper.hpp"
0023 #include "Acts/Surfaces/detail/PlanarHelper.hpp"
0024 #include "Acts/Utilities/Intersection.hpp"
0025 #include "Acts/Utilities/ThrowAssert.hpp"
0026
0027 #include <cmath>
0028 #include <numbers>
0029 #include <numeric>
0030 #include <stdexcept>
0031 #include <utility>
0032 #include <vector>
0033
0034 namespace Acts {
0035
0036 PlaneSurface::PlaneSurface(const PlaneSurface& other)
0037 : GeometryObject{}, RegularSurface(other), m_bounds(other.m_bounds) {}
0038
0039 PlaneSurface::PlaneSurface(const GeometryContext& gctx,
0040 const PlaneSurface& other,
0041 const Transform3& transform)
0042 : RegularSurface(gctx, other, transform), m_bounds(other.m_bounds) {}
0043
0044 PlaneSurface::PlaneSurface(std::shared_ptr<const PlanarBounds> pbounds,
0045 const SurfacePlacementBase& placement)
0046 : RegularSurface{placement}, m_bounds(std::move(pbounds)) {
0047
0048 throw_assert(m_bounds, "PlaneBounds must not be nullptr");
0049 }
0050
0051 PlaneSurface::PlaneSurface(const Transform3& transform,
0052 std::shared_ptr<const PlanarBounds> pbounds)
0053 : RegularSurface(transform), m_bounds(std::move(pbounds)) {}
0054
0055 PlaneSurface& PlaneSurface::operator=(const PlaneSurface& other) {
0056 if (this != &other) {
0057 Surface::operator=(other);
0058 m_bounds = other.m_bounds;
0059 }
0060 return *this;
0061 }
0062
0063 RotationMatrix3 PlaneSurface::referenceFrame(
0064 const GeometryContext& gctx) const {
0065 return localToGlobalTransform(gctx).matrix().block<3, 3>(0, 0);
0066 }
0067
0068 Surface::SurfaceType PlaneSurface::type() const {
0069 return Surface::Plane;
0070 }
0071
0072 Vector3 PlaneSurface::localToGlobal(const GeometryContext& gctx,
0073 const Vector2& lposition) const {
0074 return localToGlobalTransform(gctx) * Vector3(lposition[0], lposition[1], 0.);
0075 }
0076
0077 Result<Vector2> PlaneSurface::globalToLocal(const GeometryContext& gctx,
0078 const Vector3& position,
0079 double tolerance) const {
0080 Vector3 loc3Dframe = localToGlobalTransform(gctx).inverse() * position;
0081 if (std::abs(loc3Dframe.z()) > std::abs(tolerance)) {
0082 return Result<Vector2>::failure(SurfaceError::GlobalPositionNotOnSurface);
0083 }
0084 return Result<Vector2>::success({loc3Dframe.x(), loc3Dframe.y()});
0085 }
0086
0087 std::string PlaneSurface::name() const {
0088 return "Acts::PlaneSurface";
0089 }
0090
0091 const SurfaceBounds& PlaneSurface::bounds() const {
0092 if (m_bounds) {
0093 return *m_bounds;
0094 }
0095 return s_noBounds;
0096 }
0097
0098 Polyhedron PlaneSurface::polyhedronRepresentation(
0099 const GeometryContext& gctx, unsigned int quarterSegments) const {
0100
0101 std::vector<Vector3> vertices;
0102 bool exactPolyhedron = true;
0103
0104
0105 if (m_bounds) {
0106 auto vertices2D = m_bounds->vertices(quarterSegments);
0107 vertices.reserve(vertices2D.size() + 1);
0108 for (const auto& v2D : vertices2D) {
0109 vertices.push_back(localToGlobalTransform(gctx) *
0110 Vector3(v2D.x(), v2D.y(), 0.));
0111 }
0112 bool isEllipse = bounds().type() == SurfaceBounds::eEllipse;
0113 bool innerExists = false, coversFull = false;
0114 if (isEllipse) {
0115 exactPolyhedron = false;
0116 auto vStore = bounds().values();
0117 innerExists = vStore[EllipseBounds::eInnerRx] > s_epsilon &&
0118 vStore[EllipseBounds::eInnerRy] > s_epsilon;
0119 coversFull = std::abs(vStore[EllipseBounds::eHalfPhiSector] -
0120 std::numbers::pi) < s_epsilon;
0121 }
0122
0123
0124
0125 if (!isEllipse || !innerExists || !coversFull) {
0126 auto [faces, triangularMesh] =
0127 detail::FacesHelper::convexFaceMesh(vertices);
0128 return Polyhedron(vertices, faces, triangularMesh, exactPolyhedron);
0129 } else {
0130
0131
0132
0133 auto [faces, triangularMesh] =
0134 detail::FacesHelper::cylindricalFaceMesh(vertices);
0135 return Polyhedron(vertices, faces, triangularMesh, exactPolyhedron);
0136 }
0137 }
0138 throw std::domain_error(
0139 "Polyhedron representation of boundless surface not possible.");
0140 }
0141
0142 Vector3 PlaneSurface::normal(const GeometryContext& gctx,
0143 const Vector2& ) const {
0144 return normal(gctx);
0145 }
0146
0147 Vector3 PlaneSurface::normal(const GeometryContext& gctx,
0148 const Vector3& ) const {
0149 return normal(gctx);
0150 }
0151
0152 Vector3 PlaneSurface::normal(const GeometryContext& gctx) const {
0153 return localToGlobalTransform(gctx).linear().col(2);
0154 }
0155
0156 Vector3 PlaneSurface::referencePosition(const GeometryContext& gctx,
0157 AxisDirection ) const {
0158 return center(gctx);
0159 }
0160
0161 double PlaneSurface::pathCorrection(const GeometryContext& gctx,
0162 const Vector3& ,
0163 const Vector3& direction) const {
0164
0165 return 1. / std::abs(normal(gctx).dot(direction));
0166 }
0167
0168 MultiIntersection3D PlaneSurface::intersect(
0169 const GeometryContext& gctx, const Vector3& position,
0170 const Vector3& direction, const BoundaryTolerance& boundaryTolerance,
0171 double tolerance) const {
0172
0173 const auto& gctxTransform = localToGlobalTransform(gctx);
0174
0175 auto intersection =
0176 PlanarHelper::intersect(gctxTransform, position, direction, tolerance);
0177 auto status = intersection.status();
0178
0179 if (intersection.status() != IntersectionStatus::unreachable) {
0180
0181 const auto& tMatrix = gctxTransform.matrix();
0182
0183 const Vector3 vecLocal(intersection.position() - tMatrix.block<3, 1>(0, 3));
0184 if (!insideBounds(tMatrix.block<3, 2>(0, 0).transpose() * vecLocal,
0185 boundaryTolerance)) {
0186 status = IntersectionStatus::unreachable;
0187 }
0188 }
0189 return MultiIntersection3D(Intersection3D(intersection.position(),
0190 intersection.pathLength(), status));
0191 }
0192
0193 Matrix<2, 3> PlaneSurface::localCartesianToBoundLocalDerivative(
0194 const GeometryContext& , const Vector3& ) const {
0195 const Matrix<2, 3> loc3DToLocBound = Matrix<2, 3>::Identity();
0196 return loc3DToLocBound;
0197 }
0198
0199 std::pair<std::shared_ptr<PlaneSurface>, bool> PlaneSurface::mergedWith(
0200 const PlaneSurface& other, AxisDirection direction,
0201 const Logger& logger) const {
0202 ACTS_VERBOSE("Merging plane surfaces in " << axisDirectionName(direction)
0203 << " direction");
0204
0205 if (isAlignable() || other.isAlignable()) {
0206 throw SurfaceMergingException(getSharedPtr(), other.getSharedPtr(),
0207 "PlaneSurface::merge: surfaces are "
0208 "associated with a detector element");
0209 }
0210
0211 assert(m_transform != nullptr && other.m_transform != nullptr);
0212
0213 Transform3 otherLocal = m_transform->inverse() * *other.m_transform;
0214
0215
0216 constexpr auto tolerance = s_onSurfaceTolerance;
0217
0218
0219 if ((otherLocal.rotation().matrix() - RotationMatrix3::Identity()).norm() >
0220 tolerance) {
0221 ACTS_ERROR("PlaneSurface::merge: surfaces have relative rotation");
0222 throw SurfaceMergingException(
0223 getSharedPtr(), other.getSharedPtr(),
0224 "PlaneSurface::merge: surfaces have relative rotation");
0225 }
0226
0227 const auto* thisBounds = dynamic_cast<const RectangleBounds*>(&bounds());
0228 const auto* otherBounds =
0229 dynamic_cast<const RectangleBounds*>(&other.bounds());
0230
0231 if (thisBounds == nullptr || otherBounds == nullptr) {
0232 throw SurfaceMergingException(
0233 getSharedPtr(), other.getSharedPtr(),
0234 "PlaneSurface::merge: only Rectangle Bounds are supported");
0235 }
0236
0237 if (direction != AxisDirection::AxisX && direction != AxisDirection::AxisY) {
0238 throw SurfaceMergingException(getSharedPtr(), other.getSharedPtr(),
0239 "PlaneSurface::merge: invalid direction " +
0240 axisDirectionName(direction));
0241 }
0242
0243 bool mergeX = direction == AxisDirection::AxisX;
0244
0245 double thisHalfMerge =
0246 mergeX ? thisBounds->halfLengthX() : thisBounds->halfLengthY();
0247 double otherHalfMerge =
0248 mergeX ? otherBounds->halfLengthX() : otherBounds->halfLengthY();
0249
0250 double thisHalfNonMerge =
0251 mergeX ? thisBounds->halfLengthY() : thisBounds->halfLengthX();
0252 double otherHalfNonMerge =
0253 mergeX ? otherBounds->halfLengthY() : otherBounds->halfLengthX();
0254
0255 if (std::abs(thisHalfNonMerge - otherHalfNonMerge) > tolerance) {
0256 ACTS_ERROR(
0257 "PlaneSurface::merge: surfaces have different non-merging lengths");
0258 throw SurfaceMergingException(
0259 getSharedPtr(), other.getSharedPtr(),
0260 "PlaneSurface::merge: surfaces have different non-merging lengths");
0261 }
0262 Vector3 otherTranslation = otherLocal.translation();
0263
0264
0265 double nonMergeShift = mergeX ? otherTranslation.y() : otherTranslation.x();
0266
0267 if (std::abs(nonMergeShift) > tolerance ||
0268 std::abs(otherTranslation.z()) > tolerance) {
0269 ACTS_ERROR(
0270 "PlaneSurface::merge: surfaces have relative translation in y/z");
0271 throw SurfaceMergingException(
0272 getSharedPtr(), other.getSharedPtr(),
0273 "PlaneSurface::merge: surfaces have relative translation in y/z");
0274 }
0275
0276 double mergeShift = mergeX ? otherTranslation.x() : otherTranslation.y();
0277
0278 double thisMinMerge = -thisHalfMerge;
0279 double thisMaxMerge = thisHalfMerge;
0280
0281 double otherMinMerge = mergeShift - otherHalfMerge;
0282 double otherMaxMerge = mergeShift + otherHalfMerge;
0283
0284
0285 if (std::abs(thisMaxMerge - otherMinMerge) > tolerance &&
0286 std::abs(thisMinMerge - otherMaxMerge) > tolerance) {
0287 ACTS_ERROR(
0288 "PlaneSurface::merge: surfaces have incompatible merge bound location");
0289 throw SurfaceMergingException(
0290 getSharedPtr(), other.getSharedPtr(),
0291 "PlaneSurface::merge: surfaces have incompatible merge bound location");
0292 }
0293
0294 double newMaxMerge = std::max(thisMaxMerge, otherMaxMerge);
0295 double newMinMerge = std::min(thisMinMerge, otherMinMerge);
0296
0297 double newHalfMerge = std::midpoint(newMaxMerge, -newMinMerge);
0298 double newMidMerge = std::midpoint(newMaxMerge, newMinMerge);
0299
0300 auto newBounds =
0301 mergeX
0302 ? std::make_shared<RectangleBounds>(newHalfMerge, thisHalfNonMerge)
0303 : std::make_shared<RectangleBounds>(thisHalfNonMerge, newHalfMerge);
0304
0305 Vector3 unitDir = mergeX ? Vector3::UnitX() : Vector3::UnitY();
0306 Transform3 newTransform = *m_transform * Translation3{unitDir * newMidMerge};
0307 return {Surface::makeShared<PlaneSurface>(newTransform, newBounds),
0308 mergeShift < 0};
0309 }
0310 const std::shared_ptr<const PlanarBounds>& PlaneSurface::boundsPtr() const {
0311 return m_bounds;
0312 }
0313
0314 void PlaneSurface::assignSurfaceBounds(
0315 std::shared_ptr<const PlanarBounds> newBounds) {
0316 m_bounds = std::move(newBounds);
0317 }
0318
0319 }