File indexing completed on 2026-04-17 08:35:35
0001 #ifndef VECGEOM_SURFACE_ZPHIMASK_H
0002 #define VECGEOM_SURFACE_ZPHIMASK_H
0003
0004 #include <VecGeom/management/Logger.h>
0005 #include <VecGeom/surfaces/base/CommonTypes.h>
0006
0007 namespace vgbrep {
0008
0009
0010
0011 template <typename Real_t>
0012 struct ZPhiMask {
0013
0014
0015
0016
0017
0018 using value_type = Real_t;
0019 Range<Real_t> rangeZ;
0020 Real_t r0;
0021 Real_t invcalf;
0022 AngleVector<Real_t> vecSPhi;
0023 AngleVector<Real_t> vecEPhi;
0024 bool isFullCirc;
0025
0026 ZPhiMask() = default;
0027 template <typename Real_i>
0028 ZPhiMask(Real_i zmin, Real_i zmax, bool isFullCircle, Real_i rbottom, Real_i rtop, Real_i sphi = Real_i{0},
0029 Real_i ephi = Real_i{0})
0030 : rangeZ(static_cast<Real_t>(zmin), static_cast<Real_t>(zmax)), isFullCirc(isFullCircle)
0031 {
0032 r0 = static_cast<Real_t>(0.5 * (rbottom + rtop));
0033 Real_t t = static_cast<Real_t>((rtop - rbottom) / (zmax - zmin));
0034 invcalf = (t == Real_t(0)) ? Real_t(1) : vecCore::math::Sqrt(Real_t(1) + t * t);
0035
0036 if (isFullCirc) return;
0037 vecSPhi.Set(static_cast<Real_t>(vecgeom::Cos(sphi)), static_cast<Real_t>(vecgeom::Sin(sphi)));
0038 vecEPhi.Set(static_cast<Real_t>(vecgeom::Cos(ephi)), static_cast<Real_t>(vecgeom::Sin(ephi)));
0039 }
0040 template <typename Real_i>
0041 ZPhiMask(const ZPhiMask<Real_i> &other)
0042 : r0(static_cast<Real_t>(other.r0)), invcalf(static_cast<Real_t>(other.invcalf)), isFullCirc(other.isFullCirc)
0043 {
0044 rangeZ = Range<Real_t>(other.rangeZ);
0045 vecSPhi = AngleVector<Real_t>(other.vecSPhi);
0046 vecEPhi = AngleVector<Real_t>(other.vecEPhi);
0047 }
0048
0049
0050
0051
0052
0053
0054 void GetXYextent(Real_t &xmin, Real_t &xmax, Real_t &ymin, Real_t &ymax) const
0055 {
0056 auto const rmax = vecCore::math::Max(Radius(rangeZ[0]), Radius(rangeZ[1]));
0057 xmin = -rmax;
0058 xmax = rmax;
0059 ymin = -rmax;
0060 ymax = rmax;
0061
0062 Vector2D<Real_t> axis{1, 0};
0063 if (!isFullCirc) {
0064
0065 Real_t x1, x2, x3, x4, y1, y2, y3, y4;
0066
0067 auto const rmin = vecCore::math::Min(Radius(rangeZ[0]), Radius(rangeZ[1]));
0068
0069 x1 = rmax * axis.Dot(vecSPhi);
0070 x2 = rmax * axis.Dot(vecEPhi);
0071 x3 = rmin * axis.Dot(vecSPhi);
0072 x4 = rmin * axis.Dot(vecEPhi);
0073 axis.Set(0, 1);
0074 y1 = rmax * axis.Dot(vecSPhi);
0075 y2 = rmax * axis.Dot(vecEPhi);
0076 y3 = rmin * axis.Dot(vecSPhi);
0077 y4 = rmin * axis.Dot(vecEPhi);
0078
0079 xmax = vecgeom::Max(vecgeom::Max(x1, x2), vecgeom::Max(x3, x4));
0080 ymax = vecgeom::Max(vecgeom::Max(y1, y2), vecgeom::Max(y3, y4));
0081 xmin = vecgeom::Min(vecgeom::Min(x1, x2), vecgeom::Min(x3, x4));
0082 ymin = vecgeom::Min(vecgeom::Min(y1, y2), vecgeom::Min(y3, y4));
0083
0084 if (InsidePhi(1, 0)) xmax = rmax;
0085 if (InsidePhi(0, 1)) ymax = rmax;
0086 if (InsidePhi(-1, 0)) xmin = -rmax;
0087 if (InsidePhi(0, -1)) ymin = -rmax;
0088 }
0089 }
0090
0091
0092
0093
0094 void Extent3D(Vector3D<Real_t> &aMin, Vector3D<Real_t> &aMax) const
0095 {
0096 Real_t xmin, xmax, ymin, ymax;
0097 GetXYextent(xmin, xmax, ymin, ymax);
0098 aMin.Set(xmin, ymin, rangeZ[0]);
0099 aMax.Set(xmax, ymax, rangeZ[1]);
0100 }
0101
0102
0103
0104
0105 Real_t Radius(Real_t z) const
0106 {
0107 Real_t t = vecCore::math::Sqrt((invcalf - Real_t(1)) * (invcalf + Real_t(1)));
0108 return r0 + t * z;
0109 }
0110
0111
0112
0113
0114
0115 VECCORE_ATT_HOST_DEVICE
0116 VECGEOM_FORCE_INLINE
0117 bool InsidePhi(Real_t const &x, Real_t const &y) const
0118 {
0119 if (isFullCirc) return true;
0120 AngleVector<Real_t> localAngle{x, y};
0121 auto convex = vecSPhi.CrossZ(vecEPhi) > Real_t(0);
0122 auto in1 = vecSPhi.CrossZ(localAngle) > -vecgeom::kToleranceStrict<Real_t>;
0123 auto in2 = localAngle.CrossZ(vecEPhi) > -vecgeom::kToleranceStrict<Real_t>;
0124 return convex ? in1 && in2 : in1 || in2;
0125 }
0126
0127
0128
0129
0130
0131
0132
0133 VECCORE_ATT_HOST_DEVICE
0134 bool Inside(Vector3D<Real_t> const &local) const
0135 {
0136
0137 if (local[2] < vecgeom::MakeMinusTolerant<true, Real_t>(rangeZ[0]) ||
0138 local[2] > vecgeom::MakePlusTolerant<true, Real_t>(rangeZ[1]))
0139 return false;
0140 return InsidePhi(local[0], local[1]);
0141 }
0142
0143
0144
0145
0146 ZPhiMask<Real_t> InverseTransform(vecgeom::Transformation2DMP<Real_t> const &trans) const
0147 {
0148 ZPhiMask<Real_t> frame(*this);
0149
0150 Vector3D<Real_t> local;
0151 local = trans.InverseTransform(Vector3D<Real_t>{0, 0, rangeZ[0]});
0152 frame.rangeZ[0] = local[2];
0153 local = trans.InverseTransform(Vector3D<Real_t>{0, 0, rangeZ[1]});
0154 frame.rangeZ[1] = local[2];
0155
0156
0157 frame.r0 = Radius(-trans.Translation()[2]);
0158
0159 if (!isFullCirc) {
0160
0161 local = trans.InverseTransformDirection(Vector3D<Real_t>{vecSPhi[0], vecSPhi[1], 0});
0162 frame.vecSPhi.Set(local[0], local[1]);
0163 local = trans.InverseTransformDirection(Vector3D<Real_t>{vecEPhi[0], vecEPhi[1], 0});
0164 frame.vecEPhi.Set(local[0], local[1]);
0165 }
0166 return frame;
0167 }
0168
0169
0170
0171 bool CombineWith(ZPhiMask<Real_t> const &other)
0172 {
0173
0174 if (vecCore::math::Abs(invcalf - other.invcalf) > vecgeom::kToleranceDist<Real_t>) return false;
0175 if (vecCore::math::Abs(Radius(0) - other.Radius(0)) > vecgeom::kToleranceDist<Real_t>) return false;
0176
0177 rangeZ[0] = vecCore::math::Min(rangeZ[0], other.rangeZ[0]);
0178 rangeZ[1] = vecCore::math::Max(rangeZ[1], other.rangeZ[1]);
0179 isFullCirc |= other.isFullCirc;
0180 if (isFullCirc) return true;
0181
0182
0183 if (ApproxEqualVector2(vecSPhi, other.vecSPhi)) {
0184 if (!InsidePhi(other.vecEPhi[0], other.vecEPhi[1])) vecEPhi = other.vecEPhi;
0185 return true;
0186 }
0187
0188 if (ApproxEqualVector2(vecEPhi, other.vecEPhi)) {
0189 if (!InsidePhi(other.vecSPhi[0], other.vecSPhi[1])) vecSPhi = other.vecSPhi;
0190 return true;
0191 }
0192
0193 if (ApproxEqualVector2(vecEPhi, other.vecSPhi)) {
0194 if (InsidePhi(other.vecEPhi[0], other.vecEPhi[1])) {
0195 vecEPhi = vecSPhi;
0196 isFullCirc = true;
0197 } else {
0198 vecEPhi = other.vecEPhi;
0199 }
0200 return true;
0201 }
0202
0203 if (ApproxEqualVector2(vecSPhi, other.vecEPhi)) {
0204 if (InsidePhi(other.vecSPhi[0], other.vecSPhi[1])) {
0205 vecEPhi = vecSPhi;
0206 isFullCirc = true;
0207 } else {
0208 vecSPhi = other.vecSPhi;
0209 }
0210 return true;
0211 }
0212
0213 bool in1 = other.InsidePhi(vecSPhi[0], vecSPhi[1]);
0214 bool in2 = other.InsidePhi(vecEPhi[0], vecEPhi[1]);
0215 bool in3 = InsidePhi(other.vecSPhi[0], other.vecSPhi[1]);
0216 bool in4 = InsidePhi(other.vecEPhi[0], other.vecEPhi[1]);
0217
0218 if (!(in1 || in2 || in3 || in4)) {
0219 if ((vecSPhi - other.vecEPhi).Mag2() > (other.vecSPhi - vecEPhi).Mag2())
0220 vecEPhi = other.vecEPhi;
0221 else
0222 vecSPhi = other.vecSPhi;
0223 return true;
0224 }
0225
0226 if (in1 && in2 && in3 && in4) {
0227 vecEPhi = vecSPhi;
0228 isFullCirc = true;
0229 return true;
0230 }
0231
0232 if (!(in1 || in2)) return true;
0233
0234 if (!(in3 || in4)) {
0235 vecSPhi = other.vecSPhi;
0236 vecEPhi = other.vecEPhi;
0237 return true;
0238 }
0239
0240 if (!in1 && in2) {
0241 vecEPhi = other.vecEPhi;
0242 return true;
0243 }
0244
0245 if (in1 && !in2) {
0246 vecSPhi = other.vecSPhi;
0247 return true;
0248 }
0249 VECGEOM_LOG(critical) << "CombineWith: wrong logic";
0250 return false;
0251 }
0252
0253
0254
0255
0256
0257
0258
0259 VECCORE_ATT_HOST_DEVICE
0260 Real_t Safety(Vector3D<Real_t> const &local, Real_t safetySurf, bool &valid) const
0261 {
0262 valid = true;
0263 if (!InsidePhi(local[0], local[1])) {
0264
0265
0266 valid = false;
0267 return 0.;
0268 }
0269 auto safetyZ = vecCore::math::Max(local[2] - rangeZ[1], rangeZ[0] - local[2]);
0270 return vecCore::math::Max(safetySurf, safetyZ);
0271 }
0272 };
0273
0274 }
0275
0276 #endif