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