Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:14:09

0001 #ifndef VECGEOM_PLANAR_POLYGON_H
0002 #define VECGEOM_PLANAR_POLYGON_H
0003 
0004 #include "VecGeom/base/Global.h"
0005 #include "VecGeom/base/SOA3D.h"
0006 #include "VecGeom/base/Vector3D.h"
0007 #include "VecGeom/base/Vector.h"
0008 #include <VecCore/VecCore>
0009 #include <iostream>
0010 #include <limits>
0011 
0012 namespace vecgeom {
0013 
0014 VECGEOM_DEVICE_FORWARD_DECLARE(class PlanarPolygon;);
0015 VECGEOM_DEVICE_DECLARE_CONV(class, PlanarPolygon);
0016 
0017 inline namespace VECGEOM_IMPL_NAMESPACE {
0018 
0019 // a class representing a 2D convex or concav polygon
0020 class PlanarPolygon {
0021 
0022   friend struct SExtruImplementation;
0023 
0024 protected:
0025   // we have to work on the "ideal" memory layout/placement for this
0026   // this is WIP
0027   SOA3D<Precision> fVertices; // a vector of vertices with links between
0028                               // note that the z component will hold the slopes between 2 links
0029                               // We assume a clockwise order of points
0030   Vector<Precision> fShiftedXJ;
0031   Vector<Precision> fShiftedYJ;
0032   Vector<Precision> fLengthSqr;    // the lenghts of each segment
0033   Vector<Precision> fInvLengthSqr; // the inverse square lengths of each segment
0034   Vector<Precision> fA;            // the "a"=x coefficient in the plane equation
0035   Vector<Precision> fB;            // the "b"=y coefficient in the plane equation
0036   Vector<Precision> fD;            // the "d" coefficient in the plane equation
0037 
0038   bool fIsConvex;  // convexity property to be calculated a construction time
0039   Precision fMinX; // the extent of the polygon
0040   Precision fMinY;
0041   Precision fMaxX;
0042   Precision fMaxY;
0043 
0044   size_t fNVertices; // the actual number of vertices
0045   friend class PolygonalShell;
0046 
0047 public:
0048   VECCORE_ATT_HOST_DEVICE
0049   PlanarPolygon()
0050       : fVertices(), fShiftedXJ({}), fShiftedYJ({}), fLengthSqr({}), fInvLengthSqr({}), fA({}), fB({}), fD({}),
0051         fIsConvex(false), fMinX(kInfLength), fMinY(kInfLength), fMaxX(-kInfLength), fMaxY(-kInfLength), fNVertices(0)
0052   {
0053   }
0054 
0055   // constructor (not taking ownership of the pointers)
0056   VECCORE_ATT_HOST_DEVICE
0057   PlanarPolygon(int nvertices, Precision *x, Precision *y)
0058       : fVertices(), fShiftedXJ({}), fShiftedYJ({}), fLengthSqr({}), fInvLengthSqr({}), fA({}), fB({}), fD({}),
0059         fIsConvex(false), fMinX(kInfLength), fMinY(kInfLength), fMaxX(-kInfLength), fMaxY(-kInfLength),
0060         fNVertices(nvertices)
0061   {
0062     Init(nvertices, x, y);
0063   }
0064 
0065   VECCORE_ATT_HOST_DEVICE
0066   void Init(int nvertices, Precision *x, Precision *y)
0067   {
0068     // allocating more space than nvertices, in order
0069     // to accomodate an internally vectorized treatment without tails
0070     // --> the size comes from this formula:
0071     const size_t kVS                = vecCore::VectorSize<vecgeom::VectorBackend::Real_v>();
0072     const auto numberOfVectorChunks = (nvertices / kVS + nvertices % kVS);
0073     // actual buffersize
0074     const auto bs = numberOfVectorChunks * kVS;
0075     assert(bs > 0);
0076     fNVertices = nvertices;
0077     fVertices.reserve(bs);
0078     fVertices.resize(nvertices);
0079     fShiftedXJ.resize(bs, 0);
0080     fShiftedYJ.resize(bs, 0);
0081     fLengthSqr.resize(bs, 0);
0082     fInvLengthSqr.resize(bs, 0);
0083     fA.resize(bs, 0);
0084     fB.resize(bs, 0);
0085     fD.resize(bs, 0);
0086 
0087     int inc = (GetOrientation(x, y, nvertices) > 0) ? -1 : 1;
0088     size_t i, j;
0089     // init the vertices (wrapping around periodically)
0090     for (i = 0; i < (size_t)fNVertices; ++i) {
0091       const size_t k = (i * inc + fNVertices) % fNVertices;
0092       fVertices.set(i, x[k], y[k], 0);
0093       fMinX = vecCore::math::Min(x[k], fMinX);
0094       fMinY = vecCore::math::Min(y[k], fMinY);
0095       fMaxX = vecCore::math::Max(x[k], fMaxX);
0096       fMaxY = vecCore::math::Max(y[k], fMaxY);
0097     }
0098 
0099     // initialize and cache the slopes as a "hidden" component
0100     auto slopes      = fVertices.z();
0101     const auto S     = fNVertices;
0102     const auto vertx = fVertices.x();
0103     const auto verty = fVertices.y();
0104     for (i = 0, j = S - 1; i < S; j = i++) {
0105       const auto vertxI = vertx[i];
0106       const auto vertxJ = vertx[j];
0107 
0108       const auto vertyI = verty[i];
0109       const auto vertyJ = verty[j];
0110 
0111       slopes[i]     = (vertxJ - vertxI) / NonZero(vertyJ - vertyI);
0112       fShiftedYJ[i] = vertyJ;
0113       fShiftedXJ[i] = vertxJ;
0114     }
0115 
0116     for (i = 0; i < (size_t)S; ++i) {
0117       fLengthSqr[i] = (vertx[i] - fShiftedXJ[i]) * (vertx[i] - fShiftedXJ[i]) +
0118                       (verty[i] - fShiftedYJ[i]) * (verty[i] - fShiftedYJ[i]);
0119       fInvLengthSqr[i] = 1. / fLengthSqr[i];
0120     }
0121 
0122     // init normals
0123     // this is taken from UnplacedTrapezoid
0124     // we should make this a standalone function outside any volume class
0125     for (i = 0; i < (size_t)S; ++i) {
0126       const auto xi = fVertices.x();
0127       const auto yi = fVertices.y();
0128 
0129       // arbitary choice of normal for the moment
0130       auto a = -(fShiftedYJ[i] - yi[i]);
0131       auto b = +(fShiftedXJ[i] - xi[i]);
0132 
0133       auto norm = 1.0 / std::sqrt(a * a + b * b); // normalization factor, always positive
0134       a *= norm;
0135       b *= norm;
0136 
0137       auto d = -(a * xi[i] + b * yi[i]);
0138 
0139       // fix (sign of zero (avoid -0 ))
0140       if (std::abs(a) < kTolerance) a = 0.;
0141       if (std::abs(b) < kTolerance) b = 0.;
0142       if (std::abs(d) < kTolerance) d = 0.;
0143 
0144       //      std::cerr << a << "," << b << "," << d << "\n";
0145 
0146       fA[i] = a;
0147       fB[i] = b;
0148       fD[i] = d;
0149     }
0150 
0151     // fill rest of data buffers periodically (for safe internal vectorized treatment)
0152     for (i = S; i < bs; ++i) {
0153       const size_t k = i % fNVertices;
0154       fVertices.set(i, fVertices.x()[k], fVertices.y()[k], fVertices.z()[k]);
0155       fShiftedXJ[i]    = fShiftedXJ[k];
0156       fShiftedYJ[i]    = fShiftedYJ[k];
0157       fLengthSqr[i]    = fLengthSqr[k];
0158       fInvLengthSqr[i] = fInvLengthSqr[k];
0159       fA[i]            = fA[k];
0160       fB[i]            = fB[k];
0161       fD[i]            = fD[k];
0162     }
0163 
0164     // set convexity
0165     CalcConvexity();
0166 
0167 // check orientation
0168 #ifndef VECCORE_CUDA
0169     if (Area() < 0.) {
0170       throw std::runtime_error("Polygon not given in clockwise order");
0171     }
0172 #endif
0173   }
0174 
0175   VECCORE_ATT_HOST_DEVICE
0176   Precision GetMinX() const { return fMinX; }
0177 
0178   VECCORE_ATT_HOST_DEVICE
0179   Precision GetMinY() const { return fMinY; }
0180 
0181   VECCORE_ATT_HOST_DEVICE
0182   Precision GetMaxX() const { return fMaxX; }
0183 
0184   VECCORE_ATT_HOST_DEVICE
0185   Precision GetMaxY() const { return fMaxY; }
0186 
0187   VECCORE_ATT_HOST_DEVICE
0188   SOA3D<Precision> const &GetVertices() const { return fVertices; }
0189 
0190   VECCORE_ATT_HOST_DEVICE
0191   size_t GetNVertices() const { return fNVertices; }
0192 
0193   // checks if 2D coordinates (x,y) are on the line segment given by index i
0194   template <typename Real_v, typename InternalReal_v, typename Bool_v>
0195   VECCORE_ATT_HOST_DEVICE
0196   Bool_v OnSegment(size_t i, Real_v const &px, Real_v const &py) const
0197   {
0198     using vecCore::FromPtr;
0199 
0200     // static assert ( cannot have Real_v == InternalReal_v )
0201 
0202     Bool_v result(false);
0203     //
0204     const auto vertx = fVertices.x();
0205     const auto verty = fVertices.y();
0206     // const auto slopes = fVertices.z();
0207 
0208     // check if cross close to zero
0209     const Real_v bx(FromPtr<InternalReal_v>(&vertx[i]));
0210     const Real_v by(FromPtr<InternalReal_v>(&verty[i]));
0211     const Real_v ax(FromPtr<InternalReal_v>(&fShiftedXJ[i]));
0212     const Real_v ay(FromPtr<InternalReal_v>(&fShiftedYJ[i]));
0213     // const Real_v slope(FromPtr<InternalReal_v>(&slopes[i]));
0214 
0215     const Real_v pymay(py - ay);
0216     const Real_v pxmax(px - ax);
0217     const Real_v epsilon(1E-9);
0218 
0219     // optimized crossproduct
0220     const Real_v cross     = (pymay * (bx - ax) - pxmax * (by - ay));
0221     const Bool_v collinear = Abs(cross) < epsilon;
0222     // TODO: can we use the slope?
0223     // const Bool_v collinear = Abs(pymay - slope * pxmax) < epsilon;
0224 
0225     if (vecCore::MaskFull(!collinear)) {
0226       return result;
0227     }
0228     result |= collinear;
0229 
0230     // can we do this with the slope??
0231     const auto dotproduct = pxmax * (bx - ax) + pymay * (by - ay);
0232 
0233     // check if length correct (use MakeTolerant templates)
0234     const Real_v tol(kTolerance);
0235     result &= (dotproduct >= -tol);
0236     result &= (dotproduct <= tol + Real_v(FromPtr<InternalReal_v>(&fLengthSqr[i])));
0237     return result;
0238   }
0239 
0240   template <typename Real_v, typename Bool_v = vecCore::Mask_v<Real_v>>
0241   VECCORE_ATT_HOST_DEVICE
0242   inline Bool_v ContainsConvex(Vector3D<Real_v> const &point) const
0243   {
0244     const size_t S = fVertices.size();
0245     Bool_v result(false);
0246     Real_v distance = -InfinityLength<Real_v>();
0247     for (size_t i = 0; i < S; ++i) {
0248       Real_v dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0249       vecCore__MaskedAssignFunc(distance, dseg > distance, dseg);
0250     }
0251     result = distance < Real_v(0.);
0252     return result;
0253   }
0254 
0255   template <typename Real_v, typename Bool_v = vecCore::Mask_v<Real_v>>
0256   VECCORE_ATT_HOST_DEVICE
0257   inline Bool_v Contains(Vector3D<Real_v> const &point) const
0258   {
0259     const size_t S = fVertices.size();
0260     Bool_v result(false);
0261     // implementation based on the point-polygon test after Jordan
0262     const auto vertx  = fVertices.x();
0263     const auto verty  = fVertices.y();
0264     const auto slopes = fVertices.z();
0265     const auto py     = point.y();
0266     const auto px     = point.x();
0267     for (size_t i = 0; i < S; ++i) {
0268       const auto vertyI       = verty[i];
0269       const auto vertyJ       = fShiftedYJ[i];
0270       const Bool_v condition1 = (vertyI > py) ^ (vertyJ > py);
0271 
0272       // early return leads to performance slowdown
0273       //  if (vecCore::MaskEmpty(condition1))
0274       //    continue;
0275       const auto vertxI     = vertx[i];
0276       const auto condition2 = px < (slopes[i] * (py - vertyI) + vertxI);
0277 
0278       result = (condition1 & condition2) ^ result;
0279     }
0280     return result;
0281   }
0282 
0283   template <typename Real_v, typename Inside_v = int /*vecCore::Index_v<Real_v>*/>
0284   VECCORE_ATT_HOST_DEVICE
0285   inline Inside_v InsideConvex(Vector3D<Real_v> const &point) const
0286   {
0287     assert(fIsConvex);
0288     const size_t S  = fVertices.size();
0289     Inside_v result = Inside_v(vecgeom::kOutside);
0290     Real_v distance = -InfinityLength<Real_v>();
0291     for (size_t i = 0; i < S; ++i) {
0292       Real_v dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0293       vecCore__MaskedAssignFunc(distance, dseg > distance, dseg);
0294     }
0295     vecCore__MaskedAssignFunc(result, distance < Real_v(-kTolerance), Real_v(vecgeom::kInside));
0296     vecCore__MaskedAssignFunc(result, distance < Real_v(kTolerance), Real_v(vecgeom::kSurface));
0297     return result;
0298   }
0299 
0300   // calculate an underestimate of safety for the convex case
0301   template <typename Real_v>
0302   VECCORE_ATT_HOST_DEVICE
0303   Real_v SafetyConvex(Vector3D<Real_v> const &point, bool inside) const
0304   {
0305     assert(fIsConvex);
0306     const size_t S  = fVertices.size();
0307     Real_v distance = -InfinityLength<Real_v>();
0308     for (size_t i = 0; i < S; ++i) {
0309       Real_v dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0310       vecCore__MaskedAssignFunc(distance, dseg > distance, dseg);
0311       if (inside) distance *= Real_v(-1.);
0312     }
0313     return distance;
0314   }
0315 
0316   // calculate precise safety sqr to the polygon; return the closest "line" id
0317   template <typename Real_v>
0318   VECCORE_ATT_HOST_DEVICE
0319   Real_v SafetySqr(Vector3D<Real_v> const &point, int &closestid) const
0320   {
0321     // implementation based on TGeoPolygone@ROOT
0322     Real_v safe(1E30);
0323     int isegmin = -1;
0324 
0325     const auto vertx = fVertices.x();
0326     const auto verty = fVertices.y();
0327     const auto S     = fVertices.size();
0328     for (size_t i = 0; i < S; ++i) {
0329 
0330       // could use the slope information to calc
0331       const Real_v p1[2] = {vertx[i], verty[i]};
0332       const Real_v p2[2] = {fShiftedXJ[i], fShiftedYJ[i]};
0333 
0334       const auto dx = p2[0] - p1[0];
0335       const auto dy = p2[1] - p1[1];
0336       auto dpx      = point.x() - p1[0];
0337       auto dpy      = point.y() - p1[1];
0338 
0339       // degenerate edge?
0340       // const auto lsq = dx * dx + dy * dy;
0341 
0342       // I don't think this is useful -- its a pure static property
0343       //         if ( ClostToZero(lsq,0)) {
0344       //            ssq = dpx*dpx + dpy*dpy;
0345       //            if (ssq < safe) {
0346       //               safe = ssq;
0347       //               isegmin = i;
0348       //            }
0349       //            continue;
0350       //         }
0351 
0352       const auto u = (dpx * dx + dpy * dy) * fInvLengthSqr[i];
0353       if (u > 1) {
0354         dpx = point.x() - p2[0];
0355         dpy = point.y() - p2[1];
0356       } else {
0357         if (u >= 0) {
0358           // need to divide by lsq now
0359           // since this is a static property of the polygon
0360           // we could actually cache it;
0361           dpx -= u * dx;
0362           dpy -= u * dy;
0363         }
0364       }
0365       const auto ssq = dpx * dpx + dpy * dpy;
0366       if (ssq < safe) {
0367         safe    = ssq;
0368         isegmin = i;
0369       }
0370 
0371       // check if we are done early ( on surface )
0372       if (Abs(safe) < kTolerance * kTolerance) {
0373         closestid = isegmin;
0374         return Real_v(0.);
0375       }
0376     }
0377     closestid = isegmin;
0378     return safe;
0379   }
0380 
0381   VECCORE_ATT_HOST_DEVICE
0382   bool IsConvex() const { return fIsConvex; }
0383 
0384   // check clockwise/counterclockwise condition (returns positive for anti-clockwise)
0385   // useful function to check orientation of points x,y
0386   // before calling the PlanarPolygon constructor
0387   VECCORE_ATT_HOST_DEVICE
0388   static Precision GetOrientation(Precision *x, Precision *y, size_t N)
0389   {
0390     Precision area(0.);
0391     for (size_t i = 0; i < N; ++i) {
0392       const Precision p1[2] = {x[i], y[i]};
0393       const size_t j        = (i + 1) % N;
0394       const Precision p2[2] = {x[j], y[j]};
0395       area += (p1[0] * p2[1] - p1[1] * p2[0]);
0396     }
0397     return area;
0398   }
0399 
0400   /* returns area of polygon */
0401   VECCORE_ATT_HOST_DEVICE
0402   Precision Area() const
0403   {
0404     const auto vertx = fVertices.x();
0405     const auto verty = fVertices.y();
0406 
0407     const auto kS = fVertices.size();
0408     Precision area(0.);
0409     for (size_t i = 0; i < kS; ++i) {
0410       const Precision p1[2] = {vertx[i], verty[i]};
0411       const Precision p2[2] = {fShiftedXJ[i], fShiftedYJ[i]};
0412 
0413       area += (p1[0] * p2[1] - p1[1] * p2[0]);
0414     }
0415     return 0.5 * area;
0416   }
0417 
0418 private:
0419   VECCORE_ATT_HOST_DEVICE
0420   void CalcConvexity()
0421   {
0422     // check if we are always turning into the same sense
0423     // --> check if the sign of the cross product is always the same
0424     const auto vertx = fVertices.x();
0425     const auto verty = fVertices.y();
0426 
0427     const auto kS = fNVertices;
0428     int counter(0);
0429     for (size_t i = 0; i < kS; ++i) {
0430       size_t j              = (i + 1) % kS;
0431       size_t k              = (i + 2) % kS;
0432       const Precision p1[2] = {vertx[j] - vertx[i], verty[j] - verty[i]};
0433       const Precision p2[2] = {vertx[k] - vertx[j], verty[k] - verty[j]};
0434       counter += (p1[0] * p2[1] - p1[1] * p2[0]) < 0 ? -1 : 1;
0435     }
0436     fIsConvex = (size_t)std::abs(counter) == kS;
0437   }
0438 };
0439 
0440 // template specialization for scalar case (do internal vectorization)
0441 #define SPECIALIZE
0442 #ifdef SPECIALIZE
0443 
0444 template <>
0445 VECCORE_ATT_HOST_DEVICE
0446 inline bool PlanarPolygon::ContainsConvex(Vector3D<Precision> const &point) const
0447 {
0448   const size_t S     = fVertices.size();
0449   Precision distance = -InfinityLength<Precision>();
0450   for (size_t i = 0; i < S; ++i) {
0451     Precision dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0452     distance       = vecCore::math::Max(dseg, distance);
0453   }
0454   return (distance < 0.);
0455 }
0456 
0457 template <>
0458 VECCORE_ATT_HOST_DEVICE
0459 inline bool PlanarPolygon::Contains(Vector3D<Precision> const &point) const
0460 {
0461 
0462   using Real_v = vecgeom::VectorBackend::Real_v;
0463   using Bool_v = vecCore::Mask_v<Real_v>;
0464   using vecCore::FromPtr;
0465 
0466   const auto kVectorS = vecCore::VectorSize<Real_v>();
0467 
0468   Bool_v result(false);
0469   const Real_v px(point.x());
0470   const Real_v py(point.y());
0471   const size_t S       = fVertices.size();
0472   const size_t SVector = S - S % kVectorS;
0473   size_t i(0);
0474   const auto vertx  = fVertices.x();
0475   const auto verty  = fVertices.y();
0476   const auto slopes = fVertices.z();
0477   // treat vectorizable part of loop
0478   for (; i < SVector; i += kVectorS) {
0479     const Real_v vertyI(FromPtr<Real_v>(&verty[i]));      // init vectors
0480     const Real_v vertyJ(FromPtr<Real_v>(&fShiftedYJ[i])); // init vectors
0481 
0482     const auto condition1 = (vertyI > py) ^ (vertyJ > py); // xor
0483 
0484     const Real_v vertxI(FromPtr<Real_v>(&vertx[i]));
0485     const Real_v slope(FromPtr<Real_v>(&slopes[i]));
0486     const auto condition2 = px < (slope * (py - vertyI) + vertxI);
0487 
0488     result = result ^ (condition1 & condition2);
0489   }
0490   // reduction over vector lanes
0491   bool reduction(false);
0492   for (size_t j = 0; j < kVectorS; ++j) {
0493     if (vecCore::MaskLaneAt(result, j)) reduction = !reduction;
0494   }
0495 
0496   // treat tail
0497   using Real_s = vecCore::Scalar<Real_v>;
0498   for (; i < S; ++i) {
0499     const Real_s vertyI(FromPtr<Real_s>(&verty[i]));                     // init vectors
0500     const Real_s vertyJ(FromPtr<Real_s>(&fShiftedYJ[i]));                // init vectors
0501     const bool condition1 = (vertyI > point.y()) ^ (vertyJ > point.y()); // xor
0502     const Real_s vertxI(FromPtr<Real_s>(&vertx[i]));
0503     const Real_s slope(FromPtr<Real_s>(&slopes[i]));
0504     const bool condition2 = point.x() < (slope * (point.y() - vertyI) + vertxI);
0505 
0506     reduction = reduction ^ (condition1 & condition2);
0507   }
0508   return reduction;
0509 }
0510 
0511 template <>
0512 VECCORE_ATT_HOST_DEVICE
0513 inline Inside_t PlanarPolygon::InsideConvex(Vector3D<Precision> const &point) const
0514 {
0515   const size_t S = fVertices.size();
0516   assert(fIsConvex);
0517   Precision distance = -InfinityLength<Precision>();
0518   for (size_t i = 0; i < S; ++i) {
0519     Precision dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0520     distance       = vecCore::math::Max(dseg, distance);
0521   }
0522   if (distance > kTolerance) return vecgeom::kOutside;
0523   if (distance < -kTolerance) return vecgeom::kInside;
0524   return vecgeom::kSurface;
0525 }
0526 
0527 // template specialization for convex safety
0528 template <>
0529 VECCORE_ATT_HOST_DEVICE
0530 inline Precision PlanarPolygon::SafetyConvex(Vector3D<Precision> const &point, bool inside) const
0531 {
0532   const size_t S = fVertices.size();
0533   assert(fIsConvex);
0534   Precision distance = -InfinityLength<Precision>();
0535   for (size_t i = 0; i < S; ++i) {
0536     Precision dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0537     distance       = vecCore::math::Max(dseg, distance);
0538   }
0539   if (inside) distance *= -1.;
0540   return distance;
0541 }
0542 
0543 // template specialization for scalar safety
0544 template <>
0545 VECCORE_ATT_HOST_DEVICE
0546 inline Precision PlanarPolygon::SafetySqr(Vector3D<Precision> const &point, int &closestid) const
0547 {
0548   using Real_v = vecgeom::VectorBackend::Real_v;
0549   using vecCore::FromPtr;
0550 
0551   const auto kVectorS = vecCore::VectorSize<Real_v>();
0552   Precision safe(1E30);
0553   int isegmin(-1);
0554 
0555   const auto vertx = fVertices.x();
0556   const auto verty = fVertices.y();
0557   const auto S     = fVertices.size();
0558   const Real_v px(point.x());
0559   const Real_v py(point.y());
0560   for (size_t i = 0; i < S; i += kVectorS) {
0561     const Real_v p1[2] = {FromPtr<Real_v>(&vertx[i]), FromPtr<Real_v>(&verty[i])};
0562     const Real_v p2[2] = {FromPtr<Real_v>(&fShiftedXJ[i]), FromPtr<Real_v>(&fShiftedYJ[i])};
0563 
0564     const auto dx = p2[0] - p1[0];
0565     const auto dy = p2[1] - p1[1];
0566     auto dpx      = px - p1[0];
0567     auto dpy      = py - p1[1];
0568 
0569     // degenerate edge?
0570     const auto lsq = dx * dx + dy * dy;
0571 
0572     // I don't think this is useful -- its a pure static property
0573     //         if ( ClostToZero(lsq,0)) {
0574     //            ssq = dpx*dpx + dpy*dpy;
0575     //            if (ssq < safe) {
0576     //               safe = ssq;
0577     //               isegmin = i;
0578     //            }
0579     //            continue;
0580     //         }
0581 
0582     const auto u     = (dpx * dx + dpy * dy);
0583     const auto cond1 = (u > lsq);
0584     const auto cond2 = (!cond1 && (u >= Real_v(0.)));
0585 
0586     if (!vecCore::MaskEmpty(cond1)) {
0587       vecCore__MaskedAssignFunc(dpx, cond1, px - p2[0]);
0588       vecCore__MaskedAssignFunc(dpy, cond1, py - p2[1]);
0589     }
0590     if (!vecCore::MaskEmpty(cond2)) {
0591       const auto invlsq = Real_v(1.) / lsq;
0592       vecCore__MaskedAssignFunc(dpx, cond2, dpx - u * dx * invlsq);
0593       vecCore__MaskedAssignFunc(dpy, cond2, dpy - u * dy * invlsq);
0594     }
0595     const auto ssq = dpx * dpx + dpy * dpy;
0596 
0597 // combined reduction is a bit tricky to translate:
0598 // if (ssq < safe) {
0599 //      safe = ssq;
0600 //      isegmin = i;
0601 // }
0602 
0603 // a first try is serialized:
0604 #ifndef VECCORE_CUDA
0605     using std::min;
0606 #endif
0607     for (size_t j = 0; j < kVectorS; ++j) {
0608       Precision saftmp = vecCore::LaneAt(ssq, j);
0609       if (saftmp < safe) {
0610         safe    = saftmp;
0611         isegmin = i + j;
0612       }
0613     }
0614     if (Abs(safe) < kTolerance * kTolerance) {
0615       closestid = isegmin;
0616       return 0.;
0617     }
0618   }
0619   closestid = isegmin;
0620   return safe;
0621 }
0622 #endif
0623 
0624 } // namespace VECGEOM_IMPL_NAMESPACE
0625 } // end namespace vecgeom
0626 
0627 #endif