File: ExtrudedStruct.h

package info (click to toggle)
vecgeom 1.2.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 23,928 kB
  • sloc: cpp: 88,717; ansic: 6,894; python: 1,035; sh: 582; sql: 538; makefile: 29
file content (394 lines) | stat: -rw-r--r-- 13,773 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
/// @file ExtrudedStruct.h
/// @author Mihaela Gheata (mihaela.gheata@cern.ch)

#ifndef VECGEOM_EXTRUDED_STRUCT_H
#define VECGEOM_EXTRUDED_STRUCT_H

#include "VecGeom/base/Config.h"

#include "VecGeom/volumes/PolygonalShell.h"
#include "VecGeom/volumes/TessellatedStruct.h"

#ifndef VECGEOM_ENABLE_CUDA
#include "VecGeom/volumes/TessellatedSection.h"
#endif

namespace vecgeom {

VECGEOM_DEVICE_FORWARD_DECLARE(class ExtrudedStruct;);
VECGEOM_DEVICE_DECLARE_CONV(class, ExtrudedStruct);
VECGEOM_DEVICE_DECLARE_CONV(struct, XtruVertex2);
VECGEOM_DEVICE_DECLARE_CONV(struct, XtruSection);

inline namespace VECGEOM_IMPL_NAMESPACE {

// Structure wrapping either a polygonal shell helper in case of two
// extruded sections or a tessellated structure in case of more

struct XtruVertex2 {
  Precision x;
  Precision y;
};

struct XtruSection {
  Vector3D<Precision> fOrigin; // Origin of the section
  Precision fScale;
};

class ExtrudedStruct {

  // template <typename U>
  // using vector_t = vecgeom::Vector<U>;
  template <typename U>
  using vector_t = vecgeom::Vector<U>;

public:
  bool fIsSxtru                  = false;     ///< Flag for sxtru representation
  bool fInitialized              = false;     ///< Flag for initialization
  Precision *fZPlanes            = nullptr;   ///< Z position of planes
  mutable Precision fCubicVolume = 0.;        ///< Cubic volume
  mutable Precision fSurfaceArea = 0.;        ///< Surface area
  PolygonalShell fSxtruHelper;                ///< Sxtru helper
  TessellatedStruct<3, Precision> fTslHelper; ///< Tessellated helper
#ifndef VECGEOM_ENABLE_CUDA
  bool fUseTslSections = false;                           ///< Use tessellated section helper
  vector_t<TessellatedSection<Precision> *> fTslSections; ///< Tessellated sections
#endif
  vector_t<XtruVertex2> fVertices; ///< Polygone vertices
  vector_t<XtruSection> fSections; ///< Vector of sections
  PlanarPolygon fPolygon;          ///< Planar polygon

public:
  /** @brief Dummy constructor */
  VECCORE_ATT_HOST_DEVICE
  ExtrudedStruct() {}

  /** @brief Constructor providing polygone vertices and sections */
  VECCORE_ATT_HOST_DEVICE
  ExtrudedStruct(int nvertices, XtruVertex2 const *vertices, int nsections, XtruSection const *sections)
  {
    Initialize(nvertices, vertices, nsections, sections);
  }

  // Constructor used during Specialization for nsections == 2
  VECCORE_ATT_HOST_DEVICE
  ExtrudedStruct(size_t nvertices, const Precision *x, const Precision *y, Precision zmin, Precision zmax)
  {
    XtruVertex2 *vertices = new XtruVertex2[nvertices];
    XtruSection *sections = new XtruSection[2];
    for (size_t i = 0; i < nvertices; ++i) {
      vertices[i].x = x[i];
      vertices[i].y = y[i];
    }

    sections[0].fScale = 1.;
    sections[0].fOrigin.Set(0., 0., zmin);

    sections[1].fScale = 1.;
    sections[1].fOrigin.Set(0., 0., zmax);

    Initialize(nvertices, vertices, 2, sections);
    delete[] vertices;
    delete[] sections;
  }

  VECGEOM_FORCE_INLINE
  VECCORE_ATT_HOST_DEVICE
  int FindZSegment(Precision const &pointZ) const
  {
    int index              = -1;
    Precision const *begin = fZPlanes;
    Precision const *end   = fZPlanes + fSections.size() + 1;
    while (begin < end - 1 && pointZ - kTolerance > *begin) {
      ++index;
      ++begin;
    }
    if (pointZ + kTolerance > *begin) return (index + 1);
    return index;
  }

  /** @brief Initialize based on vertices and sections */
  void Initialize(int nvertices, XtruVertex2 const *vertices, int nsections, XtruSection const *sections)
  {
    if (fInitialized) return;
    assert(nsections > 1 && nvertices > 2);
    fZPlanes         = new Precision[nsections];
    fZPlanes[0]      = sections[0].fOrigin.z();
    bool degenerated = false;
    for (size_t i = 1; i < (size_t)nsections; ++i) {
      fZPlanes[i] = sections[i].fOrigin.z();
      // Make sure sections are defined in increasing order
      assert(fZPlanes[i] >= fZPlanes[i - 1] && "Extruded sections not defined in increasing Z order");
      if (fZPlanes[i] - fZPlanes[i - 1] < kTolerance) degenerated = true;
    }
#ifndef VECGEOM_ENABLE_CUDA
    if (!degenerated) fUseTslSections = true;
#endif
    (void)degenerated; // silence the compiler
    // Check if this is an SXtru
    if (nsections == 2 && (sections[0].fOrigin - sections[1].fOrigin).Perp2() < kTolerance &&
        vecCore::math::Abs(sections[0].fScale - sections[1].fScale) < kTolerance)
      fIsSxtru = true;
    if (fIsSxtru) {
      // Put vertices in arrays
      Precision *x = new Precision[nvertices];
      Precision *y = new Precision[nvertices];
      for (size_t i = 0; i < (size_t)nvertices; ++i) {
        x[i] = sections[0].fOrigin.x() + sections[0].fScale * vertices[i].x;
        y[i] = sections[0].fOrigin.y() + sections[0].fScale * vertices[i].y;
      }
      fSxtruHelper.Init(nvertices, x, y, sections[0].fOrigin[2], sections[1].fOrigin[2]);
      delete[] x;
      delete[] y;
    }
    // Create the tessellated structure in all cases
    CreateTessellated(nvertices, vertices, nsections, sections);
    fInitialized = true;
  }

  /** @brief Construct facets based on vertices and sections */
  VECCORE_ATT_HOST_DEVICE
  void CreateTessellated(size_t nvertices, XtruVertex2 const *vertices, size_t nsections, XtruSection const *sections)
  {
    struct FacetInd {
      size_t ind1, ind2, ind3;
      FacetInd(int i1, int i2, int i3)
      {
        ind1 = i1;
        ind2 = i2;
        ind3 = i3;
      }
    };

    // Store sections
    for (size_t isect = 0; isect < nsections; ++isect)
      fSections.push_back(sections[isect]);

    // Create the polygon
    Precision *vx = new Precision[nvertices];
    Precision *vy = new Precision[nvertices];
    for (size_t i = 0; i < nvertices; ++i) {
      vx[i] = vertices[i].x;
      vy[i] = vertices[i].y;
    }
    fPolygon.Init(nvertices, vx, vy);
#ifndef VECGEOM_ENABLE_CUDA
    fUseTslSections &= fPolygon.IsConvex();
    if (fUseTslSections) {
      // Create tessellated sections
      fTslSections.reserve(nsections);
      for (size_t i = 0; i < nsections - 1; ++i) {
        fTslSections[i] =
            new TessellatedSection<Precision>(nvertices, sections[i].fOrigin.z(), sections[i + 1].fOrigin.z());
      }
    }
#endif
    // TRIANGULATE POLYGON

    VectorBase<FacetInd> facets(nvertices);
    // Fill a vector of vertex indices
    vector_t<size_t> vtx;
    for (size_t i = 0; i < nvertices; ++i)
      vtx.push_back(i);

    size_t i1 = 0;
    size_t i2 = 1;
    size_t i3 = 2;

    while (vtx.size() > 2) {
      // Find convex parts of the polygon (ears)
      size_t counter = 0;
      while (!IsConvexSide(vtx[i1], vtx[i2], vtx[i3])) {
        i1++;
        i2++;
        i3 = (i3 + 1) % vtx.size();
        counter++;
        assert(counter < nvertices && "Triangulation failed");
        (void)counter; // silence unused variable warnings in release builds
      }
      bool good = true;
      // Check if any of the remaining vertices are in the ear
      for (auto i : vtx) {
        if (i == vtx[i1] || i == vtx[i2] || i == vtx[i3]) continue;
        if (IsPointInside(vtx[i1], vtx[i2], vtx[i3], i)) {
          good = false;
          i1++;
          i2++;
          i3 = (i3 + 1) % vtx.size();
          break;
        }
      }

      if (good) {
        // Make triangle
        facets.push_back(FacetInd(vtx[i1], vtx[i2], vtx[i3]));
        // Remove the middle vertex of the ear and restart
        vtx.erase(vtx.begin() + i2);
        i1 = 0;
        i2 = 1;
        i3 = 2;
      }
    }
    // We have all index facets, create now the real facets
    // Bottom (normals pointing down)
    for (size_t i = 0; i < facets.size(); ++i) {
      i1 = facets[i].ind1;
      i2 = facets[i].ind2;
      i3 = facets[i].ind3;
      fTslHelper.AddTriangularFacet(VertexToSection(i1, 0), VertexToSection(i2, 0), VertexToSection(i3, 0));
    }
    // Sections
    for (size_t isect = 0; isect < nsections - 1; ++isect) {
      for (size_t i = 0; i < (size_t)nvertices; ++i) {
        size_t j = (i + 1) % nvertices;
        // Quadrilateral isect:(j, i)  isect+1: (i, j)
        fTslHelper.AddQuadrilateralFacet(VertexToSection(j, isect), VertexToSection(i, isect),
                                         VertexToSection(i, isect + 1), VertexToSection(j, isect + 1));
#ifndef VECGEOM_ENABLE_CUDA
        if (fUseTslSections)
          fTslSections[isect]->AddQuadrilateralFacet(VertexToSection(j, isect), VertexToSection(i, isect),
                                                     VertexToSection(i, isect + 1), VertexToSection(j, isect + 1));
#endif
      }
    }
    // Top (normals pointing up)
    for (size_t i = 0; i < facets.size(); ++i) {
      i1 = facets[i].ind1;
      i2 = facets[i].ind2;
      i3 = facets[i].ind3;
      fTslHelper.AddTriangularFacet(VertexToSection(i1, nsections - 1), VertexToSection(i3, nsections - 1),
                                    VertexToSection(i2, nsections - 1));
    }
    // Now close the tessellated structure
    fTslHelper.Close();
  }

  /** @brief Get the number of sections */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  size_t GetNSections() const { return fSections.size(); }

  /** @brief Get the number of planes */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  size_t GetNSegments() const { return (fSections.size() - 1); }

  /** @brief Get section i */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  XtruSection GetSection(int i) const { return fSections[i]; }

  /** @brief Get the number of vertices */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  size_t GetNVertices() const { return fPolygon.GetNVertices(); }

  /** @brief Get the polygone vertex i */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  void GetVertex(int i, Precision &x, Precision &y) const
  {
    x = fPolygon.GetVertices().x()[i];
    y = fPolygon.GetVertices().y()[i];
  }

  /** Return true if i is on the line through i1, i2 */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  bool IsSameLine(size_t i, size_t i1, size_t i2) const
  {
    const Precision *x = fPolygon.GetVertices().x();
    const Precision *y = fPolygon.GetVertices().y();
    if (x[i1] == x[i2]) return std::fabs(x[i] - x[i1]) < kTolerance * 0.5;

    Precision slope = (y[i2] - y[i1]) / (x[i2] - x[i1]);
    Precision predy = y[i1] + slope * (x[i] - x[i1]);
    Precision dy    = y[i] - predy;

    // Check perpendicular distance vs tolerance 'directly'
    const Precision tol = 0.5 * kTolerance;
    bool squareComp     = (dy * dy < (1 + slope * slope) * tol * tol);
    return squareComp;
  }

  /** @brief Return true if point i is on the line through i1, i2 and lies between i1 and i2 */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  bool IsSameLineSegment(size_t i, size_t i1, size_t i2) const
  {
    const Precision *x = fPolygon.GetVertices().x();
    const Precision *y = fPolygon.GetVertices().y();
    if (x[i] < std::min(x[i1], x[i2]) - kTolerance * 0.5 || x[i] > std::max(x[i1], x[i2]) + kTolerance * 0.5 ||
        y[i] < std::min(y[i1], y[i2]) - kTolerance * 0.5 || y[i] > std::max(y[i1], y[i2]) + kTolerance * 0.5)
      return false;

    return IsSameLine(i, i1, i2);
  }

  /** @brief Return true if i and j are on the same side of the line through i1, i2 */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  bool IsSameSide(size_t i, size_t j, size_t i1, size_t i2) const
  {
    const Precision *x = fPolygon.GetVertices().x();
    const Precision *y = fPolygon.GetVertices().y();

    return ((x[i] - x[i1]) * (y[i2] - y[i1]) - (x[i2] - x[i1]) * (y[i] - y[i1])) *
               ((x[j] - x[i1]) * (y[i2] - y[i1]) - (x[i2] - x[i1]) * (y[j] - y[i1])) >
           0;
  }

  /** Return true if i is inside of triangle (i1, i2, i3) or on its edges, else returns false */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  bool IsPointInside(size_t i1, size_t i2, size_t i3, size_t i) const
  {
    const Precision *x = fPolygon.GetVertices().x();
    const Precision *y = fPolygon.GetVertices().y();

    // Check extent first
    if ((x[i] < x[i1] && x[i] < x[i2] && x[i] < x[i3]) || (x[i] > x[i1] && x[i] > x[i2] && x[i] > x[i3]) ||
        (y[i] < y[i1] && y[i] < y[i2] && y[i] < y[i3]) || (y[i] > y[i1] && y[i] > y[i2] && y[i] > y[i3]))
      return false;

    bool inside = IsSameSide(i, i1, i2, i3) && IsSameSide(i, i2, i1, i3) && IsSameSide(i, i3, i1, i2);

    bool onEdge = IsSameLineSegment(i, i1, i2) || IsSameLineSegment(i, i2, i3) || IsSameLineSegment(i, i3, i1);

    return inside || onEdge;
  }

  /** @brief Check if the polygone segments (i0, i1) and (i1, i2) make a convex side */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  bool IsConvexSide(size_t i0, size_t i1, size_t i2)
  {
    const Precision *x = fPolygon.GetVertices().x();
    const Precision *y = fPolygon.GetVertices().y();
    Precision cross    = (x[i1] - x[i0]) * (y[i2] - y[i1]) - (x[i2] - x[i1]) * (y[i1] - y[i0]);
    return cross < 0.;
  }

  /** @brief Returns convexity of polygon */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  bool IsConvexPolygon() const { return fPolygon.IsConvex(); }

  /** @brief Returns the coordinates for a given vertex index at a given section */
  VECCORE_ATT_HOST_DEVICE
  VECGEOM_FORCE_INLINE
  Vector3D<Precision> VertexToSection(size_t ivert, size_t isect) const
  {
    const Precision *x = fPolygon.GetVertices().x();
    const Precision *y = fPolygon.GetVertices().y();
    Vector3D<Precision> vert(fSections[isect].fOrigin[0] + fSections[isect].fScale * x[ivert],
                             fSections[isect].fOrigin[1] + fSections[isect].fScale * y[ivert],
                             fSections[isect].fOrigin[2]);
    return vert;
  }
};

} // namespace VECGEOM_IMPL_NAMESPACE
} // namespace vecgeom

#endif