Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-20 09:29:51

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(nvertices), fShiftedXJ(nvertices), fShiftedYJ(nvertices), fLengthSqr(nvertices),
0059         fInvLengthSqr(nvertices), fA(nvertices), fB(nvertices), fD(nvertices), fIsConvex(false), fMinX(kInfLength),
0060         fMinY(kInfLength), fMaxX(-kInfLength), fMaxY(-kInfLength), 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     VECGEOM_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 Bool_v OnSegment(size_t i, Real_v const &px, Real_v const &py) const
0196   {
0197     using vecCore::FromPtr;
0198 
0199     // static assert ( cannot have Real_v == InternalReal_v )
0200 
0201     Bool_v result(false);
0202     //
0203     const auto vertx = fVertices.x();
0204     const auto verty = fVertices.y();
0205     // const auto slopes = fVertices.z();
0206 
0207     // check if cross close to zero
0208     const Real_v bx(FromPtr<InternalReal_v>(&vertx[i]));
0209     const Real_v by(FromPtr<InternalReal_v>(&verty[i]));
0210     const Real_v ax(FromPtr<InternalReal_v>(&fShiftedXJ[i]));
0211     const Real_v ay(FromPtr<InternalReal_v>(&fShiftedYJ[i]));
0212     // const Real_v slope(FromPtr<InternalReal_v>(&slopes[i]));
0213 
0214     const Real_v pymay(py - ay);
0215     const Real_v pxmax(px - ax);
0216     const Real_v epsilon(1E-9);
0217 
0218     // optimized crossproduct
0219     const Real_v cross     = (pymay * (bx - ax) - pxmax * (by - ay));
0220     const Bool_v collinear = Abs(cross) < epsilon;
0221     // TODO: can we use the slope?
0222     // const Bool_v collinear = Abs(pymay - slope * pxmax) < epsilon;
0223 
0224     if (vecCore::MaskFull(!collinear)) {
0225       return result;
0226     }
0227     result |= collinear;
0228 
0229     // can we do this with the slope??
0230     const auto dotproduct = pxmax * (bx - ax) + pymay * (by - ay);
0231 
0232     // check if length correct (use MakeTolerant templates)
0233     const Real_v tol(kTolerance);
0234     result &= (dotproduct >= -tol);
0235     result &= (dotproduct <= tol + Real_v(FromPtr<InternalReal_v>(&fLengthSqr[i])));
0236     return result;
0237   }
0238 
0239   template <typename Real_v, typename Bool_v = vecCore::Mask_v<Real_v>>
0240   VECCORE_ATT_HOST_DEVICE inline Bool_v ContainsConvex(Vector3D<Real_v> const &point) const
0241   {
0242     const size_t S = fVertices.size();
0243     Bool_v result(false);
0244     Real_v distance = -InfinityLength<Real_v>();
0245     for (size_t i = 0; i < S; ++i) {
0246       Real_v dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0247       vecCore__MaskedAssignFunc(distance, dseg > distance, dseg);
0248     }
0249     result = distance < Real_v(kTolerance);
0250     return result;
0251   }
0252 
0253   template <typename Real_v, typename Bool_v = vecCore::Mask_v<Real_v>>
0254   VECCORE_ATT_HOST_DEVICE inline Bool_v Contains(Vector3D<Real_v> const &point) const
0255   {
0256     const size_t S = fVertices.size();
0257     Bool_v result(false);
0258     // implementation based on the point-polygon test after Jordan
0259     const auto vertx  = fVertices.x();
0260     const auto verty  = fVertices.y();
0261     const auto slopes = fVertices.z();
0262     const auto py     = point.y();
0263     const auto px     = point.x();
0264     for (size_t i = 0; i < S; ++i) {
0265       const auto vertyI       = verty[i];
0266       const auto vertyJ       = fShiftedYJ[i];
0267       const Bool_v condition1 = (vertyI > py) ^ (vertyJ > py);
0268 
0269       // early return leads to performance slowdown
0270       //  if (vecCore::MaskEmpty(condition1))
0271       //    continue;
0272       const auto vertxI     = vertx[i];
0273       const auto condition2 = px < (slopes[i] * (py - vertyI) + vertxI);
0274 
0275       result = (condition1 & condition2) ^ result;
0276     }
0277     return result;
0278   }
0279 
0280   template <typename Real_v, typename Inside_v = int /*vecCore::Index_v<Real_v>*/>
0281   VECCORE_ATT_HOST_DEVICE inline Inside_v InsideConvex(Vector3D<Real_v> const &point) const
0282   {
0283     VECGEOM_ASSERT(fIsConvex);
0284     const size_t S  = fVertices.size();
0285     Inside_v result = Inside_v(vecgeom::kOutside);
0286     Real_v distance = -InfinityLength<Real_v>();
0287     for (size_t i = 0; i < S; ++i) {
0288       Real_v dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0289       vecCore__MaskedAssignFunc(distance, dseg > distance, dseg);
0290     }
0291     vecCore__MaskedAssignFunc(result, distance < Real_v(-kTolerance), Real_v(vecgeom::kInside));
0292     vecCore__MaskedAssignFunc(result, distance < Real_v(kTolerance), Real_v(vecgeom::kSurface));
0293     return result;
0294   }
0295 
0296   // calculate an underestimate of safety for the convex case
0297   template <typename Real_v>
0298   VECCORE_ATT_HOST_DEVICE Real_v SafetyConvex(Vector3D<Real_v> const &point, bool inside) const
0299   {
0300     VECGEOM_ASSERT(fIsConvex);
0301     const size_t S  = fVertices.size();
0302     Real_v distance = -InfinityLength<Real_v>();
0303     for (size_t i = 0; i < S; ++i) {
0304       Real_v dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0305       vecCore__MaskedAssignFunc(distance, dseg > distance, dseg);
0306       if (inside) distance *= Real_v(-1.);
0307     }
0308     return distance;
0309   }
0310 
0311   // calculate precise safety sqr to the polygon; return the closest "line" id
0312   template <typename Real_v>
0313   VECCORE_ATT_HOST_DEVICE Real_v SafetySqr(Vector3D<Real_v> const &point, int &closestid) const
0314   {
0315     // implementation based on TGeoPolygone@ROOT
0316     Real_v safe(1E30);
0317     int isegmin = -1;
0318 
0319     const auto vertx = fVertices.x();
0320     const auto verty = fVertices.y();
0321     const auto S     = fVertices.size();
0322     for (size_t i = 0; i < S; ++i) {
0323 
0324       // could use the slope information to calc
0325       const Real_v p1[2] = {vertx[i], verty[i]};
0326       const Real_v p2[2] = {fShiftedXJ[i], fShiftedYJ[i]};
0327 
0328       const auto dx = p2[0] - p1[0];
0329       const auto dy = p2[1] - p1[1];
0330       auto dpx      = point.x() - p1[0];
0331       auto dpy      = point.y() - p1[1];
0332 
0333       // degenerate edge?
0334       // const auto lsq = dx * dx + dy * dy;
0335 
0336       // I don't think this is useful -- its a pure static property
0337       //         if ( ClostToZero(lsq,0)) {
0338       //            ssq = dpx*dpx + dpy*dpy;
0339       //            if (ssq < safe) {
0340       //               safe = ssq;
0341       //               isegmin = i;
0342       //            }
0343       //            continue;
0344       //         }
0345 
0346       const auto u = (dpx * dx + dpy * dy) * fInvLengthSqr[i];
0347       if (u > 1) {
0348         dpx = point.x() - p2[0];
0349         dpy = point.y() - p2[1];
0350       } else {
0351         if (u >= 0) {
0352           // need to divide by lsq now
0353           // since this is a static property of the polygon
0354           // we could actually cache it;
0355           dpx -= u * dx;
0356           dpy -= u * dy;
0357         }
0358       }
0359       const auto ssq = dpx * dpx + dpy * dpy;
0360       if (ssq < safe) {
0361         safe    = ssq;
0362         isegmin = i;
0363       }
0364 
0365       // check if we are done early ( on surface )
0366       if (Abs(safe) < kTolerance * kTolerance) {
0367         closestid = isegmin;
0368         return Real_v(0.);
0369       }
0370     }
0371     closestid = isegmin;
0372     return safe;
0373   }
0374 
0375   VECCORE_ATT_HOST_DEVICE
0376   bool IsConvex() const { return fIsConvex; }
0377 
0378   // check clockwise/counterclockwise condition (returns positive for anti-clockwise)
0379   // useful function to check orientation of points x,y
0380   // before calling the PlanarPolygon constructor
0381   VECCORE_ATT_HOST_DEVICE
0382   static Precision GetOrientation(Precision *x, Precision *y, size_t N)
0383   {
0384     Precision area(0.);
0385     for (size_t i = 0; i < N; ++i) {
0386       const Precision p1[2] = {x[i], y[i]};
0387       const size_t j        = (i + 1) % N;
0388       const Precision p2[2] = {x[j], y[j]};
0389       area += (p1[0] * p2[1] - p1[1] * p2[0]);
0390     }
0391     return area;
0392   }
0393 
0394   /* returns area of polygon */
0395   VECCORE_ATT_HOST_DEVICE
0396   Precision Area() const
0397   {
0398     const auto vertx = fVertices.x();
0399     const auto verty = fVertices.y();
0400 
0401     const auto kS = fVertices.size();
0402     Precision area(0.);
0403     for (size_t i = 0; i < kS; ++i) {
0404       const Precision p1[2] = {vertx[i], verty[i]};
0405       const Precision p2[2] = {fShiftedXJ[i], fShiftedYJ[i]};
0406 
0407       area += (p1[0] * p2[1] - p1[1] * p2[0]);
0408     }
0409     return 0.5 * area;
0410   }
0411 
0412 private:
0413   VECCORE_ATT_HOST_DEVICE
0414   void CalcConvexity()
0415   {
0416     // check if we are always turning into the same sense
0417     // --> check if the sign of the cross product is always the same
0418     const auto vertx = fVertices.x();
0419     const auto verty = fVertices.y();
0420 
0421     const auto kS = fNVertices;
0422     int counter(0);
0423     for (size_t i = 0; i < kS; ++i) {
0424       size_t j              = (i + 1) % kS;
0425       size_t k              = (i + 2) % kS;
0426       const Precision p1[2] = {vertx[j] - vertx[i], verty[j] - verty[i]};
0427       const Precision p2[2] = {vertx[k] - vertx[j], verty[k] - verty[j]};
0428       counter += (p1[0] * p2[1] - p1[1] * p2[0]) < 0 ? -1 : 1;
0429     }
0430     fIsConvex = (size_t)std::abs(counter) == kS;
0431   }
0432 };
0433 
0434 // template specialization for scalar case (do internal vectorization)
0435 #define SPECIALIZE
0436 #ifdef SPECIALIZE
0437 
0438 template <>
0439 VECCORE_ATT_HOST_DEVICE inline bool PlanarPolygon::ContainsConvex(Vector3D<Precision> const &point) const
0440 {
0441   const size_t S     = fVertices.size();
0442   Precision distance = -InfinityLength<Precision>();
0443   for (size_t i = 0; i < S; ++i) {
0444     Precision dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0445     distance       = vecCore::math::Max(dseg, distance);
0446   }
0447   return (distance < 0.);
0448 }
0449 
0450 template <>
0451 VECCORE_ATT_HOST_DEVICE inline bool PlanarPolygon::Contains(Vector3D<Precision> const &point) const
0452 {
0453 
0454   using Real_v = vecgeom::VectorBackend::Real_v;
0455   using Bool_v = vecCore::Mask_v<Real_v>;
0456   using vecCore::FromPtr;
0457 
0458   const auto kVectorS = vecCore::VectorSize<Real_v>();
0459 
0460   Bool_v result(false);
0461   const Real_v px(point.x());
0462   const Real_v py(point.y());
0463   const size_t S       = fVertices.size();
0464   const size_t SVector = S - S % kVectorS;
0465   size_t i(0);
0466   const auto vertx  = fVertices.x();
0467   const auto verty  = fVertices.y();
0468   const auto slopes = fVertices.z();
0469   // treat vectorizable part of loop
0470   for (; i < SVector; i += kVectorS) {
0471     const Real_v vertyI(FromPtr<Real_v>(&verty[i]));      // init vectors
0472     const Real_v vertyJ(FromPtr<Real_v>(&fShiftedYJ[i])); // init vectors
0473 
0474     const auto condition1 = (vertyI > py) ^ (vertyJ > py); // xor
0475 
0476     const Real_v vertxI(FromPtr<Real_v>(&vertx[i]));
0477     const Real_v slope(FromPtr<Real_v>(&slopes[i]));
0478     const auto condition2 = px < (slope * (py - vertyI) + vertxI);
0479 
0480     result = result ^ (condition1 & condition2);
0481   }
0482   // reduction over vector lanes
0483   bool reduction(false);
0484   for (size_t j = 0; j < kVectorS; ++j) {
0485     if (vecCore::MaskLaneAt(result, j)) reduction = !reduction;
0486   }
0487 
0488   // treat tail
0489   using Real_s = vecCore::Scalar<Real_v>;
0490   for (; i < S; ++i) {
0491     const Real_s vertyI(FromPtr<Real_s>(&verty[i]));                     // init vectors
0492     const Real_s vertyJ(FromPtr<Real_s>(&fShiftedYJ[i]));                // init vectors
0493     const bool condition1 = (vertyI > point.y()) ^ (vertyJ > point.y()); // xor
0494     const Real_s vertxI(FromPtr<Real_s>(&vertx[i]));
0495     const Real_s slope(FromPtr<Real_s>(&slopes[i]));
0496     const bool condition2 = point.x() < (slope * (point.y() - vertyI) + vertxI);
0497 
0498     reduction = reduction ^ (condition1 & condition2);
0499   }
0500   return reduction;
0501 }
0502 
0503 template <>
0504 VECCORE_ATT_HOST_DEVICE inline Inside_t PlanarPolygon::InsideConvex(Vector3D<Precision> const &point) const
0505 {
0506   const size_t S = fVertices.size();
0507   VECGEOM_ASSERT(fIsConvex);
0508   Precision distance = -InfinityLength<Precision>();
0509   for (size_t i = 0; i < S; ++i) {
0510     Precision dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0511     distance       = vecCore::math::Max(dseg, distance);
0512   }
0513   if (distance > kTolerance) return vecgeom::kOutside;
0514   if (distance < -kTolerance) return vecgeom::kInside;
0515   return vecgeom::kSurface;
0516 }
0517 
0518 // template specialization for convex safety
0519 template <>
0520 VECCORE_ATT_HOST_DEVICE inline Precision PlanarPolygon::SafetyConvex(Vector3D<Precision> const &point,
0521                                                                      bool inside) const
0522 {
0523   const size_t S = fVertices.size();
0524   VECGEOM_ASSERT(fIsConvex);
0525   Precision distance = -InfinityLength<Precision>();
0526   for (size_t i = 0; i < S; ++i) {
0527     Precision dseg = -(fA[i] * point.x() + fB[i] * point.y() + fD[i]);
0528     distance       = vecCore::math::Max(dseg, distance);
0529   }
0530   if (inside) distance *= -1.;
0531   return distance;
0532 }
0533 
0534 // template specialization for scalar safety
0535 template <>
0536 VECCORE_ATT_HOST_DEVICE inline Precision PlanarPolygon::SafetySqr(Vector3D<Precision> const &point,
0537                                                                   int &closestid) const
0538 {
0539   using Real_v = vecgeom::VectorBackend::Real_v;
0540   using vecCore::FromPtr;
0541 
0542   const auto kVectorS = vecCore::VectorSize<Real_v>();
0543   Precision safe(1E30);
0544   int isegmin(-1);
0545 
0546   const auto vertx = fVertices.x();
0547   const auto verty = fVertices.y();
0548   const auto S     = fVertices.size();
0549   const Real_v px(point.x());
0550   const Real_v py(point.y());
0551   for (size_t i = 0; i < S; i += kVectorS) {
0552     const Real_v p1[2] = {FromPtr<Real_v>(&vertx[i]), FromPtr<Real_v>(&verty[i])};
0553     const Real_v p2[2] = {FromPtr<Real_v>(&fShiftedXJ[i]), FromPtr<Real_v>(&fShiftedYJ[i])};
0554 
0555     const auto dx = p2[0] - p1[0];
0556     const auto dy = p2[1] - p1[1];
0557     auto dpx      = px - p1[0];
0558     auto dpy      = py - p1[1];
0559 
0560     // degenerate edge?
0561     const auto lsq = dx * dx + dy * dy;
0562 
0563     // I don't think this is useful -- its a pure static property
0564     //         if ( ClostToZero(lsq,0)) {
0565     //            ssq = dpx*dpx + dpy*dpy;
0566     //            if (ssq < safe) {
0567     //               safe = ssq;
0568     //               isegmin = i;
0569     //            }
0570     //            continue;
0571     //         }
0572 
0573     const auto u     = (dpx * dx + dpy * dy);
0574     const auto cond1 = (u > lsq);
0575     const auto cond2 = (!cond1 && (u >= Real_v(0.)));
0576 
0577     if (!vecCore::MaskEmpty(cond1)) {
0578       vecCore__MaskedAssignFunc(dpx, cond1, px - p2[0]);
0579       vecCore__MaskedAssignFunc(dpy, cond1, py - p2[1]);
0580     }
0581     if (!vecCore::MaskEmpty(cond2)) {
0582       const auto invlsq = Real_v(1.) / lsq;
0583       vecCore__MaskedAssignFunc(dpx, cond2, dpx - u * dx * invlsq);
0584       vecCore__MaskedAssignFunc(dpy, cond2, dpy - u * dy * invlsq);
0585     }
0586     const auto ssq = dpx * dpx + dpy * dpy;
0587 
0588 // combined reduction is a bit tricky to translate:
0589 // if (ssq < safe) {
0590 //      safe = ssq;
0591 //      isegmin = i;
0592 // }
0593 
0594 // a first try is serialized:
0595 #ifndef VECCORE_CUDA
0596     using std::min;
0597 #endif
0598     for (size_t j = 0; j < kVectorS; ++j) {
0599       Precision saftmp = vecCore::LaneAt(ssq, j);
0600       if (saftmp < safe) {
0601         safe    = saftmp;
0602         isegmin = i + j;
0603       }
0604     }
0605     if (Abs(safe) < kTolerance * kTolerance) {
0606       closestid = isegmin;
0607       return 0.;
0608     }
0609   }
0610   closestid = isegmin;
0611   return safe;
0612 }
0613 #endif
0614 
0615 } // namespace VECGEOM_IMPL_NAMESPACE
0616 } // end namespace vecgeom
0617 
0618 #endif