File indexing completed on 2026-04-17 08:35:33
0001 #ifndef VECGEOM_SURFACE_EXTRUDEDCONVERTER_H_
0002 #define VECGEOM_SURFACE_EXTRUDEDCONVERTER_H_
0003
0004 #include <numeric>
0005 #include <VecGeom/surfaces/conv/ConvHelper.h>
0006 #include <VecGeom/management/Logger.h>
0007
0008 #include <VecGeom/volumes/SExtru.h>
0009
0010 namespace vgbrep {
0011 namespace conv {
0012
0013 struct ReducedPoly {
0014 using Vector3 = vecgeom::Vector3D<vecgeom::Precision>;
0015
0016 int Nconvex;
0017 int Nvert;
0018 std::vector<int> ind_arr;
0019 int *ind_arr_conv;
0020 std::vector<std::vector<int>>
0021 list_of_gaps;
0022
0023 bool is_convex;
0024
0025
0026 ReducedPoly(const std::vector<int> &input_vec)
0027 : Nconvex(0), Nvert(input_vec.size()), ind_arr(input_vec), is_convex(false)
0028 {
0029 }
0030
0031
0032
0033
0034
0035 bool IsConvex(const vecgeom::Precision *vertx, const vecgeom::Precision *verty)
0036 {
0037 if (Nvert == 3) return true;
0038 int j, k;
0039 Vector3 point1;
0040 Vector3 point2;
0041 Vector3 point3;
0042 for (int i = 0; i < Nvert; i++) {
0043 j = (i + 1) % Nvert;
0044 k = (i + 2) % Nvert;
0045 if (!IsRightSided<vecgeom::Precision>({vertx[ind_arr[i]], verty[ind_arr[i]], 0},
0046 {vertx[ind_arr[j]], verty[ind_arr[j]], 0},
0047 {vertx[ind_arr[k]], verty[ind_arr[k]], 0}))
0048 return false;
0049 }
0050 return true;
0051 };
0052
0053
0054
0055
0056
0057 bool IsSegConvex(const vecgeom::Precision *vertx, const vecgeom::Precision *verty, int i1, int i2 = -1)
0058 {
0059 if (i2 < 0) i2 = (i1 + 1) % Nvert;
0060
0061 for (int i = 0; i < Nvert; i++) {
0062 if (i == i1 || i == i2) continue;
0063
0064 if (!IsRightSided<vecgeom::Precision>({vertx[ind_arr[i]], verty[ind_arr[i]], 0},
0065 {vertx[ind_arr[i1]], verty[ind_arr[i1]], 0},
0066 {vertx[ind_arr[i2]], verty[ind_arr[i2]], 0}))
0067 return false;
0068 }
0069 return true;
0070 };
0071
0072
0073
0074
0075
0076 bool IsRealSurface(int index, int Ntotalvert)
0077 {
0078
0079
0080 int nstep = ind_arr_conv[(index + 1) % Nconvex] - ind_arr_conv[index];
0081
0082 if (((ind_arr_conv[(index + 1) % Nconvex] == 0) && ind_arr_conv[index] == Ntotalvert - 1) ||
0083 ((ind_arr_conv[index] == 0) && ind_arr_conv[(index + 1) % Nconvex] == Ntotalvert - 1))
0084 return true;
0085
0086 if (std::abs(nstep) == 1) {
0087 return true;
0088 } else {
0089 return false;
0090 }
0091 };
0092
0093
0094
0095
0096
0097 void GetConvIndices(const vecgeom::Precision *vertx, const vecgeom::Precision *verty)
0098 {
0099 int iseg = 0;
0100 int ivnew;
0101 std::unique_ptr<int[]> indconv(new int[Nvert]);
0102 std::memset(indconv.get(), 0, Nvert * sizeof(int));
0103
0104 while (iseg < Nvert) {
0105 if (!IsSegConvex(vertx, verty, iseg)) {
0106
0107 if (iseg + 2 > Nvert) {
0108 break;
0109 }
0110 ivnew = (iseg + 2) % Nvert;
0111 bool conv = false;
0112
0113 while (ivnew) {
0114 if (IsSegConvex(vertx, verty, iseg, ivnew)) {
0115 conv = true;
0116 break;
0117 }
0118
0119 ivnew = (ivnew + 1) % Nvert;
0120 }
0121 if (!conv) {
0122 iseg++;
0123 continue;
0124 }
0125 } else {
0126 ivnew = (iseg + 1) % Nvert;
0127 }
0128
0129
0130 if (!Nconvex) {
0131 indconv[Nconvex++] = iseg;
0132 } else if (indconv[Nconvex - 1] != iseg) {
0133 indconv[Nconvex++] = iseg;
0134 }
0135 if (iseg < Nvert - 1) {
0136 indconv[Nconvex++] = ivnew;
0137 }
0138 if (ivnew < iseg) break;
0139 iseg = ivnew;
0140 }
0141 if (!Nconvex) {
0142 VECGEOM_LOG(warning) << " ERROR, no convex point found in polygon generation for a supposedly convex polygon!"
0143 << std::endl;
0144 return;
0145 }
0146
0147
0148 ind_arr_conv = new int[Nvert];
0149 memcpy(ind_arr_conv, indconv.get(), Nconvex * sizeof(int));
0150
0151 std::vector<int> ind_arr_conc;
0152
0153
0154 int idx_cx = 0;
0155 int indnext, indback;
0156 int nskip;
0157 while (idx_cx < Nconvex) {
0158 indnext = (idx_cx + 1) % Nconvex;
0159 nskip = ind_arr_conv[indnext] - ind_arr_conv[idx_cx];
0160 if (nskip < 0) nskip += Nvert;
0161 if (nskip == 1) {
0162 idx_cx++;
0163 continue;
0164 }
0165
0166
0167
0168 ind_arr_conc.push_back(ind_arr[ind_arr_conv[idx_cx]]);
0169 ind_arr_conc.push_back(ind_arr[ind_arr_conv[indnext]]);
0170 indback = ind_arr_conv[indnext] - 1;
0171 if (indback < 0) indback += Nvert;
0172 while (indback != ind_arr_conv[idx_cx]) {
0173 ind_arr_conc.push_back(ind_arr[indback]);
0174 indback--;
0175 if (indback < 0) indback += Nvert;
0176 }
0177
0178
0179 list_of_gaps.push_back(ind_arr_conc);
0180 ind_arr_conc.clear();
0181 idx_cx++;
0182 }
0183
0184
0185 for (idx_cx = 0; idx_cx < Nconvex; idx_cx++) {
0186 ind_arr_conv[idx_cx] = ind_arr[ind_arr_conv[idx_cx]];
0187 }
0188
0189 return;
0190 };
0191 };
0192
0193
0194
0195
0196
0197
0198 template <typename Real_t>
0199 bool CreateSExtrudedSurfaces(vecgeom::UnplacedSExtruVolume const &xtru, int logical_id, bool intersection = false)
0200 {
0201 using Vector3 = vecgeom::Vector3D<vecgeom::Precision>;
0202
0203 auto const &shell = xtru.GetStruct();
0204 int n_vertices = shell.GetPolygon().GetNVertices();
0205 auto const &vertices_poly = shell.GetPolygon().GetVertices();
0206
0207
0208 std::vector<int> idx_vec(n_vertices);
0209 std::iota(idx_vec.begin(), idx_vec.end(), 0);
0210
0211 auto poly = ReducedPoly(idx_vec);
0212
0213
0214 const auto vertx = vertices_poly.x();
0215 const auto verty = vertices_poly.y();
0216
0217 LogicExpressionCPU logic;
0218 int isurf;
0219
0220 vecgeom::Transformation3DMP<Real_t> transformation;
0221 std::vector<Vector3> vertices;
0222 std::vector<Vector3> triangle_var;
0223 Vector3 vert1 = {0., 0., 0.};
0224 Vector3 vert2 = {0., 0., 0.};
0225
0226 Vector3 section_origin1(0, 0, shell.GetLowerZ());
0227 Vector3 section_origin2(0, 0, shell.GetUpperZ());
0228 Real_t section_scale1 = 1;
0229 Real_t section_scale2 = 1;
0230
0231
0232 std::function<void(const ReducedPoly &, int)> GenerateSurfaces = [&](ReducedPoly input_poly, int level = 0) {
0233 logic.push_back(lplus);
0234 if (input_poly.IsConvex(vertx, verty)) {
0235
0236
0237 input_poly.ind_arr_conv = new int[input_poly.Nvert];
0238 for (int i = 0; i < input_poly.Nvert; ++i) {
0239 input_poly.ind_arr_conv[i] = input_poly.ind_arr[i];
0240 }
0241 input_poly.Nconvex = input_poly.Nvert;
0242
0243
0244 for (int i = 0; i < input_poly.Nvert; ++i) {
0245 vert1.Set(vertx[input_poly.ind_arr_conv[i]], verty[input_poly.ind_arr_conv[i]], 0);
0246 vert2.Set(vertx[input_poly.ind_arr_conv[(i + 1) % input_poly.Nconvex]],
0247 verty[input_poly.ind_arr_conv[(i + 1) % input_poly.Nconvex]], 0);
0248
0249
0250
0251
0252
0253
0254 if (level % 2 == 0) {
0255 vertices = {section_origin1 + section_scale1 * vert1, section_origin2 + section_scale2 * vert1,
0256 section_origin2 + section_scale2 * vert2, section_origin1 + section_scale1 * vert2};
0257 } else {
0258 vertices = {section_origin1 + section_scale1 * vert2, section_origin2 + section_scale2 * vert2,
0259 section_origin2 + section_scale2 * vert1, section_origin1 + section_scale1 * vert1};
0260 logic.push_back(lnot);
0261 }
0262
0263 if (input_poly.IsRealSurface(i, n_vertices)) {
0264
0265 isurf = builder::CreateLocalSurfaceFromVertices<Real_t>(vertices, logical_id);
0266 if (intersection) builder::GetSurface<Real_t>(isurf).fSkipConvexity = true;
0267
0268
0269 builder::GetSurface<Real_t>(isurf).fEmbedding = false;
0270 } else {
0271
0272 transformation = builder::TransformationFromPlanarPoints<Real_t>(vertices);
0273 isurf = builder::CreateLocalSurface<Real_t>(builder::CreateUnplacedSurface<Real_t>(SurfaceType::kPlanar),
0274 Frame{FrameType::kNoFrame}, transformation);
0275 if (intersection) builder::GetSurface<Real_t>(isurf).fSkipConvexity = true;
0276 builder::AddSurfaceToShell<Real_t>(logical_id, isurf);
0277 }
0278
0279 logic.push_back(isurf);
0280 if (i < input_poly.Nconvex - 1) {
0281 logic.push_back(land);
0282 }
0283 }
0284
0285 } else {
0286
0287
0288 input_poly.GetConvIndices(vertx, verty);
0289
0290
0291 for (int i = 0; i < input_poly.Nconvex; ++i) {
0292
0293 vert1.Set(vertx[input_poly.ind_arr_conv[i]], verty[input_poly.ind_arr_conv[i]], 0);
0294 vert2.Set(vertx[input_poly.ind_arr_conv[(i + 1) % input_poly.Nconvex]],
0295 verty[input_poly.ind_arr_conv[(i + 1) % input_poly.Nconvex]], 0);
0296
0297
0298
0299
0300
0301
0302 if (level % 2 == 0) {
0303 vertices = {section_origin1 + section_scale1 * vert1, section_origin2 + section_scale2 * vert1,
0304 section_origin2 + section_scale2 * vert2, section_origin1 + section_scale1 * vert2};
0305 } else {
0306 vertices = {section_origin1 + section_scale1 * vert2, section_origin2 + section_scale2 * vert2,
0307 section_origin2 + section_scale2 * vert1, section_origin1 + section_scale1 * vert1};
0308 logic.push_back(lnot);
0309 }
0310
0311 if (input_poly.IsRealSurface(i, n_vertices)) {
0312
0313 isurf = builder::CreateLocalSurfaceFromVertices<Real_t>(vertices, logical_id);
0314 builder::GetSurface<Real_t>(isurf).fEmbedding = false;
0315 if (intersection) builder::GetSurface<Real_t>(isurf).fSkipConvexity = true;
0316 } else {
0317
0318 transformation = builder::TransformationFromPlanarPoints<Real_t>(vertices);
0319 isurf = builder::CreateLocalSurface<Real_t>(builder::CreateUnplacedSurface<Real_t>(SurfaceType::kPlanar),
0320 Frame{FrameType::kNoFrame}, transformation);
0321 builder::AddSurfaceToShell<Real_t>(logical_id, isurf);
0322 if (intersection) builder::GetSurface<Real_t>(isurf).fSkipConvexity = true;
0323 }
0324 logic.push_back(isurf);
0325 if (i < input_poly.Nconvex - 1) {
0326 logic.push_back(land);
0327 }
0328 }
0329
0330
0331
0332 logic.push_back(land);
0333
0334 logic.push_back(lplus);
0335 logic.push_back(lnot);
0336 logic.push_back(lplus);
0337
0338 for (auto j = 0u; j < input_poly.list_of_gaps.size(); ++j) {
0339
0340 auto subpoly = ReducedPoly(input_poly.list_of_gaps[j]);
0341 GenerateSurfaces(subpoly, level + 1);
0342
0343 if (j < input_poly.list_of_gaps.size() - 1) logic.push_back(lor);
0344 }
0345 logic.push_back(lminus);
0346 logic.push_back(lminus);
0347 }
0348 logic.push_back(lminus);
0349 };
0350
0351
0352 GenerateSurfaces(poly, 0);
0353 logic.push_back(land);
0354
0355
0356
0357 auto sign = [=](Vector3 v1, Vector3 v2, Vector3 v3) {
0358 return (v1[0] - v3[0]) * (v2[1] - v3[1]) - (v2[0] - v3[0]) * (v1[1] - v3[1]);
0359 };
0360
0361 auto IsInsideTriangle = [=](Vector3 const &vert_a, Vector3 const &vert_b, Vector3 const &vert_c,
0362 Vector3 const &vert_p) {
0363 auto d1 = sign(vert_p, vert_a, vert_b);
0364 auto d2 = sign(vert_p, vert_b, vert_c);
0365 auto d3 = sign(vert_p, vert_c, vert_a);
0366
0367 auto has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
0368 auto has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
0369
0370 return !(has_neg && has_pos);
0371 };
0372
0373 auto IsEar = [&](int a, int b, int c, Vector3 const &vert_a, Vector3 const &vert_b, Vector3 const &vert_c) {
0374 for (int k = 0; k < n_vertices; k++) {
0375 if (k != a && k != b && k != c) {
0376 Vector3 vert_p(vertx[k], verty[k], 0);
0377 if (IsInsideTriangle(vert_a, vert_b, vert_c, vert_p)) {
0378 return false;
0379 }
0380 }
0381 }
0382
0383 if (sign(vert_a, vert_b, vert_c) >= 0) return false;
0384 return true;
0385 };
0386
0387 std::vector<std::vector<Vector3>> triangles;
0388 while (idx_vec.size() > 2) {
0389 for (unsigned i = 0; i < idx_vec.size(); ++i) {
0390 int a = idx_vec[(i + idx_vec.size() - 1) % idx_vec.size()];
0391 int b = idx_vec[i];
0392 int c = idx_vec[(i + 1) % idx_vec.size()];
0393 Vector3 vert_a(vertx[a], verty[a], 0);
0394 Vector3 vert_b(vertx[b], verty[b], 0);
0395 Vector3 vert_c(vertx[c], verty[c], 0);
0396
0397 if (IsEar(a, b, c, vert_a, vert_b, vert_c)) {
0398 triangles.push_back({vert_c, vert_b, vert_a});
0399 idx_vec.erase(idx_vec.begin() + i);
0400 }
0401 }
0402 }
0403
0404 for (unsigned j = 0; j < triangles.size(); j++) {
0405 triangle_var = triangles[j];
0406
0407
0408 vertices = {section_origin1 + section_scale1 * triangle_var[2], section_origin1 + section_scale1 * triangle_var[1],
0409 section_origin1 + section_scale1 * triangle_var[0]};
0410 isurf = builder::CreateLocalSurfaceFromVertices<Real_t>(vertices, logical_id);
0411 builder::GetSurface<Real_t>(isurf).fEmbedding = false;
0412 if (intersection) builder::GetSurface<Real_t>(isurf).fSkipConvexity = true;
0413
0414
0415 vertices = {section_origin2 + section_scale2 * triangle_var[0], section_origin2 + section_scale2 * triangle_var[1],
0416 section_origin2 + section_scale2 * triangle_var[2]};
0417 isurf = builder::CreateLocalSurfaceFromVertices<Real_t>(vertices, logical_id);
0418 builder::GetSurface<Real_t>(isurf).fEmbedding = false;
0419 if (intersection) builder::GetSurface<Real_t>(isurf).fSkipConvexity = true;
0420 }
0421
0422
0423 isurf = builder::CreateLocalSurface<Real_t>(
0424 builder::CreateUnplacedSurface<Real_t>(SurfaceType::kPlanar), Frame{FrameType::kNoFrame},
0425 vecgeom::Transformation3DMP<Precision>(0., 0., shell.GetLowerZ(), 0., 180., 0.));
0426 builder::AddSurfaceToShell<Real_t>(logical_id, isurf);
0427 if (intersection) builder::GetSurface<Real_t>(isurf).fSkipConvexity = true;
0428 logic.push_back(isurf);
0429 logic.push_back(land);
0430
0431
0432 isurf = builder::CreateLocalSurface<Real_t>(
0433 builder::CreateUnplacedSurface<Real_t>(SurfaceType::kPlanar), Frame{FrameType::kNoFrame},
0434 vecgeom::Transformation3DMP<Precision>(0., 0., shell.GetUpperZ(), 0., 0., 0.));
0435 builder::AddSurfaceToShell<Real_t>(logical_id, isurf);
0436 if (intersection) builder::GetSurface<Real_t>(isurf).fSkipConvexity = true;
0437 logic.push_back(isurf);
0438
0439 builder::AddLogicToShell<Real_t>(logical_id, logic);
0440
0441 return true;
0442 }
0443
0444 }
0445 }
0446 #endif