Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TGeoTessellated.cxx
Go to the documentation of this file.
1// @(#)root/geom:$Id$// Author: Andrei Gheata 24/10/01
2
3// Contains() and DistFromOutside/Out() implemented by Mihaela Gheata
4// 2026-01: Revision to use BVH for navigation functions by Sandro Wenzel
5
6/*************************************************************************
7 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
8 * All rights reserved. *
9 * *
10 * For the licensing terms see $ROOTSYS/LICENSE. *
11 * For the list of contributors see $ROOTSYS/README/CREDITS. *
12 *************************************************************************/
13
14/** \class TGeoTessellated
15\ingroup Geometry_classes
16
17Tessellated solid class. It is composed by a set of planar faces having triangular or
18quadrilateral shape. The class does not provide navigation functionality, it just wraps the data
19for the composing faces.
20*/
21
22#include <iostream>
23#include <fstream>
24#include <sstream>
25
26#include "TGeoManager.h"
27#include "TGeoMatrix.h"
28#include "TGeoVolume.h"
29#include "TVirtualGeoPainter.h"
30#include "TGeoTessellated.h"
31#include "TBuffer3D.h"
32#include "TBuffer3DTypes.h"
33#include "TMath.h"
34#include "TBuffer.h"
35
36#include <array>
37#include <vector>
38
39// include the Third-party BVH headers
40#include <bvh2_third_party.h>
41// some kernels on top of BVH
42#include <bvh2_extra_kernels.h>
43
44#include <cmath>
45#include <limits>
46
48
50
51////////////////////////////////////////////////////////////////////////////////
52/// Compact consecutive equal vertices
53
55{
56 // Compact the common vertices and return new facet
57 if (nvertices < 2)
58 return nvertices;
59 int nvert = nvertices;
60 int i = 0;
61 while (i < nvert) {
62 if (vert[(i + 1) % nvert] == vert[i]) {
63 // shift last vertices left by one element
64 for (int j = i + 2; j < nvert; ++j)
65 vert[j - 1] = vert[j];
66 nvert--;
67 }
68 i++;
69 }
70 return nvert;
71}
72
73////////////////////////////////////////////////////////////////////////////////
74/// Check if a connected neighbour facet has compatible normal
75
76bool TGeoFacet::IsNeighbour(const TGeoFacet &other, bool &flip) const
77{
78
79 // Find a connecting segment
80 bool neighbour = false;
81 int line1[2], line2[2];
82 int npoints = 0;
83 for (int i = 0; i < fNvert; ++i) {
84 auto ivert = fIvert[i];
85 // Check if the other facet has the same vertex
86 for (int j = 0; j < other.GetNvert(); ++j) {
87 if (ivert == other[j]) {
88 line1[npoints] = i;
89 line2[npoints] = j;
90 if (++npoints == 2) {
91 neighbour = true;
92 bool order1 = line1[1] == line1[0] + 1;
93 bool order2 = line2[1] == (line2[0] + 1) % other.GetNvert();
94 flip = (order1 == order2);
95 return neighbour;
96 }
97 }
98 }
99 }
100 return neighbour;
101}
102
103////////////////////////////////////////////////////////////////////////////////
104/// Constructor. In case nfacets is zero, it is user's responsibility to
105/// call CloseShape once all faces are defined.
106
108{
110 if (nfacets)
111 fFacets.reserve(nfacets);
112}
113
114////////////////////////////////////////////////////////////////////////////////
115/// Constructor providing directly the array of vertices. Facets have to be added
116/// providing vertex indices rather than coordinates.
117
118TGeoTessellated::TGeoTessellated(const char *name, const std::vector<Vertex_t> &vertices) : TGeoBBox(name, 0, 0, 0)
119{
120 fVertices = vertices;
121 fNvert = fVertices.size();
122}
123
124////////////////////////////////////////////////////////////////////////////////
125/// Add a vertex checking for duplicates, returning the vertex index
126
128{
129 constexpr double tolerance = 1.e-10;
130 auto vertexHash = [&](Vertex_t const &vertex) {
131 // Compute hash for the vertex
132 long hash = 0;
133 // helper function to generate hash from integer numbers
134 auto hash_combine = [](long seed, const long value) {
135 return seed ^ (std::hash<long>{}(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2));
136 };
137 for (int i = 0; i < 3; i++) {
138 // use tolerance to generate int with the desired precision from a real number for hashing
139 hash = hash_combine(hash, std::roundl(vertex[i] / tolerance));
140 }
141 return hash;
142 };
143
144 auto hash = vertexHash(vert);
145 bool isAdded = false;
146 int ivert = -1;
147 // Get the compatible vertices
148 auto range = fVerticesMap.equal_range(hash);
149 for (auto it = range.first; it != range.second; ++it) {
150 ivert = it->second;
151 if (fVertices[ivert] == vert) {
152 isAdded = true;
153 break;
154 }
155 }
156 if (!isAdded) {
157 ivert = fVertices.size();
158 fVertices.push_back(vert);
159 fVerticesMap.insert(std::make_pair(hash, ivert));
160 }
161 return ivert;
162}
163
164////////////////////////////////////////////////////////////////////////////////
165/// Adding a triangular facet from vertex positions in absolute coordinates
166
168{
169 if (fDefined) {
170 Error("AddFacet", "Shape %s already fully defined. Not adding", GetName());
171 return false;
172 }
173
174 Vertex_t vert[3];
175 vert[0] = pt0;
176 vert[1] = pt1;
177 vert[2] = pt2;
179 if (nvert < 3) {
180 Error("AddFacet", "Triangular facet at index %d degenerated. Not adding.", GetNfacets());
181 return false;
182 }
183 int ind[3];
184 for (auto i = 0; i < 3; ++i)
185 ind[i] = AddVertex(vert[i]);
186 fNseg += 3;
187 fFacets.emplace_back(ind[0], ind[1], ind[2]);
188
189 return true;
190}
191
192////////////////////////////////////////////////////////////////////////////////
193/// Adding a triangular facet from indices of vertices
194
196{
197 if (fDefined) {
198 Error("AddFacet", "Shape %s already fully defined. Not adding", GetName());
199 return false;
200 }
201 if (fVertices.empty()) {
202 Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName());
203 return false;
204 }
205
206 fNseg += 3;
207 fFacets.emplace_back(i0, i1, i2);
208 return true;
209}
210
211////////////////////////////////////////////////////////////////////////////////
212/// Adding a quadrilateral facet from vertex positions in absolute coordinates
213
215{
216 if (fDefined) {
217 Error("AddFacet", "Shape %s already fully defined. Not adding", GetName());
218 return false;
219 }
220 Vertex_t vert[4];
221 vert[0] = pt0;
222 vert[1] = pt1;
223 vert[2] = pt2;
224 vert[3] = pt3;
226 if (nvert < 3) {
227 Error("AddFacet", "Quadrilateral facet at index %d degenerated. Not adding.", GetNfacets());
228 return false;
229 }
230
231 int ind[4];
232 for (auto i = 0; i < nvert; ++i)
233 ind[i] = AddVertex(vert[i]);
234 fNseg += nvert;
235 if (nvert == 3)
236 fFacets.emplace_back(ind[0], ind[1], ind[2]);
237 else
238 fFacets.emplace_back(ind[0], ind[1], ind[2], ind[3]);
239
240 if (fNfacets > 0 && GetNfacets() == fNfacets)
241 CloseShape(false);
242 return true;
243}
244
245////////////////////////////////////////////////////////////////////////////////
246/// Adding a quadrilateral facet from indices of vertices
247
248bool TGeoTessellated::AddFacet(int i0, int i1, int i2, int i3)
249{
250 if (fDefined) {
251 Error("AddFacet", "Shape %s already fully defined. Not adding", GetName());
252 return false;
253 }
254 if (fVertices.empty()) {
255 Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName());
256 return false;
257 }
258
259 fNseg += 4;
260 fFacets.emplace_back(i0, i1, i2, i3);
261 return true;
262}
263
264////////////////////////////////////////////////////////////////////////////////
265/// Compute normal for a given facet
266
268{
269 // Compute normal using non-zero segments
270 constexpr double kTolerance = 1.e-20;
271 auto const &facet = fFacets[ifacet];
272 int nvert = facet.GetNvert();
273 degenerated = true;
275 for (int i = 0; i < nvert - 1; ++i) {
276 Vertex_t e1 = fVertices[facet[i + 1]] - fVertices[facet[i]];
277 if (e1.Mag2() < kTolerance)
278 continue;
279 for (int j = i + 1; j < nvert; ++j) {
280 Vertex_t e2 = fVertices[facet[(j + 1) % nvert]] - fVertices[facet[j]];
281 if (e2.Mag2() < kTolerance)
282 continue;
284 // e1 and e2 may be colinear
285 if (normal.Mag2() < kTolerance)
286 continue;
287 normal.Normalize();
288 degenerated = false;
289 break;
290 }
291 if (!degenerated)
292 break;
293 }
294 return normal;
295}
296
297////////////////////////////////////////////////////////////////////////////////
298/// Check validity of facet
299
301{
302 constexpr double kTolerance = 1.e-10;
303 auto const &facet = fFacets[ifacet];
304 int nvert = facet.GetNvert();
305 bool degenerated = true;
307 if (degenerated) {
308 std::cout << "Facet: " << ifacet << " is degenerated\n";
309 return false;
310 }
311
312 // Compute surface area
313 double surfaceArea = 0.;
314 for (int i = 1; i < nvert - 1; ++i) {
316 Vertex_t e2 = fVertices[facet[i + 1]] - fVertices[facet[0]];
317 surfaceArea += 0.5 * Vertex_t::Cross(e1, e2).Mag();
318 }
319 if (surfaceArea < kTolerance) {
320 std::cout << "Facet: " << ifacet << " has zero surface area\n";
321 return false;
322 }
323
324 return true;
325}
326
327////////////////////////////////////////////////////////////////////////////////
328/// Close the shape: calculate bounding box and compact vertices
329
330void TGeoTessellated::CloseShape(bool check, bool fixFlipped, bool verbose)
331{
332 const bool initialized = fIsClosed && fBVH;
333 if (initialized && !check) {
334 return;
335 }
336
337 if (!initialized) {
338 // Compute bounding box
339 fDefined = true;
340 fNvert = fVertices.size();
341 fNfacets = fFacets.size();
342 ComputeBBox();
343
344 BuildBVH();
345 fIsClosed = true;
346
347 // Cleanup the vertex map
348 std::multimap<long, int>().swap(fVerticesMap);
349 }
350
351 if (fVertices.size() > 0) {
352 if (check) {
353 // Check facets
354 for (auto i = 0; i < fNfacets; ++i)
355 FacetCheck(i);
356
358 }
359
360 if (fOutwardNormals.size() != fFacets.size())
362 }
363}
364
365////////////////////////////////////////////////////////////////////////////////
366/// Check closure of the solid and check/fix flipped normals
367
369{
370 int *nn = new int[fNfacets];
371 bool *flipped = new bool[fNfacets];
372 bool hasorphans = false;
373 bool hasflipped = false;
374 for (int i = 0; i < fNfacets; ++i) {
375 nn[i] = 0;
376 flipped[i] = false;
377 }
378
379 for (int icrt = 0; icrt < fNfacets; ++icrt) {
380 // all neighbours checked?
381 if (nn[icrt] >= fFacets[icrt].GetNvert())
382 continue;
383 for (int i = icrt + 1; i < fNfacets; ++i) {
384 bool isneighbour = fFacets[icrt].IsNeighbour(fFacets[i], flipped[i]);
385 if (isneighbour) {
386 if (flipped[icrt])
387 flipped[i] = !flipped[i];
388 if (flipped[i])
389 hasflipped = true;
390 nn[icrt]++;
391 nn[i]++;
392 if (nn[icrt] == fFacets[icrt].GetNvert())
393 break;
394 }
395 }
396 if (nn[icrt] < fFacets[icrt].GetNvert())
397 hasorphans = true;
398 }
399
400 if (hasorphans && verbose) {
401 Error("Check", "Tessellated solid %s has following not fully connected facets:", GetName());
402 for (int icrt = 0; icrt < fNfacets; ++icrt) {
403 if (nn[icrt] < fFacets[icrt].GetNvert())
404 std::cout << icrt << " (" << fFacets[icrt].GetNvert() << " edges, " << nn[icrt] << " neighbours)\n";
405 }
406 }
408 int nfixed = 0;
409 if (hasflipped) {
410 if (verbose)
411 Warning("Check", "Tessellated solid %s has following facets with flipped normals:", GetName());
412 for (int icrt = 0; icrt < fNfacets; ++icrt) {
413 if (flipped[icrt]) {
414 if (verbose)
415 std::cout << icrt << "\n";
416 if (fixFlipped) {
417 fFacets[icrt].Flip();
418 nfixed++;
419 }
420 }
421 }
422 if (nfixed && verbose)
423 Info("Check", "Automatically flipped %d facets to match first defined facet", nfixed);
424 if (nfixed && !fOutwardNormals.empty())
426 }
427 delete[] nn;
428 delete[] flipped;
429
430 return !hasorphans;
431}
432
433////////////////////////////////////////////////////////////////////////////////
434/// Compute bounding box
435
437{
438 const double kBig = TGeoShape::Big();
439 double vmin[3] = {kBig, kBig, kBig};
440 double vmax[3] = {-kBig, -kBig, -kBig};
441 for (const auto &facet : fFacets) {
442 for (int i = 0; i < facet.GetNvert(); ++i) {
443 for (int j = 0; j < 3; ++j) {
444 vmin[j] = TMath::Min(vmin[j], fVertices[facet[i]].operator[](j));
445 vmax[j] = TMath::Max(vmax[j], fVertices[facet[i]].operator[](j));
446 }
447 }
448 }
449 fDX = 0.5 * (vmax[0] - vmin[0]);
450 fDY = 0.5 * (vmax[1] - vmin[1]);
451 fDZ = 0.5 * (vmax[2] - vmin[2]);
452 for (int i = 0; i < 3; ++i)
453 fOrigin[i] = 0.5 * (vmax[i] + vmin[i]);
454}
455
456////////////////////////////////////////////////////////////////////////////////
457/// Returns numbers of vertices, segments and polygons composing the shape mesh.
458
460{
461 nvert = fNvert;
462 nsegs = fNseg;
463 npols = GetNfacets();
464}
465
466////////////////////////////////////////////////////////////////////////////////
467/// Creates a TBuffer3D describing *this* shape.
468/// Coordinates are in local reference frame.
469
471{
472 const int nvert = fNvert;
473 const int nsegs = fNseg;
474 const int npols = GetNfacets();
476 if (buff) {
477 SetPoints(buff->fPnts);
479 }
480 return buff;
481}
482
483////////////////////////////////////////////////////////////////////////////////
484/// Prints basic info
485
487{
488 std::cout << "=== Tessellated shape " << GetName() << " having " << GetNvertices() << " vertices and "
489 << GetNfacets() << " facets\n";
490}
491
492////////////////////////////////////////////////////////////////////////////////
493/// Fills TBuffer3D structure for segments and polygons.
494
496{
497 const int c = GetBasicColor();
498 int *segs = buff.fSegs;
499 int *pols = buff.fPols;
500
501 int indseg = 0; // segment internal data index
502 int indpol = 0; // polygon internal data index
503 int sind = 0; // segment index
504 for (const auto &facet : fFacets) {
505 auto nvert = facet.GetNvert();
506 pols[indpol++] = c;
507 pols[indpol++] = nvert;
508 for (auto j = 0; j < nvert; ++j) {
509 int k = (j + 1) % nvert;
510 // segment made by next consecutive points
511 segs[indseg++] = c;
512 segs[indseg++] = facet[j];
513 segs[indseg++] = facet[k];
514 // add segment to current polygon and increment segment index
515 pols[indpol + nvert - j - 1] = sind++;
516 }
517 indpol += nvert;
518 }
519}
520
521////////////////////////////////////////////////////////////////////////////////
522/// Fill tessellated points to an array.
523
525{
526 int ind = 0;
527 for (const auto &vertex : fVertices) {
528 vertex.CopyTo(&points[ind]);
529 ind += 3;
530 }
531}
532
533////////////////////////////////////////////////////////////////////////////////
534/// Fill tessellated points in float.
535
537{
538 int ind = 0;
539 for (const auto &vertex : fVertices) {
540 points[ind++] = vertex.x();
541 points[ind++] = vertex.y();
542 points[ind++] = vertex.z();
543 }
544}
545
546////////////////////////////////////////////////////////////////////////////////
547/// Resize the shape by scaling vertices within maxsize and center to origin
548
550{
551 using Vector3_t = Vertex_t;
552
553 if (!fDefined) {
554 Error("ResizeCenter", "Not all faces are defined");
555 return;
556 }
558 double maxedge = TMath::Max(TMath::Max(fDX, fDY), fDZ);
559 double scale = maxsize / maxedge;
560 constexpr double kTol = 1e-12;
561 const bool modified = (std::abs(scale - 1.0) > kTol) || (std::abs(origin[0]) > kTol) ||
562 (std::abs(origin[1]) > kTol) || (std::abs(origin[2]) > kTol);
563 for (size_t i = 0; i < fVertices.size(); ++i) {
564 fVertices[i] = scale * (fVertices[i] - origin);
565 }
566 fOrigin[0] = fOrigin[1] = fOrigin[2] = 0;
567 fDX *= scale;
568 fDY *= scale;
569 fDZ *= scale;
570 if (modified) {
571 BuildBVH();
573 }
574}
575
576////////////////////////////////////////////////////////////////////////////////
577/// Fills a static 3D buffer and returns a reference.
578
580{
581 static TBuffer3D buffer(TBuffer3DTypes::kGeneric);
582
584
585 const int nvert = fNvert;
586 const int nsegs = fNseg;
587 const int npols = GetNfacets();
588
590 if (buffer.SetRawSizes(nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols)) {
592 }
593 }
595 SetPoints(buffer.fPnts);
596 if (!buffer.fLocalFrame) {
597 TransformPoints(buffer.fPnts, buffer.NbPnts());
598 }
599
600 SetSegsAndPols(buffer);
602 }
603
604 return buffer;
605}
606
607////////////////////////////////////////////////////////////////////////////////
608/// Reads a single tessellated solid from an .obj file.
609
611{
612 using std::vector, std::string, std::ifstream, std::stringstream, std::endl;
613
614 vector<Vertex_t> vertices;
616
617 struct FacetInd_t {
618 int i0 = -1;
619 int i1 = -1;
620 int i2 = -1;
621 int i3 = -1;
622 int nvert = 0;
623 FacetInd_t(int a, int b, int c)
624 {
625 i0 = a;
626 i1 = b;
627 i2 = c;
628 nvert = 3;
629 };
630 FacetInd_t(int a, int b, int c, int d)
631 {
632 i0 = a;
633 i1 = b;
634 i2 = c;
635 i3 = d;
636 nvert = 4;
637 };
638 };
639
641 // List of geometric vertices, with (x, y, z [,w]) coordinates, w is optional and defaults to 1.0.
642 // struct vtx_t { double x = 0; double y = 0; double z = 0; double w = 1; };
643
644 // Texture coordinates in u, [,v ,w]) coordinates, these will vary between 0 and 1. v, w are optional and default to
645 // 0.
646 // struct tex_t { double u; double v; double w; };
647
648 // List of vertex normals in (x,y,z) form; normals might not be unit vectors.
649 // struct vn_t { double x; double y; double z; };
650
651 // Parameter space vertices in ( u [,v] [,w] ) form; free form geometry statement
652 // struct vp_t { double u; double v; double w; };
653
654 // Faces are defined using lists of vertex, texture and normal indices which start at 1.
655 // Polygons such as quadrilaterals can be defined by using more than three vertex/texture/normal indices.
656 // f v1//vn1 v2//vn2 v3//vn3 ...
657
658 // Records starting with the letter "l" specify the order of the vertices which build a polyline.
659 // l v1 v2 v3 v4 v5 v6 ...
660
661 string line;
662 int ind[4] = {0};
663 ifstream file(objfile);
664 if (!file.is_open()) {
665 ::Error("TGeoTessellated::ImportFromObjFormat", "Unable to open %s", objfile);
666 return nullptr;
667 }
668
669 while (getline(file, line)) {
670 stringstream ss(line);
671 string tag;
672
673 // We ignore everything which is not a vertex or a face
674 if (line.rfind('v', 0) == 0 && line.rfind("vt", 0) != 0 && line.rfind("vn", 0) != 0 && line.rfind("vn", 0) != 0) {
675 // Decode the vertex
676 double pos[4] = {0, 0, 0, 1};
677 ss >> tag >> pos[0] >> pos[1] >> pos[2] >> pos[3];
678 vertices.emplace_back(pos[0] * pos[3], pos[1] * pos[3], pos[2] * pos[3]);
679 }
680
681 else if (line.rfind('f', 0) == 0) {
682 // Decode the face
683 ss >> tag;
684 string word;
685 sfacets.clear();
686 while (ss >> word)
687 sfacets.push_back(word);
688 if (sfacets.size() > 4 || sfacets.size() < 3) {
689 ::Error("TGeoTessellated::ImportFromObjFormat", "Detected face having unsupported %zu vertices",
690 sfacets.size());
691 return nullptr;
692 }
693 int nvert = 0;
694 for (auto &sword : sfacets) {
695 stringstream ssword(sword);
696 string token;
697 getline(ssword, token, '/'); // just need the vertex index, which is the first token
698 // Convert string token to integer
699
700 ind[nvert++] = stoi(token) - 1;
701 if (ind[nvert - 1] < 0) {
702 ::Error("TGeoTessellated::ImportFromObjFormat", "Unsupported relative vertex index definition in %s",
703 objfile);
704 return nullptr;
705 }
706 }
707 if (nvert == 3)
708 facets.emplace_back(ind[0], ind[1], ind[2]);
709 else
710 facets.emplace_back(ind[0], ind[1], ind[2], ind[3]);
711 }
712 }
713
714 int nvertices = (int)vertices.size();
715 int nfacets = (int)facets.size();
716 if (nfacets < 3) {
717 ::Error("TGeoTessellated::ImportFromObjFormat", "Not enough faces detected in %s", objfile);
718 return nullptr;
719 }
720
721 string sobjfile(objfile);
722 if (verbose)
723 std::cout << "Read " << nvertices << " vertices and " << nfacets << " facets from " << sobjfile << endl;
724
725 auto tsl = new TGeoTessellated(sobjfile.erase(sobjfile.find_last_of('.')).c_str(), vertices);
726
727 for (int i = 0; i < nfacets; ++i) {
728 auto facet = facets[i];
729 if (facet.nvert == 3)
730 tsl->AddFacet(facet.i0, facet.i1, facet.i2);
731 else
732 tsl->AddFacet(facet.i0, facet.i1, facet.i2, facet.i3);
733 }
734 tsl->CloseShape(check, true, verbose);
735 tsl->Print();
736 return tsl;
737}
738
739// implementation of some geometry helper functions in anonymous namespace
740namespace {
741
743// The classic Moeller-Trumbore ray triangle-intersection kernel:
744// - Compute triangle edges e1, e2
745// - Compute determinant det
746// - Reject parallel rays
747// - Compute barycentric coordinates u, v
748// - Compute ray parameter t
749double rayTriangle(const Vertex_t &orig, const Vertex_t &dir, const Vertex_t &v0, const Vertex_t &v1,
750 const Vertex_t &v2, double rayEPS = 1e-8)
751{
752 constexpr double EPS = 1e-8;
753 const double INF = std::numeric_limits<double>::infinity();
754 Vertex_t e1{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]};
755 Vertex_t e2{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]};
756 auto p = Vertex_t::Cross(dir, e2);
757 auto det = e1.Dot(p);
758 if (std::abs(det) <= EPS) {
759 return INF;
760 }
761
762 Vertex_t tvec{orig[0] - v0[0], orig[1] - v0[1], orig[2] - v0[2]};
763 auto invDet = 1.0 / det;
764 auto u = tvec.Dot(p) * invDet;
765 if (u < 0.0 || u > 1.0) {
766 return INF;
767 }
768 auto q = Vertex_t::Cross(tvec, e1);
769 auto v = dir.Dot(q) * invDet;
770 if (v < 0.0 || u + v > 1.0) {
771 return INF;
772 }
773 auto t = e2.Dot(q) * invDet;
774 return (t > rayEPS) ? t : INF;
775}
776
777inline double rayFacet(const Vertex_t &orig, const Vertex_t &dir, const TGeoFacet &facet,
778 const std::vector<Vertex_t> &vertices, double rayEPS = 1e-8)
779{
780 // Keep the stored facet topology intact and triangulate quads only for geometric queries.
781 const auto &v0 = vertices[facet[0]];
782 const auto &v1 = vertices[facet[1]];
783 const auto &v2 = vertices[facet[2]];
784 auto t = rayTriangle(orig, dir, v0, v1, v2, rayEPS);
785 if (facet.GetNvert() == 3)
786 return t;
787 const auto &v3 = vertices[facet[3]];
788 auto t2 = rayTriangle(orig, dir, v0, v2, v3, rayEPS);
789 return std::min(t, t2);
790}
791
792inline bool rayFacetHit(const Vertex_t &orig, const Vertex_t &dir, const TGeoFacet &facet,
793 const std::vector<Vertex_t> &vertices, double rayEPS = 1e-8)
794{
795 // Contains/parity checks only need a boolean hit, so keep the finite-distance test in one place.
796 return rayFacet(orig, dir, facet, vertices, rayEPS) != std::numeric_limits<double>::infinity();
797}
798
799template <typename T = float>
800struct Vec3f {
801 T x, y, z;
802 Vec3f(T x_, T y_, T z_) : x(x_), y(y_), z(z_){};
803};
804
805template <typename T>
806inline Vec3f<T> operator-(const Vec3f<T> &a, const Vec3f<T> &b)
807{
808 return {a.x - b.x, a.y - b.y, a.z - b.z};
809}
810
811template <typename T>
812inline Vec3f<T> cross(const Vec3f<T> &a, const Vec3f<T> &b)
813{
814 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
815}
816
817template <typename T>
818inline T dot(const Vec3f<T> &a, const Vec3f<T> &b)
819{
820 return a.x * b.x + a.y * b.y + a.z * b.z;
821}
822
823// Kernel to get closest/shortest distance between a point and a triangl (a,b,c).
824// Performed by default in float since Safety can be approximate.
825// Project point onto triangle plane
826// If projection lies inside → distance to plane
827// Otherwise compute min distance to the three edges
828// Return squared distance
829template <typename T = float>
830T pointTriangleDistSq(const Vec3f<T> &p, const Vec3f<T> &a, const Vec3f<T> &b, const Vec3f<T> &c)
831{
832 // Edges
833 Vec3f<T> ab = b - a;
834 Vec3f<T> ac = c - a;
835 Vec3f<T> ap = p - a;
836
837 auto d1 = dot(ab, ap);
838 auto d2 = dot(ac, ap);
839 if (d1 <= T(0.0) && d2 <= T(0.0)) {
840 return dot(ap, ap); // barycentric (1,0,0)
841 }
842
843 Vec3f<T> bp = p - b;
844 auto d3 = dot(ab, bp);
845 auto d4 = dot(ac, bp);
846 if (d3 >= T(0.0) && d4 <= d3) {
847 return dot(bp, bp); // (0,1,0)
848 }
849
850 T vc = d1 * d4 - d3 * d2;
851 if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) {
852 T v = d1 / (d1 - d3);
853 Vec3f<T> proj = {a.x + v * ab.x, a.y + v * ab.y, a.z + v * ab.z};
854 Vec3f<T> d = p - proj;
855 return dot(d, d); // edge AB
856 }
857
858 Vec3f<T> cp = p - c;
859 T d5 = dot(ab, cp);
860 T d6 = dot(ac, cp);
861 if (d6 >= T(0.0f) && d5 <= d6) {
862 return dot(cp, cp); // (0,0,1)
863 }
864
865 T vb = d5 * d2 - d1 * d6;
866 if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) {
867 T w = d2 / (d2 - d6);
868 Vec3f<T> proj = {a.x + w * ac.x, a.y + w * ac.y, a.z + w * ac.z};
869 Vec3f<T> d = p - proj;
870 return dot(d, d); // edge AC
871 }
872
873 T va = d3 * d6 - d5 * d4;
874 if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) {
875 T w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
876 Vec3f<T> proj = {b.x + w * (c.x - b.x), b.y + w * (c.y - b.y), b.z + w * (c.z - b.z)};
877 Vec3f<T> d = p - proj;
878 return dot(d, d); // edge BC
879 }
880
881 // Inside face region
882 T denom = T(1.0f) / (va + vb + vc);
883 T v = vb * denom;
884 T w = vc * denom;
885
886 Vec3f<T> proj = {a.x + ab.x * v + ac.x * w, a.y + ab.y * v + ac.y * w, a.z + ab.z * v + ac.z * w};
887
888 Vec3f<T> d = p - proj;
889 return dot(d, d);
890}
891
892template <typename T = float>
893T pointFacetDistSq(const Vec3f<T> &p, const TGeoFacet &facet, const std::vector<Vertex_t> &vertices)
894{
895 // Safety uses the same on-the-fly split as ray queries so triangles and quads stay consistent.
896 const auto &v0 = vertices[facet[0]];
897 const auto &v1 = vertices[facet[1]];
898 const auto &v2 = vertices[facet[2]];
899 auto d = pointTriangleDistSq(p, Vec3f<T>(v0[0], v0[1], v0[2]), Vec3f<T>(v1[0], v1[1], v1[2]),
900 Vec3f<T>(v2[0], v2[1], v2[2]));
901 if (facet.GetNvert() == 3)
902 return d;
903 const auto &v3 = vertices[facet[3]];
904 auto d2 = pointTriangleDistSq(p, Vec3f<T>(v0[0], v0[1], v0[2]), Vec3f<T>(v2[0], v2[1], v2[2]),
905 Vec3f<T>(v3[0], v3[1], v3[2]));
906 return std::min(d, d2);
907}
908
909} // end anonymous namespace
910
911////////////////////////////////////////////////////////////////////////////////
912/// DistFromOutside
913
915 Double_t * /*safe*/) const
916{
917 // use the BVH intersector in combination with leaf ray-triangle testing
918 double local_step = Big(); // we need this otherwise the lambda get's confused
919
920 using Scalar = float;
922 using Node = bvh::v2::Node<Scalar, 3>;
923 using Bvh = bvh::v2::Bvh<Node>;
924 using Ray = bvh::v2::Ray<Scalar, 3>;
925
926 // let's fetch the bvh
927 auto mybvh = (Bvh *)fBVH;
928 if (!mybvh) {
929 assert(false);
930 return -1.;
931 }
932
933 auto truncate_roundup = [](double orig) {
934 float epsilon = std::numeric_limits<float>::epsilon() * std::fabs(orig);
935 // Add the bias to x before assigning it to y
936 return static_cast<float>(orig + epsilon);
937 };
938
939 // let's do very quick checks against the top node
940 const auto topnode_bbox = mybvh->get_root().get_bbox();
941 if ((-point[0] + topnode_bbox.min[0]) > stepmax) {
942 return Big();
943 }
944 if ((-point[1] + topnode_bbox.min[1]) > stepmax) {
945 return Big();
946 }
947 if ((-point[2] + topnode_bbox.min[2]) > stepmax) {
948 return Big();
949 }
950 if ((point[0] - topnode_bbox.max[0]) > stepmax) {
951 return Big();
952 }
953 if ((point[1] - topnode_bbox.max[1]) > stepmax) {
954 return Big();
955 }
956 if ((point[2] - topnode_bbox.max[2]) > stepmax) {
957 return Big();
958 }
959
960 // the ray used for bvh interaction
961 Ray ray(Vec3(point[0], point[1], point[2]), // origin
962 Vec3(dir[0], dir[1], dir[2]), // direction
963 0.0f, // minimum distance (could give stepmax ?)
965
966 static constexpr bool use_robust_traversal = true;
967
968 Vertex_t dir_v{dir[0], dir[1], dir[2]};
969 // Traverse the BVH and apply concrete object intersection in BVH leafs
971 mybvh->intersect<false, use_robust_traversal>(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) {
972 for (size_t prim_id = begin; prim_id < end; ++prim_id) {
973 auto objectid = mybvh->prim_ids[prim_id];
974 const auto &facet = fFacets[objectid];
975 const auto &n = fOutwardNormals[objectid];
976
977 // quick normal test. Coming from outside, the dot product must be negative
978 if (n.Dot(dir_v) > 0.) {
979 continue;
980 }
981
982 auto thisdist = rayFacet(Vertex_t(point[0], point[1], point[2]), dir_v, facet, fVertices, 0.);
983
984 if (thisdist < local_step) {
986 }
987 }
988 return false; // go on after this
989 });
990
991 return local_step;
992}
993
994////////////////////////////////////////////////////////////////////////////////
995/// DistFromOutside
996
998 Double_t /*stepmax*/, Double_t * /*safe*/) const
999{
1000 // use the BVH intersector in combination with leaf ray-triangle testing
1001 double local_step = Big(); // we need this otherwise the lambda get's confused
1002
1003 using Scalar = float;
1005 using Node = bvh::v2::Node<Scalar, 3>;
1006 using Bvh = bvh::v2::Bvh<Node>;
1007 using Ray = bvh::v2::Ray<Scalar, 3>;
1008
1009 // let's fetch the bvh
1010 auto mybvh = (Bvh *)fBVH;
1011 if (!mybvh) {
1012 assert(false);
1013 return -1.;
1014 }
1015
1016 auto truncate_roundup = [](double orig) {
1017 float epsilon = std::numeric_limits<float>::epsilon() * std::fabs(orig);
1018 // Add the bias to x before assigning it to y
1019 return static_cast<float>(orig + epsilon);
1020 };
1021
1022 // the ray used for bvh interaction
1023 Ray ray(Vec3(point[0], point[1], point[2]), // origin
1024 Vec3(dir[0], dir[1], dir[2]), // direction
1025 0., // minimum distance (could give stepmax ?)
1027
1028 static constexpr bool use_robust_traversal = true;
1029
1030 Vertex_t dir_v{dir[0], dir[1], dir[2]};
1031 // Traverse the BVH and apply concrete object intersection in BVH leafs
1033 mybvh->intersect<false, use_robust_traversal>(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) {
1034 for (size_t prim_id = begin; prim_id < end; ++prim_id) {
1035 auto objectid = mybvh->prim_ids[prim_id];
1036 auto facet = fFacets[objectid];
1037 const auto &n = fOutwardNormals[objectid];
1038
1039 // Only exiting surfaces are relevant (from inside--> dot product must be positive)
1040 if (n.Dot(dir_v) <= 0.) {
1041 continue;
1042 }
1043
1044 const double t = rayFacet(Vertex_t{point[0], point[1], point[2]}, dir_v, facet, fVertices, 0.);
1045 if (t < local_step) {
1046 local_step = t;
1047 }
1048 }
1049 return false; // go on after this
1050 });
1051
1052 return local_step;
1053}
1054
1055////////////////////////////////////////////////////////////////////////////////
1056/// Capacity
1057
1059{
1060 // For explanation of the following algorithm see:
1061 // https://en.wikipedia.org/wiki/Polyhedron#Volume
1062 // http://wwwf.imperial.ac.uk/~rn/centroid.pdf
1063
1064 double vol = 0.0;
1065 for (size_t i = 0; i < fFacets.size(); ++i) {
1066 auto &facet = fFacets[i];
1067 auto a = fVertices[facet[0]];
1068 auto b = fVertices[facet[1]];
1069 auto c = fVertices[facet[2]];
1070 vol +=
1071 a[0] * (b[1] * c[2] - b[2] * c[1]) + b[0] * (c[1] * a[2] - c[2] * a[1]) + c[0] * (a[1] * b[2] - a[2] * b[1]);
1072 }
1073 return vol / 6.0;
1074}
1075
1076////////////////////////////////////////////////////////////////////////////////
1077/// BuildBVH
1078
1080{
1081 using Scalar = float;
1082 using BBox = bvh::v2::BBox<Scalar, 3>;
1084 using Node = bvh::v2::Node<Scalar, 3>;
1085 using Bvh = bvh::v2::Bvh<Node>;
1086
1087 // helper determining axis aligned bounding box from a facet;
1088 auto GetBoundingBox = [this](TGeoFacet const &facet) {
1089 const auto nvertices = facet.GetNvert();
1090 if (nvertices != 3 && nvertices != 4)
1091 Fatal("BuildBVH", "only facets with 3 or 4 vertices supported");
1092 const auto &v1 = fVertices[facet[0]];
1093 const auto &v2 = fVertices[facet[1]];
1094 const auto &v3 = fVertices[facet[2]];
1095 const auto &v4 = (nvertices == 4) ? fVertices[facet[3]] : v3;
1096 BBox bbox;
1097 // A quad needs all four vertices in the BVH bounds even though traversal tests two triangles later on.
1098 bbox.min[0] = std::min(std::min(std::min(v1[0], v2[0]), v3[0]), v4[0]) - 0.001f;
1099 bbox.min[1] = std::min(std::min(std::min(v1[1], v2[1]), v3[1]), v4[1]) - 0.001f;
1100 bbox.min[2] = std::min(std::min(std::min(v1[2], v2[2]), v3[2]), v4[2]) - 0.001f;
1101 bbox.max[0] = std::max(std::max(std::max(v1[0], v2[0]), v3[0]), v4[0]) + 0.001f;
1102 bbox.max[1] = std::max(std::max(std::max(v1[1], v2[1]), v3[1]), v4[1]) + 0.001f;
1103 bbox.max[2] = std::max(std::max(std::max(v1[2], v2[2]), v3[2]), v4[2]) + 0.001f;
1104 return bbox;
1105 };
1106
1107 // we need bounding boxes enclosing the primitives and centers of primitives
1108 // (replaced here by centers of bounding boxes) to build the bvh
1109 std::vector<BBox> bboxes;
1110 std::vector<Vec3> centers;
1111
1112 int nd = fFacets.size();
1113 bboxes.reserve(nd);
1114 centers.reserve(nd);
1115
1116 for (int i = 0; i < nd; ++i) {
1117 auto &facet = fFacets[i];
1118
1119 // fetch the bounding box of this node and add to the vector of bounding boxes
1120 (bboxes).push_back(GetBoundingBox(facet));
1121 centers.emplace_back((bboxes).back().get_center());
1122 }
1123
1124 // check if some previous object is registered and delete if necessary
1125 if (fBVH) {
1126 delete (Bvh *)fBVH;
1127 fBVH = nullptr;
1128 }
1129
1130 // create the bvh
1133 auto bvh = bvh::v2::DefaultBuilder<Node>::build(bboxes, centers, config);
1134 auto bvhptr = new Bvh;
1135 *bvhptr = std::move(bvh); // copy structure
1136 fBVH = (void *)(bvhptr);
1137}
1138
1139////////////////////////////////////////////////////////////////////////////////
1140/// Contains
1141
1142bool TGeoTessellated::Contains(Double_t const *point) const
1143{
1144 // we do the parity test
1145 using Scalar = float;
1147 using Node = bvh::v2::Node<Scalar, 3>;
1148 using Bvh = bvh::v2::Bvh<Node>;
1149 using Ray = bvh::v2::Ray<Scalar, 3>;
1150
1151 // let's fetch the bvh
1152 auto mybvh = (Bvh *)fBVH;
1153 if (!mybvh) {
1154 assert(false);
1155 return false;
1156 }
1157
1158 auto truncate_roundup = [](double orig) {
1159 float epsilon = std::numeric_limits<float>::epsilon() * std::fabs(orig);
1160 // Add the bias to x before assigning it to y
1161 return static_cast<float>(orig + epsilon);
1162 };
1163
1164 // let's do very quick checks against the top node
1165 if (!TGeoBBox::Contains(point)) {
1166 return false;
1167 }
1168
1169 // An arbitrary test direction.
1170 // Doesn't need to be normalized and probes all normals. Also ensuring to be skewed somewhat
1171 // without evident symmetries.
1172 Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757};
1173
1174 double local_step = Big();
1175 // the ray used for bvh interaction
1176 Ray ray(Vec3(point[0], point[1], point[2]), // origin
1177 Vec3(test_dir[0], test_dir[1], test_dir[2]), // direction
1178 0.0f, // minimum distance (could give stepmax ?)
1180
1181 static constexpr bool use_robust_traversal = true;
1182
1183 // Traverse the BVH and apply concrete object intersection in BVH leafs
1185 size_t crossings = 0;
1186 mybvh->intersect<false, use_robust_traversal>(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) {
1187 for (size_t prim_id = begin; prim_id < end; ++prim_id) {
1188 auto objectid = mybvh->prim_ids[prim_id];
1189 auto &facet = fFacets[objectid];
1190
1191 // for the parity test, we probe all crossing surfaces
1192 if (rayFacetHit(Vertex_t(point[0], point[1], point[2]), test_dir, facet, fVertices, 0.)) {
1193 ++crossings;
1194 }
1195 }
1196 return false;
1197 });
1198
1199 return crossings & 1;
1200}
1201
1202namespace {
1203
1204// Helper classes/structs used for priority queue - BVH traversal
1205// structure keeping cost (value) for a BVH index
1206struct BVHPrioElement {
1207 size_t bvh_node_id;
1208 float value;
1209};
1210
1211// A priority queue for BVHPrioElement with an additional clear method
1212// for quick reset. We intentionally derive from std::priority_queue here to expose a
1213// clear() convenience method via access to the protected container `c`.
1214// This is internal, non-polymorphic code and relies on standard-library
1215// implementation details that are stable across supported platforms.
1216template <typename Comparator>
1217class BVHPrioQueue : public std::priority_queue<BVHPrioElement, std::vector<BVHPrioElement>, Comparator> {
1218public:
1219 using std::priority_queue<BVHPrioElement, std::vector<BVHPrioElement>,
1220 Comparator>::priority_queue; // constructor inclusion
1221
1222 // convenience method to quickly clear/reset the queue (instead of having to pop one by one)
1223 void clear() { this->c.clear(); }
1224};
1225
1226} // namespace
1227
1228/// a reusable safety kernel, which optionally returns the closest face
1229template <bool returnFace>
1230inline Double_t TGeoTessellated::SafetyKernel(const Double_t *point, bool in, int *closest_facet_id) const
1231{
1232 // This is the classic traversal/pruning of a BVH based on priority queue search
1233
1235
1236 using Scalar = float;
1238 using Node = bvh::v2::Node<Scalar, 3>;
1239 using Bvh = bvh::v2::Bvh<Node>;
1240
1241 // let's fetch the bvh
1242 auto mybvh = (Bvh *)fBVH;
1243
1244 // testpoint object in float for quick BVH interaction
1245 Vec3 testpoint(point[0], point[1], point[2]);
1246
1247 auto currnode = mybvh->nodes[0]; // we start from the top BVH node
1248 // we do a quick check on the top node (in case we are outside shape)
1249 bool outside_top = false;
1250 if (!in) {
1252 if (outside_top) {
1254 // we simply return safety to the outer bounding box as an estimate
1255 return std::sqrt(safety_sq_to_top);
1256 }
1257 }
1258
1259 // comparator bringing out "smallest" value on top
1260 auto cmp = [](BVHPrioElement a, BVHPrioElement b) { return a.value > b.value; };
1261 static thread_local BVHPrioQueue<decltype(cmp)> queue(cmp);
1262 queue.clear();
1263
1264 // algorithm is based on standard iterative tree traversal with priority queues
1265 float current_safety_to_node_sq = 0.f;
1266
1267 if (returnFace) {
1268 *closest_facet_id = -1;
1269 }
1270
1271 do {
1272 if (currnode.is_leaf()) {
1273 // we are in a leaf node and actually talk to a face primitive
1274 const auto begin_prim_id = currnode.index.first_id();
1275 const auto end_prim_id = begin_prim_id + currnode.index.prim_count();
1276
1277 for (auto p_id = begin_prim_id; p_id < end_prim_id; p_id++) {
1278 const auto object_id = mybvh->prim_ids[p_id];
1279
1280 const auto &facet = fFacets[object_id];
1281 auto thissafetySQ = pointFacetDistSq(Vec3f<float>(point[0], point[1], point[2]), facet, fVertices);
1282
1285 if (returnFace) {
1287 }
1288 }
1289 }
1290 } else {
1291 // not a leave node ... for further traversal,
1292 // we inject the children into priority queue based on distance to it's bounding box
1293 const auto leftchild_id = currnode.index.first_id();
1294 const auto rightchild_id = leftchild_id + 1;
1295
1296 for (size_t childid : {leftchild_id, rightchild_id}) {
1297 if (childid >= mybvh->nodes.size()) {
1298 continue;
1299 }
1300
1301 const auto &node = mybvh->nodes[childid];
1302 const auto inside = bvh::v2::extra::contains(node.get_bbox(), testpoint);
1303
1304 if (inside) {
1305 // this must be further considered because we are inside the bounding box
1306 queue.push(BVHPrioElement{childid, -1.});
1307 } else {
1310 // this should be further considered
1311 queue.push(BVHPrioElement{childid, safety_to_node_square});
1312 }
1313 }
1314 }
1315 }
1316
1317 if (queue.size() > 0) {
1318 auto currElement = queue.top();
1319 currnode = mybvh->nodes[currElement.bvh_node_id];
1321 queue.pop();
1322 } else {
1323 break;
1324 }
1326
1327 return std::nextafter(std::sqrt(smallest_safety_sq), 0.0f);
1328}
1329
1330////////////////////////////////////////////////////////////////////////////////
1331/// Safety
1332
1334{
1335 // we could use some caching here (in future) since queries to the solid will likely
1336 // be made with some locality
1337
1338 // fall-back to precise safety kernel
1339 return SafetyKernel<false>(point, in);
1340}
1341
1342////////////////////////////////////////////////////////////////////////////////
1343/// ComputeNormal interface
1344
1345void TGeoTessellated::ComputeNormal(const Double_t *point, const Double_t *dir, Double_t *norm) const
1346{
1347 // We take the approach to identify closest facet to the point via safety
1348 // and returning the normal from this face.
1349
1350 // TODO: Before doing that we could check for cached points from other queries
1351
1352 // use safety kernel
1353 int closest_face_id = -1;
1354 SafetyKernel<true>(point, true, &closest_face_id);
1355
1356 if (closest_face_id < 0) {
1357 norm[0] = 1.;
1358 norm[1] = 0.;
1359 norm[2] = 0.;
1360 return;
1361 }
1362
1363 const auto &n = fOutwardNormals[closest_face_id];
1364 norm[0] = n[0];
1365 norm[1] = n[1];
1366 norm[2] = n[2];
1367
1368 // change sign depending on dir
1369 if (norm[0] * dir[0] + norm[1] * dir[1] + norm[2] * dir[2] < 0) {
1370 norm[0] = -norm[0];
1371 norm[1] = -norm[1];
1372 norm[2] = -norm[2];
1373 }
1374 return;
1375}
1376
1377////////////////////////////////////////////////////////////////////////////////
1378/// Custom streamer which performs Closing on read.
1379/// Recalculation of BVH and normals is fast
1380
1382{
1383 if (b.IsReading()) {
1384 b.ReadClassBuffer(TGeoTessellated::Class(), this);
1385 CloseShape(false); // close shape but do not re-perform checks
1386 } else {
1387 b.WriteClassBuffer(TGeoTessellated::Class(), this);
1388 }
1389}
1390
1391////////////////////////////////////////////////////////////////////////////////
1392/// Calculate the normals
1393
1395{
1396 fOutwardNormals.clear();
1397 fOutwardNormals.reserve(fFacets.size());
1398 for (size_t i = 0; i < fFacets.size(); ++i) {
1399 // Reuse the generic facet normal code so triangles and quads follow the same path.
1400 bool degenerated = false;
1401 auto norm = FacetComputeNormal(static_cast<int>(i), degenerated);
1402 fOutwardNormals.emplace_back(norm);
1403 }
1404}
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
#define e(i)
Definition RSha256.hxx:103
float Float_t
Float 4 bytes (float)
Definition RtypesCore.h:72
const char Option_t
Option string (const char)
Definition RtypesCore.h:81
#define ClassImp(name)
Definition Rtypes.h:375
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t points
char name[80]
Definition TGX11.cxx:142
float * q
TTime operator-(const TTime &t1, const TTime &t2)
Definition TTime.h:83
Generic 3D primitive description class.
Definition TBuffer3D.h:18
UInt_t NbPnts() const
Definition TBuffer3D.h:89
Bool_t SectionsValid(UInt_t mask) const
Definition TBuffer3D.h:76
void SetSectionsValid(UInt_t mask)
Definition TBuffer3D.h:74
Bool_t fLocalFrame
Definition TBuffer3D.h:99
Bool_t SetRawSizes(UInt_t reqPnts, UInt_t reqPntsCapacity, UInt_t reqSegs, UInt_t reqSegsCapacity, UInt_t reqPols, UInt_t reqPolsCapacity)
Set kRaw tessellation section of buffer with supplied sizes.
Double_t * fPnts
Definition TBuffer3D.h:122
Buffer base class used for serializing objects.
Definition TBuffer.h:43
Box class.
Definition TGeoBBox.h:18
void FillBuffer3D(TBuffer3D &buffer, Int_t reqSections, Bool_t localFrame) const override
Fills the supplied buffer, with sections in desired frame See TBuffer3D.h for explanation of sections...
Double_t fDX
Definition TGeoBBox.h:21
Double_t fOrigin[3]
Definition TGeoBBox.h:24
Bool_t Contains(const Double_t *point) const override
Test if point is inside this shape.
Definition TGeoBBox.cxx:322
Double_t fDY
Definition TGeoBBox.h:22
Double_t fDZ
Definition TGeoBBox.h:23
bool IsNeighbour(const TGeoFacet &other, bool &flip) const
Check if a connected neighbour facet has compatible normal.
static int CompactFacet(Vertex_t *vert, int nvertices)
Compact consecutive equal vertices.
static Double_t Big()
Definition TGeoShape.h:95
Int_t GetBasicColor() const
Get the basic color (0-7).
void TransformPoints(Double_t *points, UInt_t NbPoints) const
Tranform a set of points (LocalToMaster)
const char * GetName() const override
Get the shape name.
Tessellated solid class.
Double_t Safety(const Double_t *point, Bool_t in=kTRUE) const override
Safety.
void ResizeCenter(double maxsize)
Resize and center the shape in a box of size maxsize.
int AddVertex(const Vertex_t &vert)
Add a vertex checking for duplicates, returning the vertex index.
bool Contains(const Double_t *point) const override
Contains.
void ComputeNormal(const Double_t *point, const Double_t *dir, Double_t *norm) const override
ComputeNormal interface.
bool FacetCheck(int ifacet) const
Check validity of facet.
void Streamer(TBuffer &) override
Custom streamer which performs Closing on read.
Double_t SafetyKernel(const Double_t *point, bool in, int *closest_facet_id=nullptr) const
a reusable safety kernel, which optionally returns the closest face
void Print(Option_t *option="") const override
Prints basic info.
Double_t Capacity() const override
Capacity.
Double_t DistFromInside(const Double_t *point, const Double_t *dir, Int_t iact=1, Double_t step=TGeoShape::Big(), Double_t *safe=nullptr) const override
DistFromOutside.
void * fBVH
to know if shape still needs closure/initialization
Double_t DistFromOutside(const Double_t *point, const Double_t *dir, Int_t iact=1, Double_t step=TGeoShape::Big(), Double_t *safe=nullptr) const override
DistFromOutside.
void SetSegsAndPols(TBuffer3D &buff) const override
Fills TBuffer3D structure for segments and polygons.
void BuildBVH()
BuildBVH.
const TBuffer3D & GetBuffer3D(int reqSections, Bool_t localFrame) const override
Fills a static 3D buffer and returns a reference.
void SetPoints(double *points) const override
Fill tessellated points to an array.
bool CheckClosure(bool fixFlipped=true, bool verbose=true)
Check closure of the solid and check/fix flipped normals.
int GetNvertices() const
bool fDefined
! Shape fully defined
Vertex_t FacetComputeNormal(int ifacet, bool &degenerated) const
Compute normal for a given facet.
void CloseShape(bool check=true, bool fixFlipped=true, bool verbose=true)
Close the shape: calculate bounding box and compact vertices.
Tessellated::Vertex_t Vertex_t
void GetMeshNumbers(int &nvert, int &nsegs, int &npols) const override
Returns numbers of vertices, segments and polygons composing the shape mesh.
std::vector< TGeoFacet > fFacets
static TGeoTessellated * ImportFromObjFormat(const char *objfile, bool check=false, bool verbose=false)
Reader from .obj format.
TBuffer3D * MakeBuffer3D() const override
Creates a TBuffer3D describing this shape.
void ComputeBBox() override
Compute bounding box.
std::multimap< long, int > fVerticesMap
! Temporary map used to deduplicate vertices
std::vector< Vertex_t > fOutwardNormals
bool AddFacet(const Vertex_t &pt0, const Vertex_t &pt1, const Vertex_t &pt2)
Adding a triangular facet from vertex positions in absolute coordinates.
void CalculateNormals()
Calculate the normals.
static TClass * Class()
std::vector< Vertex_t > fVertices
int GetNfacets() const
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:1082
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1096
virtual void Fatal(const char *method, const char *msgfmt,...) const
Issue fatal error message.
Definition TObject.cxx:1124
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1070
This builder is only a wrapper around all the other builders, which selects the best builder dependin...
static BVH_ALWAYS_INLINE Bvh< Node > build(ThreadPool &thread_pool, std::span< const BBox > bboxes, std::span< const Vec > centers, const Config &config={})
Build a BVH in parallel using the given thread pool.
TLine * line
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
Short_t Max(Short_t a, Short_t b)
Returns the largest of a and b.
Definition TMathBase.h:249
Short_t Min(Short_t a, Short_t b)
Returns the smallest of a and b.
Definition TMathBase.h:197
ROOT::Geom::Vertex_t Vertex_t
bool contains(bvh::v2::BBox< T, 3 > const &box, bvh::v2::Vec< T, 3 > const &p)
auto SafetySqToNode(bvh::v2::BBox< T, 3 > const &box, bvh::v2::Vec< T, 3 > const &p)
BVH_ALWAYS_INLINE Vec< T, 3 > cross(const Vec< T, 3 > &a, const Vec< T, 3 > &b)
Definition vec.h:104
BVH_ALWAYS_INLINE T dot(const Vec< T, N > &a, const Vec< T, N > &b)
Definition vec.h:98
Definition bbox.h:9
static Vertex_t Cross(Vertex_t const &left, Vertex_t const &right)
The cross (vector) product of two Vector3D<T> objects.
static double Dot(Vertex_t const &left, Vertex_t const &right)
The dot product of two vector objects.
Definition TGeoVector3.h:98
Quality quality
The quality of the BVH produced by the builder.
Growing stack that can be used for BVH traversal.
Definition stack.h:34
Binary BVH node, containing its bounds and an index into its children or the primitives it contains.
Definition node.h:23