Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:09:22

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2016-2020 CERN for the benefit of the Acts project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 #include "Acts/Definitions/Algebra.hpp"
0011 #include "Acts/Definitions/TrackParametrization.hpp"
0012 #include "Acts/Surfaces/detail/VerticesHelper.hpp"
0013 
0014 #include <cfloat>
0015 #include <cmath>
0016 #include <iterator>
0017 #include <vector>
0018 
0019 namespace Acts {
0020 
0021 /// @class BoundaryCheck
0022 ///
0023 /// The BoundaryCheck class provides boundary checks and distance calculations
0024 /// for aligned box-like and polygonal boundaries on local surfaces.
0025 /// Different types of boundary checks are supported and are transparently
0026 /// selected when calling the `isInside(...)` and `distance(...)` methods:
0027 ///
0028 /// -   Hard checks w/o any tolerances
0029 /// -   Tolerance-based checks in one or in both local coordinates
0030 /// -   Chi2-based checks based on a covariance matrix. Non-vanishing
0031 ///     correlations are correctly taken into account.
0032 ///
0033 /// With a defined covariance matrix, the closest point and the distance are
0034 /// not defined along the usual Euclidean metric, but by the Mahalanobis
0035 /// distance induced by the covariance.
0036 class BoundaryCheck {
0037  public:
0038   /// Construct either hard cut in both dimensions or no cut at all.
0039   explicit BoundaryCheck(bool check);
0040 
0041   /// Construct a tolerance based check.
0042   ///
0043   /// @param checkLocal0 Boolean directive to check coordinate 0
0044   /// @param checkLocal1 Boolean directive to check coordinate 1
0045   /// @param tolerance0 Tolerance along coordinate 0
0046   /// @param tolerance1 Tolerance along coordinate 1
0047   BoundaryCheck(bool checkLocal0, bool checkLocal1, double tolerance0 = 0,
0048                 double tolerance1 = 0);
0049 
0050   /// Construct a chi2-based check.
0051   ///
0052   /// @param localCovariance Coverance matrix in local coordinates
0053   /// @param sigmaMax  Significance for the compatibility test
0054   BoundaryCheck(const SquareMatrix2& localCovariance, double sigmaMax = 1);
0055 
0056   bool isEnabled() const { return m_type != Type::eNone; }
0057 
0058   /// Check if the point is inside a polygon.
0059   ///
0060   /// @param point    Test point
0061   /// @param vertices Forward iterable container of convex polygon vertices.
0062   ///                 Calling `std::begin`/ `std::end` on the container must
0063   ///                 return an iterator where `*it` must be convertible to
0064   ///                 an `Acts::Vector2`.
0065   ///
0066   /// The check takes into account whether tolerances or covariances are defined
0067   /// for the boundary check.
0068   template <typename Vector2Container>
0069   bool isInside(const Vector2& point, const Vector2Container& vertices) const;
0070 
0071   /// Check if the point is inside a box aligned with the local axes.
0072   ///
0073   /// @param point   Test point
0074   /// @param lowerLeft Minimal vertex of the box
0075   /// @param upperRight Maximal vertex of the box
0076   ///
0077   /// The check takes into account whether tolerances or covariances are defined
0078   /// for the boundary check.
0079   bool isInside(const Vector2& point, const Vector2& lowerLeft,
0080                 const Vector2& upperRight) const;
0081 
0082   /// Calculate the signed, weighted, closest distance to a polygonal boundary.
0083   ///
0084   /// @param point Test point
0085   /// @param vertices Forward iterable container of convex polygon vertices.
0086   ///                 Calling `std::begin`/ `std::end` on the container must
0087   ///                 return an iterator where `*it` must be convertible to
0088   ///                 an `Acts::Vector2`.
0089   /// @return Negative value if inside, positive if outside
0090   ///
0091   /// If a covariance is defined, the distance is the corresponding Mahalanobis
0092   /// distance. Otherwise, it is the Eucleadian distance.
0093   template <typename Vector2Container>
0094   double distance(const Vector2& point, const Vector2Container& vertices) const;
0095 
0096   /// Calculate the signed, weighted, closest distance to an aligned box.
0097   ///
0098   /// @param point Test point
0099   /// @param lowerLeft Minimal vertex of the box
0100   /// @param upperRight Maximal vertex of the box
0101   /// @return Negative value if inside, positive if outside
0102   ///
0103   /// If a covariance is defined, the distance is the corresponding Mahalanobis
0104   /// distance. Otherwise, it is the Eucleadian distance.
0105   double distance(const Vector2& point, const Vector2& lowerLeft,
0106                   const Vector2& upperRight) const;
0107 
0108   enum class Type {
0109     eNone,      ///< disable boundary check
0110     eAbsolute,  ///< absolute cut
0111     eChi2       ///< chi2-based cut with full correlations
0112   };
0113 
0114   /// Broadcast the type
0115   Type type() const;
0116 
0117   // Broadcast the tolerance
0118   const Vector2& tolerance() const;
0119 
0120   // Return the covariance matrix
0121   SquareMatrix2 covariance() const;
0122 
0123  private:
0124   /// Return a new BoundaryCheck with updated covariance.
0125   /// @param jacobian Transform Jacobian for the covariance
0126   /// @warning This currently only transforms the covariance and does not work
0127   ///          for the tolerance based check.
0128   BoundaryCheck transformed(const ActsMatrix<2, 2>& jacobian) const;
0129 
0130   /// Check if the distance vector is within the absolute or relative limits.
0131   bool isTolerated(const Vector2& delta) const;
0132 
0133   /// Compute vector norm based on the covariance.
0134   double squaredNorm(const Vector2& x) const;
0135 
0136   /// Calculate the closest point on the polygon.
0137   template <typename Vector2Container>
0138   Vector2 computeClosestPointOnPolygon(const Vector2& point,
0139                                        const Vector2Container& vertices) const;
0140 
0141   /// Calculate the closest point on the box
0142   Vector2 computeEuclideanClosestPointOnRectangle(
0143       const Vector2& point, const Vector2& lowerLeft,
0144       const Vector2& upperRight) const;
0145 
0146   /// metric weight matrix: identity for absolute mode or inverse covariance
0147   SquareMatrix2 m_weight;
0148 
0149   /// dual use: absolute tolerances or relative chi2/ sigma cut.
0150   Vector2 m_tolerance;
0151   Type m_type;
0152 
0153   // To access the m_type
0154   friend class CylinderBounds;
0155   friend class RectangleBounds;
0156   // To be able to use `transformed`
0157   friend class CylinderBounds;
0158   friend class DiscTrapezoidBounds;
0159   // EllipseBounds needs a custom implementation
0160   friend class EllipseBounds;
0161 };
0162 
0163 }  // namespace Acts
0164 
0165 inline Acts::BoundaryCheck::Type Acts::BoundaryCheck::type() const {
0166   return m_type;
0167 }
0168 
0169 inline const Acts::Vector2& Acts::BoundaryCheck::tolerance() const {
0170   return m_tolerance;
0171 }
0172 
0173 inline Acts::SquareMatrix2 Acts::BoundaryCheck::covariance() const {
0174   return m_weight.inverse();
0175 }
0176 
0177 inline Acts::BoundaryCheck::BoundaryCheck(bool check)
0178     : m_weight(SquareMatrix2::Identity()),
0179       m_tolerance(0, 0),
0180       m_type(check ? Type::eAbsolute : Type::eNone) {}
0181 
0182 inline Acts::BoundaryCheck::BoundaryCheck(bool checkLocal0, bool checkLocal1,
0183                                           double tolerance0, double tolerance1)
0184     : m_weight(SquareMatrix2::Identity()),
0185       m_tolerance(checkLocal0 ? tolerance0 : DBL_MAX,
0186                   checkLocal1 ? tolerance1 : DBL_MAX),
0187       m_type(Type::eAbsolute) {}
0188 
0189 inline Acts::BoundaryCheck::BoundaryCheck(const SquareMatrix2& localCovariance,
0190                                           double sigmaMax)
0191     : m_weight(localCovariance.inverse()),
0192       m_tolerance(sigmaMax, 0),
0193       m_type(Type::eChi2) {}
0194 
0195 inline Acts::BoundaryCheck Acts::BoundaryCheck::transformed(
0196     const ActsMatrix<2, 2>& jacobian) const {
0197   BoundaryCheck bc = *this;
0198   if (m_type == Type::eAbsolute) {
0199     // project tolerances to the new system. depending on the jacobian we need
0200     // to check both tolerances, even when the initial check does not.
0201     bc.m_tolerance = (jacobian * m_tolerance).cwiseAbs();
0202   } else /* Type::eChi2 */ {
0203     bc.m_weight =
0204         (jacobian * m_weight.inverse() * jacobian.transpose()).inverse();
0205   }
0206   return bc;
0207 }
0208 
0209 template <typename Vector2Container>
0210 inline bool Acts::BoundaryCheck::isInside(
0211     const Vector2& point, const Vector2Container& vertices) const {
0212   if (m_type == Type::eNone) {
0213     // The null boundary check always succeeds
0214     return true;
0215   } else if (detail::VerticesHelper::isInsidePolygon(point, vertices)) {
0216     // If the point falls inside the polygon, the check always succeeds
0217     return true;
0218   } else if (m_tolerance == Vector2(0., 0.)) {
0219     // Outside of the polygon, since we've eliminated the case of an absence of
0220     // check above, we know we'll always fail if the tolerance is zero.
0221     //
0222     // This allows us to avoid the expensive computeClosestPointOnPolygon
0223     // computation in this simple case.
0224     //
0225     // TODO: When tolerance is not 0, we could also avoid this computation in
0226     //       some cases by testing against a bounding box of the polygon, padded
0227     //       on each side with our tolerance. Check if this optimization is
0228     //       worthwhile in some production workflows, and if so implement it.
0229     return false;
0230   } else {
0231     // We are outside of the polygon, but there is a tolerance. Must find what
0232     // the closest point on the polygon is and check if it's within tolerance.
0233     auto closestPoint = computeClosestPointOnPolygon(point, vertices);
0234     return isTolerated(closestPoint - point);
0235   }
0236 }
0237 
0238 inline bool Acts::BoundaryCheck::isInside(const Vector2& point,
0239                                           const Vector2& lowerLeft,
0240                                           const Vector2& upperRight) const {
0241   if (detail::VerticesHelper::isInsideRectangle(point, lowerLeft, upperRight)) {
0242     return true;
0243   } else {
0244     Vector2 closestPoint;
0245 
0246     if (m_type == Type::eNone || m_type == Type::eAbsolute) {
0247       // absolute, can calculate directly
0248       closestPoint =
0249           computeEuclideanClosestPointOnRectangle(point, lowerLeft, upperRight);
0250 
0251     } else /* Type::eChi2 */ {
0252       // need to calculate by projection and squarednorm
0253       Vector2 vertices[] = {{lowerLeft[0], lowerLeft[1]},
0254                             {upperRight[0], lowerLeft[1]},
0255                             {upperRight[0], upperRight[1]},
0256                             {lowerLeft[0], upperRight[1]}};
0257       closestPoint = computeClosestPointOnPolygon(point, vertices);
0258     }
0259 
0260     return isTolerated(closestPoint - point);
0261   }
0262 }
0263 
0264 template <typename Vector2Container>
0265 inline double Acts::BoundaryCheck::distance(
0266     const Acts::Vector2& point, const Vector2Container& vertices) const {
0267   // TODO 2017-04-06 msmk: this should be calculable directly
0268   double d = squaredNorm(point - computeClosestPointOnPolygon(point, vertices));
0269   d = std::sqrt(d);
0270   return detail::VerticesHelper::isInsidePolygon(point, vertices) ? -d : d;
0271 }
0272 
0273 inline double Acts::BoundaryCheck::distance(const Acts::Vector2& point,
0274                                             const Vector2& lowerLeft,
0275                                             const Vector2& upperRight) const {
0276   if (m_type == Type::eNone || m_type == Type::eAbsolute) {
0277     // compute closest point on box
0278     double d = (point - computeEuclideanClosestPointOnRectangle(
0279                             point, lowerLeft, upperRight))
0280                    .norm();
0281     return detail::VerticesHelper::isInsideRectangle(point, lowerLeft,
0282                                                      upperRight)
0283                ? -d
0284                : d;
0285 
0286   } else /* Type::eChi2 */ {
0287     Vector2 vertices[] = {{lowerLeft[0], lowerLeft[1]},
0288                           {upperRight[0], lowerLeft[1]},
0289                           {upperRight[0], upperRight[1]},
0290                           {lowerLeft[0], upperRight[1]}};
0291     return distance(point, vertices);
0292   }
0293 }
0294 
0295 inline bool Acts::BoundaryCheck::isTolerated(const Vector2& delta) const {
0296   if (m_type == Type::eNone) {
0297     return true;
0298   } else if (m_type == Type::eAbsolute) {
0299     return (std::abs(delta[0]) <= m_tolerance[0]) &&
0300            (std::abs(delta[1]) <= m_tolerance[1]);
0301   } else /* Type::eChi2 */ {
0302     // Mahalanobis distances mean is 2 in 2-dim. cut is 1-d sigma.
0303     return (squaredNorm(delta) < (2 * m_tolerance[0]));
0304   }
0305 }
0306 
0307 inline double Acts::BoundaryCheck::squaredNorm(const Vector2& x) const {
0308   return (x.transpose() * m_weight * x).value();
0309 }
0310 
0311 template <typename Vector2Container>
0312 inline Acts::Vector2 Acts::BoundaryCheck::computeClosestPointOnPolygon(
0313     const Acts::Vector2& point, const Vector2Container& vertices) const {
0314   // calculate the closest position on the segment between `ll0` and `ll1` to
0315   // the point as measured by the metric induced by the weight matrix
0316   auto closestOnSegment = [&](auto&& ll0, auto&& ll1) {
0317     // normal vector and position of the closest point along the normal
0318     auto n = ll1 - ll0;
0319     auto weighted_n = m_weight * n;
0320     auto f = n.dot(weighted_n);
0321     auto u = std::isnormal(f)
0322                  ? (point - ll0).dot(weighted_n) / f
0323                  : 0.5;  // ll0 and ll1 are so close it doesn't matter
0324     // u must be in [0, 1] to still be on the polygon segment
0325     return ll0 + std::clamp(u, 0.0, 1.0) * n;
0326   };
0327 
0328   auto iv = std::begin(vertices);
0329   Vector2 l0 = *iv;
0330   Vector2 l1 = *(++iv);
0331   Vector2 closest = closestOnSegment(l0, l1);
0332   auto closestDist = squaredNorm(closest - point);
0333   // Calculate the closest point on other connecting lines and compare distances
0334   for (++iv; iv != std::end(vertices); ++iv) {
0335     l0 = l1;
0336     l1 = *iv;
0337     Vector2 current = closestOnSegment(l0, l1);
0338     auto currentDist = squaredNorm(current - point);
0339     if (currentDist < closestDist) {
0340       closest = current;
0341       closestDist = currentDist;
0342     }
0343   }
0344   // final edge from last vertex back to the first vertex
0345   Vector2 last = closestOnSegment(l1, *std::begin(vertices));
0346   if (squaredNorm(last - point) < closestDist) {
0347     closest = last;
0348   }
0349   return closest;
0350 }
0351 
0352 inline Acts::Vector2
0353 Acts::BoundaryCheck::computeEuclideanClosestPointOnRectangle(
0354     const Vector2& point, const Vector2& lowerLeft,
0355     const Vector2& upperRight) const {
0356   /*
0357    *
0358    *        |                 |
0359    *   IV   |       V         | I
0360    *        |                 |
0361    *  ------------------------------
0362    *        |                 |
0363    *        |                 |
0364    *   VIII |     INSIDE      | VI
0365    *        |                 |
0366    *        |                 |
0367    *  ------------------------------
0368    *        |                 |
0369    *   III  |      VII        | II
0370    *        |                 |
0371    *
0372    */
0373 
0374   double l0 = point[0], l1 = point[1];
0375   double loc0Min = lowerLeft[0], loc0Max = upperRight[0];
0376   double loc1Min = lowerLeft[1], loc1Max = upperRight[1];
0377 
0378   // check if inside
0379   if (loc0Min <= l0 && l0 < loc0Max && loc1Min <= l1 && l1 < loc1Max) {
0380     // INSIDE
0381     double dist = std::abs(loc0Max - l0);
0382     Vector2 cls(loc0Max, l1);
0383 
0384     double test = std::abs(loc0Min - l0);
0385     if (test <= dist) {
0386       dist = test;
0387       cls = {loc0Min, l1};
0388     }
0389 
0390     test = std::abs(loc1Max - l1);
0391     if (test <= dist) {
0392       dist = test;
0393       cls = {l0, loc1Max};
0394     }
0395 
0396     test = std::abs(loc1Min - l1);
0397     if (test <= dist) {
0398       return {l0, loc1Min};
0399     }
0400     return cls;
0401   } else {
0402     // OUTSIDE, check sectors
0403     if (l0 > loc0Max) {
0404       if (l1 > loc1Max) {  // I
0405         return {loc0Max, loc1Max};
0406       } else if (l1 <= loc1Min) {  // II
0407         return {loc0Max, loc1Min};
0408       } else {  // VI
0409         return {loc0Max, l1};
0410       }
0411     } else if (l0 < loc0Min) {
0412       if (l1 > loc1Max) {  // IV
0413         return {loc0Min, loc1Max};
0414       } else if (l1 <= loc1Min) {  // III
0415         return {loc0Min, loc1Min};
0416       } else {  // VIII
0417         return {loc0Min, l1};
0418       }
0419     } else {
0420       if (l1 > loc1Max) {  // V
0421         return {l0, loc1Max};
0422       } else {  // l1 <= loc1Min # VII
0423         return {l0, loc1Min};
0424       }
0425       // third case not necessary, see INSIDE above
0426     }
0427   }
0428 }