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
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(), 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
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 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
0196 Bool_v OnSegment(size_t i, Real_v const &px, Real_v const &py) const
0197 {
0198 using vecCore::FromPtr;
0199
0200
0201
0202 Bool_v result(false);
0203
0204 const auto vertx = fVertices.x();
0205 const auto verty = fVertices.y();
0206
0207
0208
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
0214
0215 const Real_v pymay(py - ay);
0216 const Real_v pxmax(px - ax);
0217 const Real_v epsilon(1E-9);
0218
0219
0220 const Real_v cross = (pymay * (bx - ax) - pxmax * (by - ay));
0221 const Bool_v collinear = Abs(cross) < epsilon;
0222
0223
0224
0225 if (vecCore::MaskFull(!collinear)) {
0226 return result;
0227 }
0228 result |= collinear;
0229
0230
0231 const auto dotproduct = pxmax * (bx - ax) + pymay * (by - ay);
0232
0233
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
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
0273
0274
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 >
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
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
0317 template <typename Real_v>
0318 VECCORE_ATT_HOST_DEVICE
0319 Real_v SafetySqr(Vector3D<Real_v> const &point, int &closestid) const
0320 {
0321
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
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
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
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
0359
0360
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
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
0385
0386
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
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
0423
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
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
0478 for (; i < SVector; i += kVectorS) {
0479 const Real_v vertyI(FromPtr<Real_v>(&verty[i]));
0480 const Real_v vertyJ(FromPtr<Real_v>(&fShiftedYJ[i]));
0481
0482 const auto condition1 = (vertyI > py) ^ (vertyJ > py);
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
0491 bool reduction(false);
0492 for (size_t j = 0; j < kVectorS; ++j) {
0493 if (vecCore::MaskLaneAt(result, j)) reduction = !reduction;
0494 }
0495
0496
0497 using Real_s = vecCore::Scalar<Real_v>;
0498 for (; i < S; ++i) {
0499 const Real_s vertyI(FromPtr<Real_s>(&verty[i]));
0500 const Real_s vertyJ(FromPtr<Real_s>(&fShiftedYJ[i]));
0501 const bool condition1 = (vertyI > point.y()) ^ (vertyJ > point.y());
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
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
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
0570 const auto lsq = dx * dx + dy * dy;
0571
0572
0573
0574
0575
0576
0577
0578
0579
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
0598
0599
0600
0601
0602
0603
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 }
0625 }
0626
0627 #endif