Back to home page

EIC code displayed by LXR

 
 

    


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;              // number of convex vertices
0017   int Nvert;                // total number of vertices
0018   std::vector<int> ind_arr; // vector of all indices of the polygon
0019   int *ind_arr_conv;        // list of all indices of the outscribed convex polygon
0020   std::vector<std::vector<int>>
0021       list_of_gaps; // list of all gaps that consists of index lists of the concave polygons in the gap
0022 
0023   bool is_convex;
0024 
0025   // Constructor that takes a vector of ints to initialize the index array
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   /// @brief Helper function whether a polygon is convex
0032   /// @param vertx vertices in x of original, global polygon
0033   /// @param verty vertices in y of original, global polygon
0034   /// @return If the ReducedPoly polygon is convex
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   /// @brief Helper function whether a polygon is convex
0054   /// @param vertx vertices in x of original, global polygon
0055   /// @param verty vertices in y of original, global polygon
0056   /// @return If the ReducedPoly polygon is convex
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   /// @brief Helper function whether the surface is a real, framed surface or a virtual one
0073   /// @param index index of the convex vertex to be checked
0074   /// @param Ntotalvert number of vertices of the original, global polygon
0075   /// @return if the surface is real
0076   bool IsRealSurface(int index, int Ntotalvert)
0077   {
0078     // check whether two subsequent points in the local polygon correspond to two subsequent points
0079     // in the global polygon.
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   /// @brief This function sets the array of convex indices ind_arr_conv,
0094   ///        and creates the list of gaps, which contains the list of concave indices per gap
0095   /// @param vertx vertices in x of original, global polygon
0096   /// @param verty vertices in y of original, global polygon
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) {                    // loop over all local indices
0105       if (!IsSegConvex(vertx, verty, iseg)) { // if a segment is not convex
0106                                               // Loop over segments to find the next convex segment.
0107         if (iseg + 2 > Nvert) {               // finished loop over indices
0108           break;
0109         }
0110         ivnew     = (iseg + 2) % Nvert; // next vertex
0111         bool conv = false;
0112         // check iseg with next vertices
0113         while (ivnew) {
0114           if (IsSegConvex(vertx, verty, iseg, ivnew)) {
0115             conv = true;
0116             break;
0117           }
0118           // increase indices until next vertex is convex. If last vertex is checked, ivnew = 0 and loop breaks
0119           ivnew = (ivnew + 1) % Nvert;
0120         }
0121         if (!conv) { // no convex found, jump to the next segment
0122           iseg++;
0123           continue;
0124         }
0125       } else { // if segment is convex, pick the next point as index
0126         ivnew = (iseg + 1) % Nvert;
0127       }
0128 
0129       // fill the convex array indconv and increase Nconvex simultaneously
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; // next index is smaller than iseg, so we looped through all indices.
0139       iseg = ivnew;            // set iseg to next point in the convex outscribed polygon
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     // copy local convex indices into array
0147     // note, these are not the global indices yet!
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     // Loop over convex indices and check whether there is a jump between two convex indices
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       // A gap was found. Generate a polygon out of concave indices
0167       // in a backward manner so that it has a right-handed orientation
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       // push gap to list of gaps
0179       list_of_gaps.push_back(ind_arr_conc);
0180       ind_arr_conc.clear();
0181       idx_cx++;
0182     }
0183 
0184     // transform local indices in the convex index array to global indices.
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 /// @brief Converter for Extruded
0194 /// @tparam Real_t Precision type
0195 /// @param xtru Extruded solid to be converted
0196 /// @param logical_id Id of the logical volume
0197 /// @return Conversion success
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   // vector of global indices
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   // arrays of global vertices
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   // recursive function to generate side surfaces of the extruded from a polygon
0232   std::function<void(const ReducedPoly &, int)> GenerateSurfaces = [&](ReducedPoly input_poly, int level = 0) {
0233     logic.push_back(lplus); // use parenthesis around each polygon
0234     if (input_poly.IsConvex(vertx, verty)) {
0235 
0236       // if polygon is convex, the convex index array equals the full index array
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       // For convex polygon, the surfaces can be directly generated
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         // for nested polygons:
0250         // Since real surfaces must point in the outwards direction of the original polygon,
0251         // their vertices are reversed for odd levels (so the they are negated).
0252         // However, since in the logic the full higher level is already negated, the surface must be
0253         // additionally negated to revert the negation by the index reversion.
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           // create real surface with frame
0265           isurf = builder::CreateLocalSurfaceFromVertices<Real_t>(vertices, logical_id);
0266           if (intersection) builder::GetSurface<Real_t>(isurf).fSkipConvexity = true;
0267           // Make the surface for now non-embedding, in future this needs to be done only for surfaces sitting on the
0268           // same common plane
0269           builder::GetSurface<Real_t>(isurf).fEmbedding = false;
0270         } else {
0271           // create virtual surface without frame
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 { // else: polygon is concave
0286 
0287       // get convex indices, list of gaps with indices of all gaps
0288       input_poly.GetConvIndices(vertx, verty);
0289 
0290       // creating the outscribed convex polygone surfaces
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         // for nested polygons:
0298         // Since real surfaces must point in the outwards direction of the original polygon,
0299         // their vertices are reversed for odd levels (so the they are negated).
0300         // However, since in the logic the full higher level is already negated, the surface must be
0301         // additionally negated to revert the negation by the index reversion.
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           // create real surface with frame
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           // create virtual surface without frame
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       // LOGIC: polygon = conv surf 1 & conv surf 2 & ... & (!(gap1 | gap2 | ... ) )
0331       // note, gaps can also be concave and call the function recursively
0332       logic.push_back(land); // last & before subtraction of gaps
0333 
0334       logic.push_back(lplus);
0335       logic.push_back(lnot);  // negation for next level
0336       logic.push_back(lplus); // bracket around gaps
0337 
0338       for (auto j = 0u; j < input_poly.list_of_gaps.size(); ++j) {
0339         // creating the concave polygone that needs to be subtracted
0340         auto subpoly = ReducedPoly(input_poly.list_of_gaps[j]);
0341         GenerateSurfaces(subpoly, level + 1);
0342         // the addition of gaps is their union
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   }; // end of defintion GenerateSurfaces
0350 
0351   // generate side surfaces of polygon
0352   GenerateSurfaces(poly, 0);
0353   logic.push_back(land); // logical and between the side surfaces and the top and bottom surfaces
0354   // Preparation for bottom and top surfaces
0355   // Triangualion by ear clipping
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     // if vertex itself is concave return false
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     // bottom triangles
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     // top triangles
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   // bottom virtual surface
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   // top virtual surface
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 } // namespace conv
0445 } // namespace vgbrep
0446 #endif