Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-03-13 09:29:31

0001 // This file is part of VecGeom and is distributed under the
0002 // conditions in the file LICENSE.txt in the top directory.
0003 // For the full list of authors see CONTRIBUTORS.txt and `git log`.
0004 /// \file volumes/TessellatedStruct.h
0005 /// \author Mihaela Gheata
0006 
0007 #ifndef VECGEOM_VOLUMES_TESSELLATEDCLUSTER_H_
0008 #define VECGEOM_VOLUMES_TESSELLATEDCLUSTER_H_
0009 
0010 #include <VecCore/VecCore>
0011 
0012 #include "VecGeom/base/AlignedBase.h"
0013 #include "VecGeom/base/Global.h"
0014 #include "VecGeom/base/Vector3D.h"
0015 #include "VecGeom/base/Vector.h"
0016 #include "VecGeom/volumes/kernel/GenericKernels.h"
0017 #include "Tile.h"
0018 
0019 namespace vecgeom {
0020 
0021 VECGEOM_DEVICE_DECLARE_CONV_TEMPLATE_1v_1t(class, TessellatedCluster, size_t, typename);
0022 
0023 inline namespace VECGEOM_IMPL_NAMESPACE {
0024 
0025 constexpr size_t kVecSize = vecCore::VectorSize<vecgeom::VectorBackend::Real_v>();
0026 
0027 /** Structure used for vectorizing queries on groups of triangles.
0028 
0029 The class represents a cluster of as many facets as the SIMD vector length for double
0030 precision operations.
0031 */
0032 template <size_t NVERT, typename Real_v>
0033 class TessellatedCluster : public AlignedBase {
0034 public:
0035   /// Scalar double precision type
0036   using T = typename vecCore::ScalarType<Real_v>::Type;
0037   /// A facet of the cluster
0038   using Facet_t = Tile<NVERT, T>;
0039 
0040   Vector3D<Real_v> fNormals;            ///< Normals to facet components
0041   Real_v fDistances;                    ///< Distances from origin to facets
0042   Vector3D<Real_v> fSideVectors[NVERT]; ///< Side vectors of the triangular facets
0043   Vector3D<Real_v> fVertices[NVERT];    ///< Vertices stored in SIMD format
0044   size_t fIfacets[kVecSize]  = {};      ///< Real indices of facets
0045   Facet_t *fFacets[kVecSize] = {};      ///< Array of scalar facets matching the ones in the cluster
0046   Vector3D<T> fMinExtent;               ///< Minimum extent
0047   Vector3D<T> fMaxExtent;               ///< Maximum extent
0048   bool fConvex = false;                 ///< Convexity of the cluster with respect to the solid it belongs to
0049 
0050   /// Deafult constructor.
0051   VECCORE_ATT_HOST_DEVICE
0052   TessellatedCluster()
0053   {
0054     fMinExtent.Set(InfinityLength<T>());
0055     fMaxExtent.Set(-InfinityLength<T>());
0056   }
0057 
0058   /// Convexity getter
0059   VECGEOM_FORCE_INLINE
0060   VECCORE_ATT_HOST_DEVICE
0061   bool IsConvex() const { return fConvex; }
0062 
0063   /// Method to calculate convexity
0064   VECGEOM_FORCE_INLINE
0065   VECCORE_ATT_HOST_DEVICE
0066   bool CalculateConvexity()
0067   {
0068     bool convex = true;
0069     for (size_t i = 0; i < kVecSize; ++i)
0070       convex &= fFacets[i]->fConvex;
0071     return convex;
0072   }
0073 
0074   /// Getter for a vertex position.
0075   /// @param [in]  ifacet Facet index
0076   /// @param[ in]  ivert Vertex number
0077   /// @param [out] vertex Vertex position
0078   VECGEOM_FORCE_INLINE
0079   VECCORE_ATT_HOST_DEVICE
0080   void GetVertex(size_t ifacet, size_t ivert, Vector3D<T> &vertex) const
0081   {
0082     vertex[0] = vecCore::Get(fVertices[ivert].x(), ifacet);
0083     vertex[1] = vecCore::Get(fVertices[ivert].y(), ifacet);
0084     vertex[2] = vecCore::Get(fVertices[ivert].z(), ifacet);
0085   }
0086 
0087   /// Getter for a facet of the cluster.
0088   /// @param ifacet Facet index
0089   /// @return Facet at given index
0090   VECGEOM_FORCE_INLINE
0091   VECCORE_ATT_HOST_DEVICE
0092   Facet_t *GetFacet(size_t ifacet) const { return fFacets[ifacet]; }
0093 
0094   /// Calculate cluster sparsity
0095   /// @param [out]  nblobs Number of separate blobs
0096   /// @param [out] nfacets Number of non-replicated facets
0097   /// @return Dispersion as ratio between maximum facet size and maximum distance from a
0098   /// facet to the cluster centroid
0099   VECGEOM_FORCE_INLINE
0100   VECCORE_ATT_HOST_DEVICE
0101   T ComputeSparsity(int &nblobs, int &nfacets)
0102   {
0103     // Find cluster center
0104     Vector3D<T> clcenter;
0105     for (unsigned ifacet = 0; ifacet < kVecSize; ++ifacet) {
0106       clcenter += fFacets[ifacet]->fCenter;
0107     }
0108     clcenter /= kVecSize;
0109 
0110     // Compute dispersion
0111     T maxsize = 0, lensq = 0, dmax = 0;
0112     for (unsigned ifacet = 0; ifacet < kVecSize; ++ifacet) {
0113       T facetsizesq = 0;
0114       for (int i = 0; i < 3; ++i) {
0115         lensq = (fFacets[ifacet]->fVertices[i] - fFacets[ifacet]->fVertices[(i + 1) % 3]).Mag2();
0116         if (lensq > facetsizesq) facetsizesq = lensq;
0117       }
0118       if (facetsizesq > maxsize) maxsize = facetsizesq;
0119       lensq = (fFacets[ifacet]->fCenter - clcenter).Mag2();
0120       if (lensq > dmax) dmax = lensq;
0121     }
0122     T dispersion = vecCore::math::Sqrt(dmax / maxsize);
0123 
0124     // Compute number of distinct facets
0125     nfacets = 0;
0126     for (unsigned ifacet = 0; ifacet < kVecSize; ++ifacet) {
0127       bool duplicate = false;
0128       for (unsigned jfacet = ifacet + 1; jfacet < kVecSize; ++jfacet) {
0129         if (fFacets[jfacet] == fFacets[ifacet]) {
0130           duplicate = true;
0131           break;
0132         }
0133       }
0134       if (!duplicate) nfacets++;
0135     }
0136 
0137     // Compute number of blobs
0138     nblobs = 0;
0139     int cluster[kVecSize];
0140     int ncl             = 0;
0141     bool used[kVecSize] = {false};
0142     for (unsigned ifacet = 0; ifacet < kVecSize; ++ifacet) {
0143       ncl = 0;
0144       if (used[ifacet]) break;
0145       cluster[ncl++] = ifacet;
0146       used[ifacet]   = true;
0147       nblobs++;
0148       // loop remaining facets
0149       for (unsigned jfacet = ifacet + 1; jfacet < kVecSize; ++jfacet) {
0150         if (used[jfacet]) break;
0151         // loop facets already in sub-cluster
0152         int nneighbors = 0;
0153         for (int incl = 0; incl < ncl; ++incl) {
0154           nneighbors += fFacets[jfacet]->IsNeighbor(*fFacets[cluster[incl]]);
0155         }
0156         if (nneighbors > ncl) {
0157           cluster[ncl++] = jfacet;
0158           used[jfacet]   = true;
0159         }
0160       }
0161     }
0162     return dispersion;
0163   }
0164 
0165   /// Fill the components of the cluster with facet data
0166   /// @param index Triangle index, equivalent to SIMD lane index
0167   /// @param facet Triangle facet data
0168   /// @param ifacet Facet index
0169   VECCORE_ATT_HOST_DEVICE
0170   void AddFacet(size_t index, Facet_t *facet, size_t ifacet)
0171   {
0172     // Fill the facet normal by accessing individual SIMD lanes
0173     assert(index < kVecSize);
0174     vecCore::Set(fNormals.x(), index, facet->fNormal.x());
0175     vecCore::Set(fNormals.y(), index, facet->fNormal.y());
0176     vecCore::Set(fNormals.z(), index, facet->fNormal.z());
0177     // Fill the distance to the plane
0178     vecCore::Set(fDistances, index, facet->fDistance);
0179     // Compute side vectors and fill them using the store operation per SIMD lane
0180     for (size_t ivert = 0; ivert < NVERT; ++ivert) {
0181       Vector3D<T> c0 = facet->fVertices[ivert];
0182       if (c0.x() < fMinExtent[0]) fMinExtent[0] = c0.x();
0183       if (c0.y() < fMinExtent[1]) fMinExtent[1] = c0.y();
0184       if (c0.z() < fMinExtent[2]) fMinExtent[2] = c0.z();
0185       if (c0.x() > fMaxExtent[0]) fMaxExtent[0] = c0.x();
0186       if (c0.y() > fMaxExtent[1]) fMaxExtent[1] = c0.y();
0187       if (c0.z() > fMaxExtent[2]) fMaxExtent[2] = c0.z();
0188       Vector3D<T> c1         = facet->fVertices[(ivert + 1) % NVERT];
0189       Vector3D<T> sideVector = facet->fNormal.Cross(c1 - c0).Normalized();
0190       vecCore::Set(fSideVectors[ivert].x(), index, sideVector.x());
0191       vecCore::Set(fSideVectors[ivert].y(), index, sideVector.y());
0192       vecCore::Set(fSideVectors[ivert].z(), index, sideVector.z());
0193       vecCore::Set(fVertices[ivert].x(), index, c0.x());
0194       vecCore::Set(fVertices[ivert].y(), index, c0.y());
0195       vecCore::Set(fVertices[ivert].z(), index, c0.z());
0196     }
0197     fFacets[index]  = facet;
0198     fIfacets[index] = ifacet;
0199     if (index == kVecSize - 1) CalculateConvexity();
0200   }
0201 
0202   // === Navigation functionality === //
0203 
0204   /// Check if a scalar point is inside any of the cluster tiles
0205   /// @param point Point position
0206   VECGEOM_FORCE_INLINE
0207   VECCORE_ATT_HOST_DEVICE
0208   bool Contains(Vector3D<T> point)
0209   {
0210     using Bool_v = vecCore::Mask<Real_v>;
0211 
0212     Bool_v inside;
0213     // Implicit conversion of point to Real_v
0214     InsideCluster(point, inside);
0215     return (!vecCore::MaskEmpty(inside));
0216   }
0217 
0218   /// Check if the points are inside some of the triangles. The points are assumed
0219   /// to be already propagated on the triangle planes.
0220   VECGEOM_FORCE_INLINE
0221   VECCORE_ATT_HOST_DEVICE
0222   void InsideCluster(Vector3D<Real_v> const &point, typename vecCore::Mask<Real_v> &inside) const
0223   {
0224     using Bool_v = vecCore::Mask<Real_v>;
0225 
0226     inside = Bool_v(true);
0227     for (size_t i = 0; i < NVERT; ++i) {
0228       Real_v saf = (point - fVertices[i]).Dot(fSideVectors[i]);
0229       inside &= saf > Real_v(-kTolerance);
0230     }
0231   }
0232 
0233   /// Compute distance from point to all facet planes. This is positive if the point is on
0234   /// the outside halfspace, negative otherwise.
0235   VECGEOM_FORCE_INLINE
0236   VECCORE_ATT_HOST_DEVICE
0237   Real_v DistPlanes(Vector3D<Real_v> const &point) const { return (point.Dot(fNormals) + fDistances); }
0238 
0239   /// Computes both distance to in and distance to out for the cluster
0240   /// @param[in] point Point position
0241   /// @param [in] direction Input direction
0242   /// @param [out] distanceToIn Distance in case the point is outside
0243   /// @param [out] distanceToOut Distance in case the point is inside
0244   /// @param [out] isurfToIn Index of hit surface if point is outside
0245   /// @param [out] isurfToOut Index of hit surface if point is inside
0246   VECCORE_ATT_HOST_DEVICE
0247   void DistanceToCluster(Vector3D<Real_v> const &point, Vector3D<Real_v> const &direction, T &distanceToIn,
0248                          T &distanceToOut, int &isurfToIn, int &isurfToOut) const
0249   {
0250     using Bool_v = vecCore::Mask<Real_v>;
0251 
0252     distanceToIn  = InfinityLength<T>();
0253     distanceToOut = InfinityLength<T>();
0254     //    Real_v distToIn   = InfinityLength<Real_v>();
0255     //    Real_v distToOut  = InfinityLength<Real_v>();
0256     isurfToIn  = -1;
0257     isurfToOut = -1;
0258 
0259     // Vector3D<Real_v> pointv(point);
0260     // Vector3D<Real_v> dirv(direction);
0261     Real_v ndd = NonZero(direction.Dot(fNormals));
0262     Real_v saf = DistPlanes(point);
0263 
0264     Bool_v validToIn  = ndd < Real_v(0.) && saf > Real_v(-kTolerance);
0265     Bool_v validToOut = ndd > Real_v(0.) && saf < Real_v(kTolerance);
0266 
0267     Real_v dist             = -saf / ndd;
0268     Vector3D<Real_v> pointv = point + dist * direction;
0269     // Check if propagated points hit the triangles
0270     Bool_v hit;
0271     InsideCluster(pointv, hit);
0272 
0273     validToIn &= hit;
0274     validToOut &= hit;
0275 
0276     // Now we need to return the minimum distance for the hit facets
0277     if (vecCore::EarlyReturnAllowed() && vecCore::MaskEmpty(validToIn || validToOut)) return;
0278 
0279     // Since we can make no assumptions on convexity, we need to actually check
0280     // which surface is actually crossed. First propagate the point with the
0281     // distance to each plane.
0282     for (size_t i = 0; i < kVecSize; ++i) {
0283       if (vecCore::Get(validToIn, i)) {
0284         T dlane = vecCore::Get(dist, i);
0285         if (dlane < distanceToIn) {
0286           distanceToIn = dlane;
0287           isurfToIn    = fIfacets[i];
0288         }
0289       } else {
0290         if (vecCore::Get(validToOut, i)) {
0291           T dlane = vecCore::Get(dist, i);
0292           if (dlane < distanceToOut) {
0293             distanceToOut = dlane;
0294             isurfToOut    = fIfacets[i];
0295           }
0296         }
0297       }
0298     }
0299   }
0300 
0301   /// Computes distance from point outside for the cluster
0302   /// @param [in]  point Point position
0303   /// @param [in]  direction Direction for the distance computation
0304   /// @param [out] distance Distance to the cluster
0305   /// @param [out] isurf Surface crossed
0306   VECCORE_ATT_HOST_DEVICE
0307   void DistanceToIn(Vector3D<Real_v> const &point, Vector3D<Real_v> const &direction, T const & /*stepMax*/,
0308                     T &distance, int &isurf) const
0309   {
0310     using Bool_v = vecCore::Mask<Real_v>;
0311 
0312     distance    = InfinityLength<T>();
0313     Real_v dist = InfinityLength<Real_v>();
0314     isurf       = -1;
0315     //    Vector3D<Real_v> pointv(point);
0316     //    Vector3D<Real_v> dirv(direction);
0317     Real_v ndd   = NonZero(direction.Dot(fNormals));
0318     Real_v saf   = DistPlanes(point);
0319     Bool_v valid = ndd < Real_v(0.) && saf > Real_v(-kTolerance);
0320     //    if (vecCore::EarlyReturnAllowed() && vecCore::MaskEmpty(valid)) return;
0321 
0322     vecCore__MaskedAssignFunc(dist, valid, -saf / ndd);
0323     // Since we can make no assumptions on convexity, we need to actually check
0324     // which surface is actually crossed. First propagate the point with the
0325     // distance to each plane.
0326     Vector3D<Real_v> pointv = point + dist * direction;
0327     // Check if propagated points hit the triangles
0328     Bool_v hit;
0329     InsideCluster(pointv, hit);
0330     valid &= hit;
0331     // Now we need to return the minimum distance for the hit facets
0332     if (vecCore::EarlyReturnAllowed() && vecCore::MaskEmpty(valid)) return;
0333 
0334     for (size_t i = 0; i < kVecSize; ++i) {
0335       if (vecCore::Get(valid, i) && (vecCore::Get(dist, i) < distance)) {
0336         distance = vecCore::Get(dist, i);
0337         isurf    = fIfacets[i];
0338       }
0339     }
0340   }
0341 
0342   /// Compute distance from point outside for the convex case.
0343   /// @param [in]  point Point position
0344   /// @param [in]  direction Direction for the distance computation
0345   /// @param [out] distance Distance to the cluster
0346   /// @param [out] limit Search limit
0347   /// @return validity of the computed distance.
0348   VECCORE_ATT_HOST_DEVICE
0349   VECGEOM_FORCE_INLINE
0350   bool DistanceToInConvex(Vector3D<Real_v> const &point, Vector3D<Real_v> const &direction, T &distance, T &limit) const
0351   {
0352     using Bool_v = vecCore::Mask<Real_v>;
0353 
0354     // Check if track is moving away from facets
0355     const Real_v proj        = NonZero(direction.Dot(fNormals));
0356     const Bool_v moving_away = proj > Real_v(-kTolerance);
0357     // Check if track is on the correct side of of the planes
0358     const Real_v pdist        = DistPlanes(point);
0359     const Bool_v side_correct = pdist > Real_v(-kTolerance);
0360 
0361     if (!vecCore::MaskEmpty(side_correct && moving_away)) return false;
0362 
0363     // These facets can be hit from outside
0364     const Bool_v from_outside = side_correct && !moving_away;
0365 
0366     // These facets can be hit from inside
0367     const Bool_v from_inside = !side_correct && moving_away;
0368 
0369     Real_v dmin = -InfinityLength<Real_v>();
0370     Real_v dmax = InfinityLength<Real_v>();
0371     // Distances to facets
0372     const Real_v dist = -pdist / NonZero(proj);
0373     vecCore__MaskedAssignFunc(dmin, from_outside, dist);
0374     vecCore__MaskedAssignFunc(dmax, from_inside, dist);
0375     distance = vecCore::math::Max(distance, vecCore::ReduceMax(dmin));
0376     limit    = vecCore::math::Min(limit, vecCore::ReduceMin(dmax));
0377 
0378     // if (distance < limit - kTolerance) return true;
0379     // distance = InfinityLength<T>();
0380     return true;
0381   }
0382 
0383   /// Computes distance to out for the cluster
0384   /// @param [in]  point Point position
0385   /// @param [in]  direction Direction for the distance computation
0386   /// @param [out] distance Distance to the cluster
0387   /// @param [out] isurf Surface crossed
0388   VECCORE_ATT_HOST_DEVICE
0389   void DistanceToOut(Vector3D<Real_v> const &point, Vector3D<Real_v> const &direction, T const & /*stepMax*/,
0390                      T &distance, int &isurf) const
0391   {
0392     using Bool_v = vecCore::Mask<Real_v>;
0393 
0394     distance    = 0.;
0395     Real_v dist = InfinityLength<Real_v>();
0396     isurf       = -1;
0397     // Transform scalar point and direction into Real_v types
0398     //    Vector3D<Real_v> pointv(point);
0399     //    Vector3D<Real_v> dirv(direction);
0400 
0401     // Dot product between direction and facet normals should be positive
0402     // for valid crossings
0403     Real_v ndd = NonZero(direction.Dot(fNormals));
0404 
0405     // Distances to facet planes should be negative for valid crossing ("behind" normals)
0406     Real_v saf = DistPlanes(point);
0407 
0408     Bool_v valid = ndd > Real_v(0.) && saf < Real_v(kTolerance);
0409     // In case no crossing is valid, the point is outside and returns 0 distance
0410     if (vecCore::EarlyReturnAllowed() && vecCore::MaskEmpty(valid)) return;
0411 
0412     vecCore__MaskedAssignFunc(dist, valid, -saf / ndd);
0413     // Since we can make no assumptions on convexity, we need to actually check
0414     // which surface is actually crossed. First propagate the point with the
0415     // distance to each plane.
0416     Vector3D<Real_v> pointv = point + dist * direction;
0417     // Check if propagated points hit the triangles
0418     Bool_v hit;
0419     InsideCluster(pointv, hit);
0420     valid &= hit;
0421     if (vecCore::MaskEmpty(valid)) return;
0422 
0423     // Now we need to return the minimum distance for the hit facets
0424     distance = InfinityLength<T>();
0425     for (size_t i = 0; i < kVecSize; ++i) {
0426       if (vecCore::Get(valid, i) && vecCore::Get(dist, i) < distance) {
0427         distance = vecCore::Get(dist, i);
0428         isurf    = fIfacets[i];
0429       }
0430     }
0431   }
0432 
0433   /// Compute distance from point inside for the convex case.
0434   /// @param [in]  point Point position
0435   /// @param [in]  direction Direction for the distance computation
0436   /// @param [out] distance Distance to the cluster
0437   /// @return validity of the computed distance.
0438   VECCORE_ATT_HOST_DEVICE
0439   bool DistanceToOutConvex(Vector3D<Real_v> const &point, Vector3D<Real_v> const &direction, T &distance) const
0440   {
0441     using Bool_v = vecCore::Mask<Real_v>;
0442 
0443     distance    = -kTolerance;
0444     Real_v dist = InfinityLength<Real_v>();
0445 
0446     // Distances to facet planes should be negative for valid crossing ("behind" normals)
0447     Real_v saf   = DistPlanes(point);
0448     Bool_v valid = saf < Real_v(kTolerance);
0449     if (vecCore::EarlyReturnAllowed() && !vecCore::MaskFull(valid)) return false;
0450 
0451     // Dot product between direction and facet normals should be positive
0452     // for valid crossings
0453     Real_v ndd = NonZero(direction.Dot(fNormals));
0454     valid      = ndd > Real_v(0.);
0455 
0456     vecCore__MaskedAssignFunc(dist, valid, -saf / ndd);
0457     distance = vecCore::ReduceMin(dist);
0458     return true;
0459   }
0460 
0461   /// Compute distance to in/out for the convex case.
0462   /// @param [in]  point Point position
0463   /// @param [in]  direction Direction for the distance computation
0464   /// @param [out] dtoin Distance in case the point is outside
0465   /// @param [out] dtoout Distance in case the point is inside
0466   VECCORE_ATT_HOST_DEVICE
0467   bool DistanceToInOut(Vector3D<Real_v> const &point, Vector3D<Real_v> const &direction, T &dtoin, T &dtoout) const
0468   {
0469     using Bool_v = vecCore::Mask<Real_v>;
0470     using vecCore::ReduceMax;
0471     using vecCore::ReduceMin;
0472     using vecCore::math::Max;
0473     using vecCore::math::Min;
0474 
0475     // Direction projected to all facets
0476     Real_v projdir_v   = NonZero(direction.Dot(fNormals));
0477     Bool_v moving_away = projdir_v > Real_v(0.);
0478     // Signed projected distances to facets
0479     Real_v projdist_v = DistPlanes(point);
0480     Bool_v outside    = projdist_v > Real_v(kTolerance);
0481     // If outside and mowing away any facet, no hit possible (convexity)
0482     if (!vecCore::MaskEmpty(outside && moving_away)) return false;
0483     // Facets that can be hit from inside
0484     Bool_v from_inside = !outside && moving_away;
0485     // Facets that can be hit from outside
0486     Bool_v from_outside = outside && !moving_away;
0487     // Distances to all facets
0488     const Real_v dist_v = -projdist_v / NonZero(projdir_v);
0489     Real_v dtoin_v      = -InfinityLength<Real_v>();
0490     Real_v dtoout_v     = InfinityLength<Real_v>();
0491     vecCore__MaskedAssignFunc(dtoin_v, from_outside, dist_v);
0492     dtoin = Max(dtoin, ReduceMax(dtoin_v));
0493     vecCore__MaskedAssignFunc(dtoout_v, from_inside, dist_v);
0494     dtoout = Min(dtoout, ReduceMin(dtoout_v));
0495     return true;
0496   }
0497 
0498   /// Compute safety squared from point to closest facet.
0499   /// @param [in] point Point position
0500   /// @param [out] isurf Closest facet index
0501   template <bool ToIn>
0502   VECCORE_ATT_HOST_DEVICE
0503   T SafetySq(Vector3D<Real_v> const &point, int &isurf) const
0504   {
0505     using Bool_v = vecCore::Mask<Real_v>;
0506     //    Vector3D<Real_v> pointv(point);
0507     Real_v safetyv = DistPlanes(point);
0508     T distancesq   = InfinityLength<T>();
0509     // Find the projection of the point on each plane
0510     Vector3D<Real_v> intersectionv = point - safetyv * fNormals;
0511     Bool_v withinBound;
0512     InsideCluster(intersectionv, withinBound);
0513     if (ToIn)
0514       withinBound &= safetyv > Real_v(-kTolerance);
0515     else
0516       withinBound &= safetyv < Real_v(kTolerance);
0517     safetyv *= safetyv;
0518 
0519     isurf = -1;
0520     if (vecCore::MaskFull(withinBound)) {
0521       // loop over lanes to get minimum positive value.
0522       for (size_t i = 0; i < kVecSize; ++i) {
0523         auto saflane = vecCore::Get(safetyv, i);
0524         if (saflane < distancesq) {
0525           distancesq = saflane;
0526           isurf      = fIfacets[i];
0527         }
0528       }
0529       return distancesq;
0530     }
0531 
0532     Vector3D<Real_v> safetyv_outbound = InfinityLength<Real_v>();
0533     for (size_t ivert = 0; ivert < NVERT; ++ivert) {
0534       safetyv_outbound[ivert] =
0535           DistanceToLineSegmentSquared2(fVertices[ivert], fVertices[(ivert + 1) % NVERT], point, !withinBound);
0536     }
0537     Real_v safety_outv = safetyv_outbound.Min();
0538     vecCore::MaskedAssign(safetyv, !withinBound, safety_outv);
0539 
0540     // loop over lanes to get minimum positive value.
0541     for (size_t i = 0; i < kVecSize; ++i) {
0542       auto saflane = vecCore::Get(safetyv, i);
0543       if (saflane < distancesq) {
0544         distancesq = saflane;
0545         isurf      = fIfacets[i];
0546       }
0547     }
0548     return distancesq;
0549   }
0550 
0551   /*
0552     VECCORE_ATT_HOST_DEVICE
0553     void DistanceToInScalar(Vector3D<T> const &point, Vector3D<T> const &direction, T const &stepMax, T &distance,
0554                             int &isurf)
0555     {
0556       distance = InfinityLength<T>();
0557       isurf    = -1;
0558       T distfacet;
0559       for (size_t i = 0; i < kVecSize; ++i) {
0560         distfacet = fFacets[i]->DistanceToIn(point, direction, stepMax);
0561         if (distfacet < distance) {
0562           distance = distfacet;
0563           isurf    = fIfacets[i];
0564         }
0565       }
0566     }
0567 
0568     VECCORE_ATT_HOST_DEVICE
0569     void DistanceToOutScalar(Vector3D<T> const &point, Vector3D<T> const &direction, T const &stepMax, T &distance,
0570                              int &isurf)
0571     {
0572       distance = InfinityLength<T>();
0573       isurf    = -1;
0574       T distfacet;
0575       for (size_t i = 0; i < kVecSize; ++i) {
0576         distfacet = fFacets[i]->DistanceToOut(point, direction, stepMax);
0577         if (distfacet < distance) {
0578           distance = distfacet;
0579           isurf    = fIfacets[i];
0580         }
0581       }
0582     }
0583 
0584     template <bool ToIn>
0585     VECCORE_ATT_HOST_DEVICE
0586     T SafetySqScalar(Vector3D<T> const &point, int &isurf)
0587     {
0588       T distance = InfinityLength<T>();
0589       T distfacet;
0590       for (size_t i = 0; i < kVecSize; ++i) {
0591         distfacet = fFacets[i]->template SafetySq<ToIn>(point, isurf);
0592         if (distfacet < distance) {
0593           distance = distfacet;
0594           isurf    = fIfacets[i];
0595         }
0596       }
0597       return distance;
0598     }
0599   */
0600 };
0601 
0602 std::ostream &operator<<(std::ostream &os, TessellatedCluster<3, typename vecgeom::VectorBackend::Real_v> const &tcl);
0603 std::ostream &operator<<(std::ostream &os, TessellatedCluster<4, typename vecgeom::VectorBackend::Real_v> const &tcl);
0604 
0605 } // namespace VECGEOM_IMPL_NAMESPACE
0606 } // end namespace vecgeom
0607 
0608 #endif