Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-09 08:04:00

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Surfaces/detail/VerticesHelper.hpp"
0010 
0011 #include <algorithm>
0012 #include <cmath>
0013 #include <cstddef>
0014 #include <numbers>
0015 
0016 namespace Acts {
0017 
0018 std::vector<double> detail::VerticesHelper::phiSegments(
0019     double phiMin, double phiMax, const std::vector<double>& phiRefs,
0020     unsigned int quarterSegments) {
0021   // Check that the phi range is valid
0022   if (phiMin > phiMax) {
0023     throw std::invalid_argument(
0024         "VerticesHelper::phiSegments ... Minimum phi must be smaller than "
0025         "maximum phi");
0026   }
0027 
0028   // First check that no reference phi is outside the range
0029   for (double phiRef : phiRefs) {
0030     if (phiRef < phiMin || phiRef > phiMax) {
0031       throw std::invalid_argument(
0032           "VerticesHelper::phiSegments ... Reference phi is outside the range "
0033           "of the segment");
0034     }
0035   }
0036   if (quarterSegments == 0u) {
0037     throw std::invalid_argument(
0038         "VerticesHelper::phiSegments ... Number of segments must be larger "
0039         "than 0.");
0040   }
0041   std::vector<double> phiSegments = {phiMin, phiMax};
0042   // Minimum approximation for a circle need
0043   // - if the circle is closed the last point is given twice
0044   for (unsigned int i = 0; i < 4 * quarterSegments + 1; ++i) {
0045     double phiExt =
0046         -std::numbers::pi + i * 2 * std::numbers::pi / (4 * quarterSegments);
0047     if (phiExt > phiMin && phiExt < phiMax &&
0048         std::ranges::none_of(
0049             phiSegments,
0050             [&phiExt](double phi) {
0051               return std::abs(phi - phiExt) <
0052                      std::numeric_limits<double>::epsilon();
0053             })) {
0054       phiSegments.push_back(phiExt);
0055     }
0056   }
0057   // Add the reference phis
0058   for (const auto& phiRef : phiRefs) {
0059     if (phiRef > phiMin && phiRef < phiMax) {
0060       if (std::ranges::none_of(phiSegments, [&phiRef](double phi) {
0061             return std::abs(phi - phiRef) <
0062                    std::numeric_limits<double>::epsilon();
0063           })) {
0064         phiSegments.push_back(phiRef);
0065       }
0066     }
0067   }
0068 
0069   // Sort the phis
0070   std::ranges::sort(phiSegments);
0071   return phiSegments;
0072 }
0073 
0074 std::vector<Vector2> detail::VerticesHelper::ellipsoidVertices(
0075     double innerRx, double innerRy, double outerRx, double outerRy,
0076     double avgPhi, double halfPhi, unsigned int quarterSegments) {
0077   // List of vertices counter-clockwise starting at smallest phi w.r.t center,
0078   // for both inner/outer ring/segment
0079   std::vector<Vector2> rvertices;  // return vertices
0080   std::vector<Vector2> ivertices;  // inner vertices
0081   std::vector<Vector2> overtices;  // outer verices
0082 
0083   bool innerExists = (innerRx > 0. && innerRy > 0.);
0084   bool closed = std::abs(halfPhi - std::numbers::pi) < s_onSurfaceTolerance;
0085 
0086   std::vector<double> refPhi = {};
0087   if (avgPhi != 0.) {
0088     refPhi.push_back(avgPhi);
0089   }
0090 
0091   // The inner (if exists) and outer bow
0092   if (innerExists) {
0093     ivertices = segmentVertices<Vector2, Transform2>(
0094         {innerRx, innerRy}, avgPhi - halfPhi, avgPhi + halfPhi, refPhi,
0095         quarterSegments);
0096   }
0097   overtices = segmentVertices<Vector2, Transform2>(
0098       {outerRx, outerRy}, avgPhi - halfPhi, avgPhi + halfPhi, refPhi,
0099       quarterSegments);
0100 
0101   // We want to keep the same counter-clockwise orientation for displaying
0102   if (!innerExists) {
0103     if (!closed) {
0104       // Add the center case we have a sector
0105       rvertices.push_back(Vector2(0., 0.));
0106     }
0107     rvertices.insert(rvertices.end(), overtices.begin(), overtices.end());
0108   } else if (!closed) {
0109     rvertices.insert(rvertices.end(), overtices.begin(), overtices.end());
0110     rvertices.insert(rvertices.end(), ivertices.rbegin(), ivertices.rend());
0111   } else {
0112     rvertices.insert(rvertices.end(), overtices.begin(), overtices.end());
0113     rvertices.insert(rvertices.end(), ivertices.begin(), ivertices.end());
0114   }
0115   return rvertices;
0116 }
0117 
0118 std::vector<Vector2> detail::VerticesHelper::circularVertices(
0119     double innerR, double outerR, double avgPhi, double halfPhi,
0120     unsigned int quarterSegments) {
0121   return ellipsoidVertices(innerR, innerR, outerR, outerR, avgPhi, halfPhi,
0122                            quarterSegments);
0123 }
0124 
0125 bool detail::VerticesHelper::onHyperPlane(const std::vector<Vector3>& vertices,
0126                                           double tolerance) {
0127   // Obvious always on one surface
0128   if (vertices.size() < 4) {
0129     return true;
0130   }
0131   // Create the hyperplane
0132   auto hyperPlane = Eigen::Hyperplane<double, 3>::Through(
0133       vertices[0], vertices[1], vertices[2]);
0134   for (std::size_t ip = 3; ip < vertices.size(); ++ip) {
0135     if (hyperPlane.absDistance(vertices[ip]) > tolerance) {
0136       return false;
0137     }
0138   }
0139   return true;
0140 }
0141 
0142 Vector2 detail::VerticesHelper::computeClosestPointOnPolygon(
0143     const Vector2& point, std::span<const Vector2> vertices,
0144     const SquareMatrix2& metric) {
0145   auto squaredNorm = [&](const Vector2& x) {
0146     return (x.transpose() * metric * x).value();
0147   };
0148 
0149   // calculate the closest position on the segment between `ll0` and `ll1` to
0150   // the point as measured by the metric induced by the metric matrix
0151   auto closestOnSegment = [&](auto&& ll0, auto&& ll1) {
0152     // normal vector and position of the closest point along the normal
0153     auto n = ll1 - ll0;
0154     auto n_transformed = metric * n;
0155     auto f = n.dot(n_transformed);
0156     auto u = std::isnormal(f)
0157                  ? (point - ll0).dot(n_transformed) / f
0158                  : 0.5;  // ll0 and ll1 are so close it doesn't matter
0159     // u must be in [0, 1] to still be on the polygon segment
0160     return ll0 + std::clamp(u, 0.0, 1.0) * n;
0161   };
0162 
0163   auto iv = std::begin(vertices);
0164   Vector2 l0 = *iv;
0165   Vector2 l1 = *(++iv);
0166   Vector2 closest = closestOnSegment(l0, l1);
0167   auto closestDist = squaredNorm(closest - point);
0168   // Calculate the closest point on other connecting lines and compare distances
0169   for (++iv; iv != std::end(vertices); ++iv) {
0170     l0 = l1;
0171     l1 = *iv;
0172     Vector2 current = closestOnSegment(l0, l1);
0173     auto currentDist = squaredNorm(current - point);
0174     if (currentDist < closestDist) {
0175       closest = current;
0176       closestDist = currentDist;
0177     }
0178   }
0179   // final edge from last vertex back to the first vertex
0180   Vector2 last = closestOnSegment(l1, *std::begin(vertices));
0181   if (squaredNorm(last - point) < closestDist) {
0182     closest = last;
0183   }
0184   return closest;
0185 }
0186 
0187 Vector2 detail::VerticesHelper::computeEuclideanClosestPointOnRectangle(
0188     const Vector2& point, const Vector2& lowerLeft, const Vector2& upperRight) {
0189   /*
0190    *
0191    *        |                 |
0192    *   IV   |       V         | I
0193    *        |                 |
0194    *  ------------------------------
0195    *        |                 |
0196    *        |                 |
0197    *   VIII |     INSIDE      | VI
0198    *        |                 |
0199    *        |                 |
0200    *  ------------------------------
0201    *        |                 |
0202    *   III  |      VII        | II
0203    *        |                 |
0204    *
0205    */
0206 
0207   double l0 = point[0];
0208   double l1 = point[1];
0209   double loc0Min = lowerLeft[0];
0210   double loc0Max = upperRight[0];
0211   double loc1Min = lowerLeft[1];
0212   double loc1Max = upperRight[1];
0213 
0214   // check if inside
0215   if (loc0Min <= l0 && l0 < loc0Max && loc1Min <= l1 && l1 < loc1Max) {
0216     // INSIDE
0217     double dist = std::abs(loc0Max - l0);
0218     Vector2 cls(loc0Max, l1);
0219 
0220     double test = std::abs(loc0Min - l0);
0221     if (test <= dist) {
0222       dist = test;
0223       cls = {loc0Min, l1};
0224     }
0225 
0226     test = std::abs(loc1Max - l1);
0227     if (test <= dist) {
0228       dist = test;
0229       cls = {l0, loc1Max};
0230     }
0231 
0232     test = std::abs(loc1Min - l1);
0233     if (test <= dist) {
0234       return {l0, loc1Min};
0235     }
0236     return cls;
0237   } else {
0238     // OUTSIDE, check sectors
0239     if (l0 > loc0Max) {
0240       if (l1 > loc1Max) {  // I
0241         return {loc0Max, loc1Max};
0242       } else if (l1 <= loc1Min) {  // II
0243         return {loc0Max, loc1Min};
0244       } else {  // VI
0245         return {loc0Max, l1};
0246       }
0247     } else if (l0 < loc0Min) {
0248       if (l1 > loc1Max) {  // IV
0249         return {loc0Min, loc1Max};
0250       } else if (l1 <= loc1Min) {  // III
0251         return {loc0Min, loc1Min};
0252       } else {  // VIII
0253         return {loc0Min, l1};
0254       }
0255     } else {
0256       if (l1 > loc1Max) {  // V
0257         return {l0, loc1Max};
0258       } else {  // l1 <= loc1Min # VII
0259         return {l0, loc1Min};
0260       }
0261       // third case not necessary, see INSIDE above
0262     }
0263   }
0264 }
0265 
0266 Vector2 detail::VerticesHelper::computeClosestPointOnAlignedBox(
0267     const Vector2& lowerLeft, const Vector2& upperRight, const Vector2& point,
0268     const SquareMatrix2& metric) {
0269   Vector2 closestPoint;
0270 
0271   if (metric.isIdentity()) {
0272     closestPoint =
0273         detail::VerticesHelper::computeEuclideanClosestPointOnRectangle(
0274             point, lowerLeft, upperRight);
0275   } else {
0276     // TODO there might be a more optimal way to compute the closest point to a
0277     // box with metric
0278 
0279     std::array<Vector2, 4> vertices = {{lowerLeft,
0280                                         {upperRight[0], lowerLeft[1]},
0281                                         upperRight,
0282                                         {lowerLeft[0], upperRight[1]}}};
0283 
0284     closestPoint = detail::VerticesHelper::computeClosestPointOnPolygon(
0285         point, vertices, metric);
0286   }
0287 
0288   return closestPoint;
0289 }
0290 
0291 }  // namespace Acts