Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-17 08:35:32

0001 #ifndef VECGEOM_SURFACE_COMMONTYPES_H
0002 #define VECGEOM_SURFACE_COMMONTYPES_H
0003 
0004 #include "VecGeom/base/Assert.h"
0005 #include <VecGeom/base/Transformation3D.h>
0006 #include <VecGeom/base/Transformation3DMP.h>
0007 #include <VecGeom/base/Transformation2DMP.h>
0008 #include <VecGeom/base/Vector2D.h>
0009 #include <VecGeom/base/Vector3D.h>
0010 #include <VecGeom/volumes/kernel/GenericKernels.h>
0011 #include <VecGeom/navigation/NavigationState.h>
0012 #include <VecGeom/management/Logger.h>
0013 
0014 namespace vgbrep {
0015 
0016 ///< VecGeom type aliases
0017 
0018 using Precision      = vecgeom::Precision;
0019 using uint           = vecgeom::uint;
0020 using NavIndex_t     = vecgeom::NavIndex_t;
0021 using Transformation = vecgeom::Transformation3D;
0022 
0023 template <typename Real_t>
0024 using Vector3D = vecgeom::Vector3D<Real_t>;
0025 template <typename Real_t>
0026 using Vector2D = vecgeom::Vector2D<Real_t>;
0027 template <typename Real_t>
0028 using TransformationMP = vecgeom::Transformation3DMP<Real_t>;
0029 
0030 using logic_int = int;
0031 
0032 /// @brief Operators used inside Boolean expressions
0033 enum OperatorToken : logic_int {
0034   lbegin = logic_int(0x7FFFFFFF - 8),
0035   lfalse = lbegin, ///< Push 'false'
0036   ltrue,           ///< Push 'true'
0037   lplus,           ///< increase logical depth
0038   lminus,          ///< decrease logical depth
0039   lnot,            ///< Unary negation
0040   lor,             ///< Binary logical OR
0041   land,            ///< Binary logical AND
0042   lend
0043 };
0044 
0045 struct LogicExpression {
0046   logic_int size_;
0047   logic_int *data_{nullptr};
0048 
0049   static VECCORE_ATT_HOST_DEVICE bool is_operator_token(logic_int lv) { return (lv >= lbegin); }
0050 
0051   VECCORE_ATT_HOST_DEVICE
0052   unsigned size() const { return (unsigned)size_; }
0053 
0054   VECCORE_ATT_HOST_DEVICE
0055   logic_int operator[](unsigned i) const { return data_[i]; }
0056 };
0057 
0058 ///< Supported surface types
0059 ///< kPlanar        <- planar (xOy) half-space having the normal along z direction
0060 ///< kCylindrical   <- cylindrical half-space around the z-axis, having the normals pointing outwards
0061 ///< kConical       <- conical half-space around z-axis, having the normals pointing outwards
0062 ///< kSpherical     <- sphere centered in origin, normals pointing outwards
0063 ///< kTorus         <- toroidal surface hafing the median circle in the xy plane, cenetred in origin
0064 ///< kElliptical    <- elliptical half-space around the z-axis, having the normals pointing outwards
0065 ///< kArb4          <- twisted surface defined by 4 non co-planar vertices
0066 enum class SurfaceType : char { kNoSurf, kPlanar, kCylindrical, kConical, kSpherical, kTorus, kElliptical, kArb4 };
0067 
0068 ///< Supported frame types
0069 ///< kNoFrame       <- no frame, used for Inside only
0070 ///< kRangeZ        <- range along z-axis
0071 ///< kRing          <- a "ring" range on a plane
0072 ///< kZPhi          <- z and phi range on a cylinder
0073 ///< kRangeSph      <- theta and phi range on a sphere
0074 ///< kWindow        <- rectangular range in xy-plane
0075 ///< kTriangle      <- triangular range in xy-plane
0076 ///< kQuadrilateral <- planar quadrilateral in xy-plane
0077 enum class FrameType : char { kNoFrame, kRangeZ, kRing, kZPhi, kRangeSph, kWindow, kTriangle, kQuadrilateral };
0078 
0079 ///< Segment-segment intersection types
0080 enum class SegmentIntersect : char { kNoIntersect, kEmbedding, kEmbedded, kOverlap, kIntersect, kEqual };
0081 using FrameIntersect = SegmentIntersect;
0082 
0083 /// @brief A list of candidate surfaces
0084 struct Candidates {
0085   int fNcand{0};             ///< Number of candidate surfaces
0086   int fNExiting{0};          ///< Number of exiting candidate surfaces. fNcand = NEntering + NExiting
0087   int *fCandidates{nullptr}; ///< [fNcand] Array of candidates
0088   int *fFrameInd{nullptr};   ///< [fNcand] Start index of the frame contributed by the touchable on the common surface
0089   char *fSides{nullptr};     ///< [fNcand] Side containing relevant frames for each candidate (0=left, 1=right, 2=both)
0090 
0091   VECCORE_ATT_HOST_DEVICE
0092   int operator[](int i) const { return fCandidates[i]; }
0093   VECCORE_ATT_HOST_DEVICE
0094   int operator[](int i) { return fCandidates[i]; }
0095 
0096   Candidates() = default;
0097 };
0098 
0099 /// @brief Framed surface locator
0100 struct FSlocatorB {
0101   int common_id{0}; ///< Common surface id (positive = left side, negative = right side)
0102   int frame_id{-1}; ///< frame index on the side
0103 
0104   FSlocatorB() = default;
0105 
0106   FSlocatorB(int csind, int iframe) : common_id(csind), frame_id(iframe) {}
0107 
0108   VECCORE_ATT_HOST_DEVICE
0109   VECGEOM_FORCE_INLINE
0110   FSlocatorB(int csind, int iframe, bool left) { Set(csind, iframe, left); }
0111 
0112   VECCORE_ATT_HOST_DEVICE
0113   VECGEOM_FORCE_INLINE
0114   void Set(int csind, int iframe, bool left)
0115   {
0116     common_id = (2 * int(left) - 1) * csind;
0117     frame_id  = iframe;
0118   }
0119 
0120   VECCORE_ATT_HOST_DEVICE
0121   VECGEOM_FORCE_INLINE
0122   int GetCSindex() const { return vecCore::math::Abs(common_id); }
0123 
0124   VECCORE_ATT_HOST_DEVICE
0125   VECGEOM_FORCE_INLINE
0126   bool IsLeftSide() const { return common_id > 0; }
0127 
0128   VECCORE_ATT_HOST_DEVICE
0129   VECGEOM_FORCE_INLINE
0130   int GetFSindex() const { return frame_id; }
0131 };
0132 
0133 #ifdef VECGEOM_USE_SURF
0134 /// @brief Framed surface locator
0135 struct FSlocator : FSlocatorB {
0136   vecgeom::NavigationState state; ///< full state associated to the frame
0137 
0138   VECCORE_ATT_HOST_DEVICE
0139   VECGEOM_FORCE_INLINE
0140   FSlocator() : FSlocatorB() {}
0141 
0142   VECCORE_ATT_HOST_DEVICE
0143   VECGEOM_FORCE_INLINE
0144   FSlocator(int csind, int iframe) : FSlocatorB(csind, iframe) {}
0145 
0146   VECCORE_ATT_HOST_DEVICE
0147   VECGEOM_FORCE_INLINE
0148   FSlocator(int csind, int iframe, bool left) : FSlocatorB(csind, iframe, left) {}
0149 };
0150 
0151 /// @brief Exit surf data, contains all relevant surface information
0152 struct CrossedSurface {
0153   FSlocator exit_surf; // containing the surface information of the highest exited frame
0154   FSlocator hit_surf;  // containing the surface data of the last frame hit (exited or entered)
0155 
0156   // Default constructor
0157   CrossedSurface() = default;
0158 
0159   // Constructor with initialization
0160   VECCORE_ATT_HOST_DEVICE
0161   VECGEOM_FORCE_INLINE
0162   CrossedSurface(int csind1, int iframe1, bool left1, int csind2, int iframe2, bool left2)
0163       : exit_surf(csind1, iframe1, left1), hit_surf(csind2, iframe2, left2)
0164   {
0165   }
0166 
0167   // Method to set both FSlocators
0168   VECCORE_ATT_HOST_DEVICE
0169   VECGEOM_FORCE_INLINE
0170   void Set(int csind1, int iframe1, bool left1, int csind2, int iframe2, bool left2)
0171   {
0172     exit_surf.Set(csind1, iframe1, left1);
0173     hit_surf.Set(csind2, iframe2, left2);
0174   }
0175 
0176   VECCORE_ATT_HOST_DEVICE
0177   VECGEOM_FORCE_INLINE
0178   void Set(int csind, int iframe, bool left)
0179   {
0180     exit_surf.Set(csind, iframe, left);
0181     hit_surf.Set(csind, iframe, left);
0182   }
0183 };
0184 
0185 struct SliceCand {
0186   int fNcand{0};             ///< Number of candidate frames in the slice
0187   int *fCandidates{nullptr}; ///< [fNcand] Array of candidates
0188 };
0189 
0190 enum AxisType : char {
0191   kX,     ///< X axis
0192   kY,     ///< Y axis
0193   kZ,     ///< Z axis
0194   kR,     ///< Radial axis
0195   kPhi,   ///< Phi axis
0196   kXY,    ///< XY grid
0197   kNoAxis ///< No axis
0198 };
0199 #endif
0200 
0201 template <typename Real_t>
0202 VECCORE_ATT_HOST_DEVICE bool ApproxEqual(Real_t t1, Real_t t2)
0203 {
0204   return std::abs(t1 - t2) <= vecgeom::kToleranceDist<Real_t>;
0205 }
0206 
0207 template <typename Real_t>
0208 VECCORE_ATT_HOST_DEVICE bool ApproxEqualVector(Vector3D<Real_t> const &v1, Vector3D<Real_t> const &v2)
0209 {
0210   return ApproxEqual(v1[0], v2[0]) && ApproxEqual(v1[1], v2[1]) && ApproxEqual(v1[2], v2[2]);
0211 }
0212 
0213 template <typename Real_t>
0214 VECCORE_ATT_HOST_DEVICE bool ApproxEqualVector2(Vector2D<Real_t> const &v1, Vector2D<Real_t> const &v2)
0215 {
0216   return ApproxEqual(v1[0], v2[0]) && ApproxEqual(v1[1], v2[1]);
0217 }
0218 
0219 template <typename Real_t>
0220 bool ApproxEqualTransformation(const vecgeom::Transformation3DMP<Real_t> &t1,
0221                                const vecgeom::Transformation3DMP<Real_t> &t2)
0222 {
0223   if (!ApproxEqualVector(t1.Translation(), t2.Translation())) return false;
0224   for (int i = 0; i < 9; ++i)
0225     if (!ApproxEqual(t1.Rotation(i), t2.Rotation(i))) return false;
0226   return true;
0227 }
0228 
0229 /// @brief Truncate a value to as many significant digits as a given tolerance.
0230 ///  For example, if the tolerance is 1e-9, truncate the vlaue to 9 significant digits
0231 /// @tparam Real_t Precision type
0232 /// @param x Value to truncate
0233 /// @param tolerance Tolerance with as many significant digits as the truncation result
0234 /// @return Truncated value
0235 template <typename Real_t>
0236 VECCORE_ATT_HOST_DEVICE Real_t TruncateValue(Real_t x, Real_t tolerance = vecgeom::kToleranceDist<Real_t>())
0237 {
0238   auto div = std::abs(x);
0239   while (int(div) > 0) {
0240     div /= 10;
0241     tolerance *= 10;
0242   }
0243   return tolerance * std::lround(x / tolerance);
0244 }
0245 
0246 /// @brief Return the rounding error of a value, assuming as many significant digits as a provided tolerance
0247 /// @tparam Real_t Precision type
0248 /// @param x Value subject to rounding
0249 /// @param tolerance Tolerance with as many significant digits as the truncation result
0250 /// @return Truncation error
0251 template <typename Real_t>
0252 VECCORE_ATT_HOST_DEVICE Real_t RoundingError(Real_t x, Real_t tolerance = vecgeom::kToleranceDist<Real_t>())
0253 {
0254   auto div = std::abs(x);
0255   while (int(div) > 0) {
0256     div /= 10;
0257     tolerance *= 10;
0258   }
0259   return tolerance;
0260 }
0261 
0262 /// @brief Computes squared distance from a point to a segment
0263 /// @tparam Real_t Precision type
0264 /// @param point Point from which the distance is computed
0265 /// @param p1 Segment start
0266 /// @param p2 Segment end
0267 /// @return Distance to the segment
0268 template <typename Point>
0269 VECCORE_ATT_HOST_DEVICE auto DistanceToSegmentSquared(Point const &p, Point const &p1, Point const &p2)
0270 {
0271   auto line = p2 - p1;
0272   auto pvec = p - p1;
0273   auto dot0 = line.Dot(pvec);
0274   if (dot0 <= 0) return pvec.Mag2();
0275   auto dot1 = line.Mag2();
0276   if (dot1 <= dot0) return (p - p2).Mag2();
0277   return ((dot0 / dot1) * line - pvec).Mag2();
0278 }
0279 
0280 /// @brief Helper for dividing a side in equal slices along one axis, keeping frame candidates in each slice
0281 /// @details The range given by the side extent is divided in equal slices along an axis. Each
0282 ///          slice intersects a number of frames. The slice is found besed on the crossing point, then the full
0283 ///          frame loops are reduced to the list of candidates in that slice.
0284 template <typename Real_t>
0285 struct SideDivision {
0286   Real_t fStartU{0.};                ///< Division start on primary division axis
0287   Real_t fStepU{0.};                 ///< Division step on primary division axis
0288   Real_t fStartV{0.};                ///< Division start on secondary axis (if any)
0289   Real_t fStepV{0.};                 ///< Division step on secondary axis (if any)
0290   unsigned short fNslices{0};        ///< Total number of slices
0291   unsigned short fNslicesU{0};       ///< Number of slices on primary division axis
0292   unsigned short fNslicesV{0};       ///< Number of slices on secondary division axis
0293   AxisType fAxis{AxisType::kNoAxis}; ///< Division axis
0294   SliceCand *fSlices{nullptr};       ///< Array of slices
0295 
0296   // Methods
0297   SideDivision() = default;
0298 
0299   VECCORE_ATT_HOST_DEVICE VECGEOM_FORCE_INLINE SliceCand const &GetSlice(int indU, int indV)
0300   {
0301     return (fAxis == AxisType::kXY) ? fSlices[fNslicesV * indU + indV] : fSlices[indU];
0302   }
0303 
0304   /// @brief Compute the slice index for a point on surface
0305   /// @tparam Real_i Precision of input point
0306   /// @param onsurf Point on surface
0307   /// @return Index of the slice containing the point, -1 if none
0308   template <typename Real_i>
0309   VECCORE_ATT_HOST_DEVICE VECGEOM_FORCE_INLINE int GetSliceIndex(Vector3D<Real_i> const &onsurf) const
0310   {
0311     int ind = -1;
0312     switch (fAxis) {
0313     case AxisType::kXY: {
0314       int indU = (onsurf[0] - fStartU) / fStepU;
0315       int indV = (onsurf[1] - fStartV) / fStepV;
0316       ind      = (indU >= 0 && indV >= 0 && indU < fNslicesU && indV < fNslicesV) ? fNslicesV * indU + indV : -1;
0317       return ind;
0318     }
0319     case AxisType::kR: {
0320       ind = (onsurf.Perp() - fStartU) / fStepU;
0321       return (ind < fNslices) ? ind : -1;
0322     }
0323     case AxisType::kPhi: {
0324       ind = (onsurf.Phi() - fStartU) / fStepU;
0325       return (ind >= 0 && ind < fNslices) ? ind : -1;
0326     }
0327     default:
0328       ind = (onsurf[fAxis] - fStartU) / fStepU;
0329       return (ind >= 0 && ind < fNslices) ? ind : -1;
0330     }
0331   }
0332 
0333   /// @brief Get number of touched slices by a bounding box aligned with the surface reference frame
0334   /// @tparam Real_i Precision of input box corners
0335   /// @param corner1 Lower box corner
0336   /// @param corner2 Upper box corner
0337   /// @param u1 Lower first axis touched slice
0338   /// @param u2 Upper first axis touched slice
0339   /// @param v1 Lower second axis touched slice
0340   /// @param v2 Upper second axis touched slice
0341   /// @return Number of touched slices
0342   template <typename Real_i>
0343   VECGEOM_FORCE_INLINE int GetNslices(Vector3D<Real_i> const &corner1, Vector3D<Real_i> const &corner2, int &u1,
0344                                       int &u2, int &v1, int &v2) const
0345   {
0346     u1 = -1;
0347     u2 = -1;
0348     v1 = -1;
0349     v2 = -1;
0350     switch (fAxis) {
0351     case kXY: {
0352       int ind1     = (corner1[0] - fStartU) / fStepU;
0353       int ind2     = (corner2[0] - fStartU) / fStepU;
0354       int indmin   = std::min(ind1, ind2);
0355       int indmax   = std::max(ind1, ind2);
0356       u1           = std::max(indmin, 0);
0357       u2           = std::min(indmax, fNslicesU - 1);
0358       int nslicesx = u2 - u1 + 1;
0359       nslicesx     = std::max(nslicesx, 0);
0360       ind1         = (corner1[1] - fStartV) / fStepV;
0361       ind2         = (corner2[1] - fStartV) / fStepV;
0362       indmin       = std::min(ind1, ind2);
0363       indmax       = std::max(ind1, ind2);
0364       v1           = std::max(indmin, 0);
0365       v2           = std::min(indmax, fNslicesV - 1);
0366       int nslicesy = v2 - v1 + 1;
0367       nslicesy     = std::max(nslicesy, 0);
0368       return (nslicesx * nslicesy);
0369     }
0370     case AxisType::kR: {
0371       auto rmax  = std::max(corner1.Perp(), corner2.Perp());
0372       u1         = 0;
0373       int indmax = (rmax - fStartU) / fStepU;
0374       u2         = std::min(indmax, fNslices - 1);
0375       return (u2 + 1);
0376     }
0377     case AxisType::kPhi: {
0378       int ind1    = (corner1.Phi() - fStartU) / fStepU;
0379       int ind2    = (corner2.Phi() - fStartU) / fStepU;
0380       int indmin  = std::min(ind1, ind2);
0381       int indmax  = std::max(ind1, ind2);
0382       u1          = std::max(indmin, 0);
0383       u2          = std::min(indmax, fNslices - 1);
0384       int nslices = u2 - u1 + 1;
0385       return std::max(nslices, 0);
0386     }
0387     default:
0388       int ind1    = (corner1[fAxis] - fStartU) / fStepU;
0389       int ind2    = (corner2[fAxis] - fStartU) / fStepU;
0390       int indmin  = std::min(ind1, ind2);
0391       int indmax  = std::max(ind1, ind2);
0392       u1          = std::max(indmin, 0);
0393       u2          = std::min(indmax, fNslices - 1);
0394       int nslices = u2 - u1 + 1;
0395       return std::max(nslices, 0);
0396     }
0397   }
0398 };
0399 
0400 // Aliases for different usages of Vec2D.
0401 template <typename Real_t>
0402 using Range = Vector2D<Real_t>;
0403 
0404 template <typename Real_t>
0405 using AngleVector = Vector2D<Real_t>;
0406 
0407 template <typename Real_t>
0408 using Point2D = Vector2D<Real_t>;
0409 
0410 template <typename Real_t>
0411 struct AngleInterval {
0412   Range<Real_t> range;
0413 
0414   AngleInterval(Real_t start, Real_t end, bool normalize = true)
0415   {
0416     range[0] = start;
0417     range[1] = end;
0418     if (normalize) Normalize();
0419   }
0420 
0421   bool operator<(AngleInterval const &other) { return (range[1] - other.range[0]) < vecgeom::kToleranceStrict<Real_t>; }
0422   bool operator>(AngleInterval const &other) { return (other.range[1] - range[0]) < vecgeom::kToleranceStrict<Real_t>; }
0423   bool operator==(AngleInterval const &other)
0424   {
0425     // Move the other interval left until not larger than this one
0426     AngleInterval other_moved = other;
0427     while (other_moved > *this)
0428       other_moved = other_moved.Previous();
0429     // Move the interval right until not smaller than this one
0430     while (other_moved < *this)
0431       other_moved = other_moved.Next();
0432     bool equal = vecCore::math::Abs(range[0] - other_moved.range[0]) < vecgeom::kToleranceStrict<Real_t> &&
0433                  vecCore::math::Abs(range[1] - other_moved.range[1]) < vecgeom::kToleranceStrict<Real_t>;
0434     return equal;
0435   }
0436   bool operator!=(AngleInterval const &other) { return !operator==(other); }
0437 
0438   void Clear() { range[0] = range[1] = Real_t(0); }
0439   bool IsNull() const { return (range[1] - range[0]) < vecgeom::kToleranceStrict<Real_t>; }
0440   // Normalize the angle such that start is in [0, 2*pi) and end >= start
0441   void Normalize()
0442   {
0443     range[0] = std::fmod(range[0], vecgeom::kTwoPi) + vecgeom::kTwoPi * (range[0] < Real_t(0));
0444     range[1] = std::fmod(range[1], vecgeom::kTwoPi) + vecgeom::kTwoPi * (range[1] < Real_t(0));
0445     range[1] += vecgeom::kTwoPi * (range[1] - range[0] < vecgeom::kToleranceStrict<Real_t>);
0446   }
0447 
0448   AngleInterval Next() const { return AngleInterval(range[0] + vecgeom::kTwoPi, range[1] + vecgeom::kTwoPi, false); }
0449   AngleInterval Previous() const
0450   {
0451     return AngleInterval(range[0] - vecgeom::kTwoPi, range[1] - vecgeom::kTwoPi, false);
0452   }
0453 
0454   AngleInterval Intersect(AngleInterval const &other)
0455   {
0456     // Move the other interval left until not larger than this one
0457     AngleInterval other_moved = other;
0458     while (other_moved > *this)
0459       other_moved = other_moved.Previous();
0460     // Move the interval right until not smaller than this one
0461     while (other_moved < *this)
0462       other_moved = other_moved.Next();
0463     // No either the moved interval is larger or it overlaps
0464     if (other_moved > *this) return AngleInterval(Real_t(0), Real_t(0), false);
0465     return AngleInterval(vecCore::math::Max(range[0], other_moved.range[0]),
0466                          vecCore::math::Min(range[1], other_moved.range[1]));
0467   }
0468 };
0469 
0470 template <typename Real_t>
0471 struct Circle2D {
0472   Real_t fR;          ///< Circle radius
0473   Point2D<Real_t> fC; ///< Center
0474   Circle2D(Real_t r, Point2D<Real_t> const &center) : fR{r}, fC{center} {}
0475 };
0476 
0477 template <typename Real_t>
0478 struct Segment2D {
0479   Point2D<Real_t> fP1;
0480   Point2D<Real_t> fP2;
0481   Vector2D<Real_t> fV;
0482   bool fDegen{false};
0483 
0484   Segment2D(Point2D<Real_t> const &p1, Point2D<Real_t> const &p2)
0485   {
0486     fP1    = p1;
0487     fP2    = p2;
0488     fV     = fP2 - fP1;
0489     fDegen = fV.Mag2() < vecgeom::kToleranceDistSquared<Real_t>;
0490   }
0491 
0492   /// @brief Segment intersection with a circle.
0493   /// @param circle Circle to check against
0494   /// @return Intersection result. Can be kEmbedded, kIntersect or kNoIntersect
0495   SegmentIntersect Intersect(Circle2D<Real_t> const &circle) const
0496   {
0497     Vector2D<Real_t> v1 = circle.fC - fP1;
0498     Vector2D<Real_t> v2 = circle.fC - fP2;
0499     bool inside1        = false;
0500     bool inside2        = false;
0501     if (v1.Dot(fV) < vecgeom::MakePlusTolerant<true, Real_t>(0) ||
0502         v2.Dot(fV) > vecgeom::MakeMinusTolerant<true, Real_t>(0)) {
0503       // One of the segment points is the closest to the circle center (includes the degen case)
0504       // Check the inside of each point and compare them
0505       inside1 = v1.Mag() < vecgeom::MakeMinusTolerant<true, Real_t>(circle.fR);
0506       inside2 = v2.Mag() < vecgeom::MakeMinusTolerant<true, Real_t>(circle.fR);
0507       if (inside1 && inside2) return SegmentIntersect::kEmbedded;
0508       if (inside1 ^ inside2) return SegmentIntersect::kIntersect;
0509       return SegmentIntersect::kNoIntersect;
0510     }
0511     // There is a point on the segment closest to the center (no degen here)
0512     auto saf = std::abs(v1.CrossZ(fV)) / fV.Mag();
0513     if (saf > vecgeom::MakeMinusTolerant<true, Real_t>(circle.fR)) return SegmentIntersect::kNoIntersect;
0514     // The closest point is inside the circle
0515     inside1 = v1.Mag() < vecgeom::MakeMinusTolerant<true, Real_t>(circle.fR);
0516     inside2 = v2.Mag() < vecgeom::MakeMinusTolerant<true, Real_t>(circle.fR);
0517     if (inside1 && inside2) return SegmentIntersect::kEmbedded;
0518     return SegmentIntersect::kIntersect;
0519   }
0520 
0521   bool BoundingBoxOverlap(Segment2D<Real_t> const &other) const
0522   {
0523     Real_t min_x1 = std::min(fP1.x(), fP2.x());
0524     Real_t max_x1 = std::max(fP1.x(), fP2.x());
0525     Real_t min_y1 = std::min(fP1.y(), fP2.y());
0526     Real_t max_y1 = std::max(fP1.y(), fP2.y());
0527 
0528     Real_t min_x2 = std::min(other.fP1.x(), other.fP2.x());
0529     Real_t max_x2 = std::max(other.fP1.x(), other.fP2.x());
0530     Real_t min_y2 = std::min(other.fP1.y(), other.fP2.y());
0531     Real_t max_y2 = std::max(other.fP1.y(), other.fP2.y());
0532 
0533     return !(max_x1 - min_x2 < vecgeom::kToleranceDist<Real_t> || max_x2 - min_x1 < vecgeom::kToleranceDist<Real_t> ||
0534              max_y1 - min_y2 < vecgeom::kToleranceDist<Real_t> || max_y2 - min_y1 < vecgeom::kToleranceDist<Real_t>);
0535   }
0536 
0537   /// @brief Intersection with another segment
0538   /// @param other Other segment
0539   /// @return Intersection type
0540   SegmentIntersect Intersect(Segment2D<Real_t> const &other) const
0541   {
0542     // Return kNoIntersect if one of the segments is degenerated
0543     if (fDegen || other.fDegen) return SegmentIntersect::kNoIntersect;
0544     auto v12  = other.fP1 - fP1;
0545     auto s    = v12.CrossZ(other.fV);
0546     auto t    = v12.CrossZ(fV);
0547     auto norm = fV.CrossZ(other.fV);
0548     auto l1   = fV.Length();
0549     auto l2   = other.fV.Length();
0550     if (std::abs(norm) < 2. * vecCore::math::Max(l1, l2) * vecgeom::kToleranceDist<Real_t>) {
0551       // the segments are parallel, but how far
0552       if (std::abs(s / other.fV.Length()) < vecgeom::kToleranceDist<Real_t>) {
0553         // the segments are colinear -> compute intersection distances with other segment ends
0554         auto inv_vsq = l1 / fV.Dot(fV);
0555         auto t1      = inv_vsq * fV.Dot(v12);
0556         auto t2      = inv_vsq * fV.Dot(v12 + other.fV);
0557         if (t1 > t2) std::swap(t1, t2);
0558         // Compute intersection between [0, l1] and [t1, t2]
0559         if (ApproxEqual(t1, Real_t(0)) && ApproxEqual(t2, l1)) return SegmentIntersect::kEqual;
0560         auto start = std::max(0., t1);
0561         auto end   = std::min(l1, t2);
0562         if (end - start < vecgeom::kToleranceDist<Real_t>) return SegmentIntersect::kNoIntersect;
0563         if (ApproxEqual(start, t1) && ApproxEqual(end, t2)) return SegmentIntersect::kEmbedding;
0564         if (ApproxEqual(start, Real_t(0)) && ApproxEqual(end, l1)) return SegmentIntersect::kEmbedded;
0565         return SegmentIntersect::kOverlap;
0566       } else
0567         // The segments are parallel
0568         return SegmentIntersect::kNoIntersect;
0569     }
0570 
0571     // Calculate the intersection points using both segments
0572     auto invnorm = 1. / norm;
0573     s *= l1 * invnorm;
0574     t *= l2 * invnorm;
0575     bool outBounds = s < vecgeom::kToleranceDist<Real_t> || (s - l1) > -vecgeom::kToleranceDist<Real_t> ||
0576                      t < vecgeom::kToleranceDist<Real_t> || (t - l2) > -vecgeom::kToleranceDist<Real_t>;
0577 
0578     if (outBounds) return SegmentIntersect::kNoIntersect;
0579     return SegmentIntersect::kIntersect;
0580   }
0581 };
0582 
0583 template <typename Real_t, char N>
0584 struct Polygon {
0585   Point2D<Real_t> fVert[N]; ///< vector of vertices
0586   Polygon(Point2D<Real_t> const vertices[N])
0587   {
0588     for (auto i = 0; i < int(N); ++i)
0589       fVert[i] = vertices[i];
0590   }
0591 
0592   Polygon(Vector3D<Real_t> const vertices[N])
0593   {
0594     for (auto i = 0; i < int(N); ++i) {
0595       fVert[i][0] = vertices[i][0];
0596       fVert[i][1] = vertices[i][1];
0597     }
0598   }
0599 
0600   Point2D<Real_t> GetCenter() const
0601   {
0602     Point2D<Real_t> center;
0603     for (auto i = 0; i < int(N); ++i)
0604       center += fVert[i];
0605     center *= Real_t(1.) / N;
0606     return center;
0607   }
0608 
0609   /// @brief Detect disjointness with another polygon
0610   /// @tparam M Number of vertices of the other polygon
0611   /// @param other Other polygon
0612   /// @return Intersection type: kIntersect or kNoIntersect
0613   template <char M>
0614   FrameIntersect Intersect(Polygon<Real_t, M> const &other) const
0615   {
0616     // Loop over segments of this polygon
0617     int noverlaps = 0;
0618     for (auto i = 0; i < int(N); ++i) {
0619       Segment2D<Real_t> crt{fVert[i], fVert[(i + 1) % N]};
0620       // Intersect with all segments of the other polygon
0621 
0622       for (auto j = 0; j < int(M); ++j) {
0623         Segment2D<Real_t> ocrt{other.fVert[j], other.fVert[(j + 1) % M]};
0624         auto intersect = crt.Intersect(ocrt);
0625         // segment intersections means polygon intersection
0626         if (intersect == SegmentIntersect::kIntersect) return FrameIntersect::kIntersect;
0627         // anything else but kNoIntersect means segment overlapping
0628         noverlaps += intersect != FrameIntersect::kNoIntersect;
0629         // more than two segment overlaps means polygon intersection
0630         if (noverlaps > 1) return FrameIntersect::kIntersect;
0631       }
0632     }
0633     // The polygones survived the intersection check, but the user needs to check embedding
0634     return FrameIntersect::kNoIntersect;
0635   }
0636 
0637   /// @brief Polygon intersection with a circle
0638   /// @param circle Circle to check for intersection
0639   /// @return Intersection type
0640   FrameIntersect Intersect(Circle2D<Real_t> const &circle) const
0641   {
0642     bool intersect = false;
0643     bool embedded  = false;
0644     for (auto i = 0; i < int(N); ++i) {
0645       Segment2D<Real_t> crt{fVert[i], fVert[(i + 1) % N]};
0646       // Intersect with the circle
0647       auto intersect_type = crt.Intersect(circle);
0648       embedded &= intersect_type == SegmentIntersect::kEmbedded;
0649       intersect |= intersect_type == SegmentIntersect::kIntersect;
0650     }
0651     if (intersect) return SegmentIntersect::kIntersect;
0652     if (embedded) return SegmentIntersect::kEmbedded;
0653     return SegmentIntersect::kNoIntersect;
0654   }
0655 };
0656 
0657 /// @brief Data for conical surfaces
0658 /// @tparam Real_t Storage type
0659 /// @tparam Real_s Interface type
0660 template <typename Real_t>
0661 struct EllipData {
0662   Real_t Rx{0}; ///< semi-axis in x
0663   Real_t Ry{0}; ///< semi-axis in y
0664   Real_t dz{0}; ///< half height of the elliptical tube, so it extends from -dz to dz
0665   Real_t R{0};  ///< radius after rescaling to circle
0666 
0667   // Precalculated cached values
0668   //
0669   Real_t fRsph; ///< Radius of bounding sphere
0670   Real_t fDDx;  ///< Dx squared
0671   Real_t fDDy;  ///< Dy squared
0672   Real_t fSx;   ///< X scale factor
0673   Real_t fSy;   ///< Y scale factor
0674   Real_t fQ1;   ///< Coefficient in the approximation of dist = Q1*(x^2+y^2) - Q2
0675   Real_t fQ2;   ///< Coefficient in the approximation of dist = Q1*(x^2+y^2) - Q2
0676 
0677   EllipData() = default;
0678   /// @tparam Real_i precision type of inputs
0679   template <typename Real_i>
0680   EllipData(Real_i radx, Real_i rady, Real_i half_z)
0681       : Rx(static_cast<Real_t>(radx)), Ry(static_cast<Real_t>(rady)), dz(static_cast<Real_t>(half_z))
0682   {
0683 
0684     R = vecCore::math::Min(Rx, Ry);
0685 
0686     // Precalculated values
0687     fRsph = vecCore::math::Sqrt(Rx * Rx + Ry * Ry + dz * dz);
0688     fDDx  = Rx * Rx;
0689     fDDy  = Ry * Ry;
0690     fSx   = R / Rx;
0691     fSy   = R / Ry;
0692 
0693     // Coefficient for approximation of distance : Q1 * (x^2 + y^2) - Q2
0694     fQ1 = 0.5 / R;
0695     fQ2 = 0.5 * (R + vecgeom::kHalfTolerance * vecgeom::kHalfTolerance / R);
0696   }
0697   template <typename Real_i>
0698   EllipData(const EllipData<Real_i> &other)
0699       : Rx(static_cast<Real_t>(other.Rx)), Ry(static_cast<Real_t>(other.Ry)), dz(static_cast<Real_t>(other.dz)),
0700         R(static_cast<Real_t>(other.R)), fRsph(static_cast<Real_t>(other.fRsph)), fDDx(static_cast<Real_t>(other.fDDx)),
0701         fDDy(static_cast<Real_t>(other.fDDy)), fSx(static_cast<Real_t>(other.fSx)), fSy(static_cast<Real_t>(other.fSy)),
0702         fQ1(static_cast<Real_t>(other.fQ1)), fQ2(static_cast<Real_t>(other.fQ2))
0703   {
0704   }
0705 };
0706 
0707 /// @brief Data for Arb4 surfaces
0708 /// @tparam Real_t Storage type
0709 /// @tparam Real_s Interface type
0710 template <typename Real_t>
0711 struct Arb4Data {
0712   using Vector3D = vecgeom::Vector3D<Real_t>;
0713 
0714   Real_t verticesX[4];
0715   Real_t verticesY[4];
0716   Real_t connecting_compX[2];
0717   Real_t connecting_compY[2];
0718   Real_t halfH;                  ///< half the height of the Arb4
0719   Real_t halfH_inv;              ///< inverse of half the height
0720   Real_t ftx1, fty1, ftx2, fty2; /** Connecting components top-bottom */
0721 
0722   Real_t ft1crosst2; /** Cross term ftx1[i]*fty2[i] - ftx2[i]*fty1[i] */
0723   Real_t fDeltatx;   /** Term ftx2[i] - ftx1[i] */
0724   Real_t fDeltaty;   /** Term fty2[i] - fty1[i] */
0725 
0726   // pre-computed cross products for normal computation
0727   Vector3D fViCrossHi0;  /** Pre-computed vi X hi0 */
0728   Vector3D fViCrossVj;   /** Pre-computed vi X vj */
0729   Vector3D fHi1CrossHi0; /** Pre-computed hi1 X hi0 */
0730 
0731 #ifdef SURF_ACCURATE_SAFETY
0732   // pre-computed normals of the plane using 3 of the 4 points of the Arb4 for additional safety calcuation
0733   Vector3D normal0;
0734   Vector3D normal1;
0735   Vector3D normal2;
0736   Vector3D normal3;
0737 #endif
0738 
0739   Arb4Data() = default;
0740   /// @tparam Real_i precision type of inputs
0741   template <typename Real_i>
0742   Arb4Data(Real_i v0_0, Real_i v0_1, Real_i v0_2, Real_i v1_0, Real_i v1_1, Real_i v2_0, Real_i v2_1, Real_i v2_2,
0743            Real_i v3_0, Real_i v3_1)
0744       : halfH(0.5 * static_cast<Real_t>(v2_2 - v0_2)), halfH_inv(1. / halfH)
0745   {
0746 
0747     verticesX[0] = static_cast<Real_t>(v0_0);
0748     verticesX[1] = static_cast<Real_t>(v1_0);
0749     verticesX[2] = static_cast<Real_t>(
0750         v3_0); // note the flip here to stick to the convention of GenTrapImplementation in the solid model
0751     verticesX[3] = static_cast<Real_t>(v2_0);
0752     verticesY[0] = static_cast<Real_t>(v0_1);
0753     verticesY[1] = static_cast<Real_t>(v1_1);
0754     verticesY[2] = static_cast<Real_t>(v3_1);
0755     verticesY[3] = static_cast<Real_t>(v2_1);
0756 
0757     for (int i = 0; i < 2; ++i) {
0758       connecting_compX[i] = verticesX[i] - verticesX[i + 2];
0759       connecting_compY[i] = verticesY[i] - verticesY[i + 2];
0760     }
0761 
0762     ftx1 = 0.5 * halfH_inv * -connecting_compX[1];
0763     fty1 = 0.5 * halfH_inv * -connecting_compY[1];
0764     ftx2 = 0.5 * halfH_inv * -connecting_compX[0];
0765     fty2 = 0.5 * halfH_inv * -connecting_compY[0];
0766 
0767     ft1crosst2 = ftx1 * fty2 - ftx2 * fty1;
0768     fDeltatx   = ftx2 - ftx1;
0769     fDeltaty   = fty2 - fty1;
0770 
0771     // temporary vertices
0772     Vector3D va = {verticesX[1], verticesY[1], -halfH};
0773     Vector3D vb = {verticesX[3], verticesY[3], halfH};
0774     Vector3D vc = {verticesX[0], verticesY[0], -halfH};
0775     Vector3D vd = {verticesX[2], verticesY[2], halfH};
0776 
0777     // Cross products used for normal computation
0778     fViCrossHi0  = (vb - va).Cross(vc - va);
0779     fViCrossVj   = (vb - va).Cross(vd - vc);
0780     fHi1CrossHi0 = (vd - vb).Cross(vc - va);
0781 
0782 #ifdef SURF_ACCURATE_SAFETY
0783     normal0 = (vd - vc).Cross(va - vc);
0784     normal0.Normalize();
0785     if ((vb - vc).Dot(normal0) < 0) normal0 *= -1;
0786 
0787     normal1 = (vb - va).Cross(vc - va);
0788     normal1.Normalize();
0789     if ((vd - va).Dot(normal1) < 0) normal1 *= -1;
0790 
0791     normal2 = (vc - vd).Cross(vb - vd);
0792     normal2.Normalize();
0793     if ((va - vd).Dot(normal2) < 0) normal2 *= -1;
0794 
0795     normal3 = (vd - vb).Cross(vc - vb);
0796     normal3.Normalize();
0797     if ((vc - vb).Dot(normal3) < 0) normal3 *= -1;
0798 #endif
0799   }
0800   template <typename Real_i>
0801   Arb4Data(const Arb4Data<Real_i> &other)
0802       : halfH(static_cast<Real_t>(other.halfH)), halfH_inv(static_cast<Real_t>(other.halfH_inv)),
0803         ftx1(static_cast<Real_t>(other.ftx1)), fty1(static_cast<Real_t>(other.fty1)),
0804         ftx2(static_cast<Real_t>(other.ftx2)), fty2(static_cast<Real_t>(other.fty2)),
0805         ft1crosst2(static_cast<Real_t>(other.ft1crosst2)), fDeltatx(static_cast<Real_t>(other.fDeltatx)),
0806         fDeltaty(static_cast<Real_t>(other.fDeltaty)),
0807         fViCrossHi0(Vector3D(static_cast<Real_t>(other.fViCrossHi0[0]), static_cast<Real_t>(other.fViCrossHi0[1]),
0808                              static_cast<Real_t>(other.fViCrossHi0[2]))),
0809         fViCrossVj(Vector3D(static_cast<Real_t>(other.fViCrossVj[0]), static_cast<Real_t>(other.fViCrossVj[1]),
0810                             static_cast<Real_t>(other.fViCrossVj[2]))),
0811         fHi1CrossHi0(Vector3D(static_cast<Real_t>(other.fHi1CrossHi0[0]), static_cast<Real_t>(other.fHi1CrossHi0[1]),
0812                               static_cast<Real_t>(other.fHi1CrossHi0[2])))
0813 #ifdef SURF_ACCURATE_SAFETY
0814         ,
0815         normal0(Vector3D(static_cast<Real_t>(other.normal0[0]), static_cast<Real_t>(other.normal0[1]),
0816                          static_cast<Real_t>(other.normal0[2]))),
0817         normal1(Vector3D(static_cast<Real_t>(other.normal1[0]), static_cast<Real_t>(other.normal1[1]),
0818                          static_cast<Real_t>(other.normal1[2]))),
0819         normal2(Vector3D(static_cast<Real_t>(other.normal2[0]), static_cast<Real_t>(other.normal2[1]),
0820                          static_cast<Real_t>(other.normal2[2]))),
0821         normal3(Vector3D(static_cast<Real_t>(other.normal3[0]), static_cast<Real_t>(other.normal3[1]),
0822                          static_cast<Real_t>(other.normal3[2])))
0823 
0824 #endif
0825   {
0826     for (int i = 0; i < 4; ++i) {
0827       verticesX[i] = static_cast<Real_t>(other.verticesX[i]);
0828       verticesY[i] = static_cast<Real_t>(other.verticesY[i]);
0829     }
0830 
0831     for (int i = 0; i < 2; ++i) {
0832       connecting_compX[i] = static_cast<Real_t>(other.connecting_compX[i]);
0833       connecting_compY[i] = static_cast<Real_t>(other.connecting_compY[i]);
0834     }
0835   }
0836 };
0837 
0838 /// @brief Data for torus surfaces
0839 /// @tparam Real_t Storage type
0840 /// @tparam Real_s Interface type
0841 template <typename Real_t>
0842 struct TorusData {
0843   Real_t rTor{0};  ///< radius to the center the torus.
0844   Real_t rTube{0}; ///< radius of the tube around the torus center, if negative, the torus is flipped
0845   AngleVector<Real_t> vecSPhi{vecCore::NumericLimits<Real_t>::Max(),
0846                               vecCore::NumericLimits<Real_t>::Max()}; ///< Cartesian coordinates of vectors that
0847                                                                       ///< represents the start of the phi-cut.
0848   AngleVector<Real_t> vecEPhi{vecCore::NumericLimits<Real_t>::Max(),
0849                               vecCore::NumericLimits<Real_t>::Max()}; ///< Cartesian coordinates of vectors that
0850                                                                       ///< represents the end of the phi-cut.
0851   Real_t inner_BC_radius{0}; ///< Cylindrical data for the inner bouding cylinder, normalized to rTor
0852   Real_t outer_BC_radius{0}; ///< Cylindrical data for the outer bouding cylinder, normalized to rTor
0853 
0854   /// @brief Check if local point is in the phi range
0855   /// @param local Point in local coordinates
0856   /// @return Point inside phi
0857   VECCORE_ATT_HOST_DEVICE
0858   VECGEOM_FORCE_INLINE
0859   bool InsidePhi(Vector3D<Real_t> const &local, bool flip) const
0860   {
0861     if (vecSPhi[0] >= vecgeom::InfinityLength<Real_t>() - vecgeom::kToleranceStrict<Real_t> &&
0862         vecEPhi[0] >= vecgeom::InfinityLength<Real_t>() - vecgeom::kToleranceStrict<Real_t>)
0863       return true;
0864     int flipsign = flip ? -1 : 1;
0865     AngleVector<Real_t> localAngle{local[0], local[1]};
0866     auto convex = vecSPhi.CrossZ(vecEPhi) > Real_t(0);
0867     auto in1    = vecSPhi.CrossZ(localAngle) > -flipsign * vecgeom::kToleranceStrict<Real_t>;
0868     auto in2    = localAngle.CrossZ(vecEPhi) > -flipsign * vecgeom::kToleranceStrict<Real_t>;
0869     return convex ? in1 && in2 : in1 || in2;
0870   }
0871 
0872   TorusData() = default;
0873   /// @tparam Real_i precision type of inputs
0874   template <typename Real_i>
0875   TorusData(Real_i rad, Real_i rad_tube, Real_i sphi = Real_i{0}, Real_i ephi = Real_i{0}, bool flip = false)
0876       : rTor(static_cast<Real_t>(rad)), rTube(flip ? static_cast<Real_t>(-rad_tube) : static_cast<Real_t>(rad_tube)),
0877         vecSPhi(static_cast<Real_t>(vecgeom::Cos(sphi)), static_cast<Real_t>(vecgeom::Sin(sphi))),
0878         vecEPhi(static_cast<Real_t>(vecgeom::Cos(ephi)), static_cast<Real_t>(vecgeom::Sin(ephi))),
0879         inner_BC_radius(-(1. - rad_tube / vecgeom::NonZero(rad))),
0880         outer_BC_radius(1. + rad_tube / vecgeom::NonZero(rad))
0881   {
0882   }
0883   template <typename Real_i>
0884   TorusData(const TorusData<Real_i> &other)
0885       : rTor(static_cast<Real_t>(other.rTor)), rTube(static_cast<Real_t>(other.rTube)),
0886         vecSPhi(static_cast<Real_t>(other.vecSPhi[0]), static_cast<Real_t>(other.vecSPhi[1])),
0887         vecEPhi(static_cast<Real_t>(other.vecEPhi[0]), static_cast<Real_t>(other.vecEPhi[1])),
0888         inner_BC_radius(static_cast<Real_t>(other.inner_BC_radius)),
0889         outer_BC_radius(static_cast<Real_t>(other.outer_BC_radius))
0890   {
0891   }
0892 
0893   VECCORE_ATT_HOST_DEVICE
0894   Real_t Radius() const { return std::abs(rTor); }
0895   VECCORE_ATT_HOST_DEVICE
0896   Real_t RadiusTube() const { return std::abs(rTube); }
0897   VECCORE_ATT_HOST_DEVICE
0898   VECCORE_ATT_HOST_DEVICE
0899   bool IsFlipped() const { return rTube < 0; }
0900   VECCORE_ATT_HOST_DEVICE
0901   VECGEOM_FORCE_INLINE
0902   Real_t InnerBCRadius() const { return std::abs(inner_BC_radius); }
0903   VECCORE_ATT_HOST_DEVICE
0904   VECGEOM_FORCE_INLINE
0905   Real_t OuterBCRadius() const { return std::abs(outer_BC_radius); }
0906 };
0907 
0908 } // namespace vgbrep
0909 
0910 #endif