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
0020 class PlanarPolygon {
0021
0022 friend struct SExtruImplementation;
0023
0024 protected:
0025
0026
0027 SOA3D<Precision> fVertices;
0028
0029
0030 Vector<Precision> fShiftedXJ;
0031 Vector<Precision> fShiftedYJ;
0032 Vector<Precision> fLengthSqr;
0033 Vector<Precision> fInvLengthSqr;
0034 Vector<Precision> fA;
0035 Vector<Precision> fB;
0036 Vector<Precision> fD;
0037
0038 bool fIsConvex;
0039 Precision fMinX;
0040 Precision fMinY;
0041 Precision fMaxX;
0042 Precision fMaxY;
0043
0044 size_t fNVertices;
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
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
0069
0070
0071 const size_t kVS = vecCore::VectorSize<vecgeom::VectorBackend::Real_v>();
0072 const auto numberOfVectorChunks = (nvertices / kVS + nvertices % kVS);
0073
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
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
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
0123
0124
0125 for (i = 0; i < (size_t)S; ++i) {
0126 const auto xi = fVertices.x();
0127 const auto yi = fVertices.y();
0128
0129
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);
0134 a *= norm;
0135 b *= norm;
0136
0137 auto d = -(a * xi[i] + b * yi[i]);
0138
0139
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
0145
0146 fA[i] = a;
0147 fB[i] = b;
0148 fD[i] = d;
0149 }
0150
0151
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
0165 CalcConvexity();
0166
0167
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
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
0200
0201 Bool_v result(false);
0202
0203 const auto vertx = fVertices.x();
0204 const auto verty = fVertices.y();
0205
0206
0207
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
0213
0214 const Real_v pymay(py - ay);
0215 const Real_v pxmax(px - ax);
0216 const Real_v epsilon(1E-9);
0217
0218
0219 const Real_v cross = (pymay * (bx - ax) - pxmax * (by - ay));
0220 const Bool_v collinear = Abs(cross) < epsilon;
0221
0222
0223
0224 if (vecCore::MaskFull(!collinear)) {
0225 return result;
0226 }
0227 result |= collinear;
0228
0229
0230 const auto dotproduct = pxmax * (bx - ax) + pymay * (by - ay);
0231
0232
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
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
0270
0271
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 >
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
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
0312 template <typename Real_v>
0313 VECCORE_ATT_HOST_DEVICE Real_v SafetySqr(Vector3D<Real_v> const &point, int &closestid) const
0314 {
0315
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
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
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
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
0353
0354
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
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
0379
0380
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
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
0417
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
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
0470 for (; i < SVector; i += kVectorS) {
0471 const Real_v vertyI(FromPtr<Real_v>(&verty[i]));
0472 const Real_v vertyJ(FromPtr<Real_v>(&fShiftedYJ[i]));
0473
0474 const auto condition1 = (vertyI > py) ^ (vertyJ > py);
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
0483 bool reduction(false);
0484 for (size_t j = 0; j < kVectorS; ++j) {
0485 if (vecCore::MaskLaneAt(result, j)) reduction = !reduction;
0486 }
0487
0488
0489 using Real_s = vecCore::Scalar<Real_v>;
0490 for (; i < S; ++i) {
0491 const Real_s vertyI(FromPtr<Real_s>(&verty[i]));
0492 const Real_s vertyJ(FromPtr<Real_s>(&fShiftedYJ[i]));
0493 const bool condition1 = (vertyI > point.y()) ^ (vertyJ > point.y());
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
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
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
0561 const auto lsq = dx * dx + dy * dy;
0562
0563
0564
0565
0566
0567
0568
0569
0570
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
0589
0590
0591
0592
0593
0594
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 }
0616 }
0617
0618 #endif