21#include "TGeoManager.h"
22#include "TGeoMatrix.h"
23#include "TGeoVolume.h"
24#include "TVirtualGeoPainter.h"
27#include "TBuffer3DTypes.h"
53int TGeoFacet::CompactFacet(
Vertex_t* vert,
int nvertices)
58 int nvert = nvertices;
61 if (vert[(
i + 1) % nvert] == vert[
i]) {
63 for (
int j =
i + 2;
j < nvert; ++
j)
64 vert[
j - 1] = vert[
j];
75bool TGeoFacet::IsNeighbour(
const TGeoFacet&
other,
bool& flip)
const
79 bool neighbour =
false;
80 int line1[2], line2[2];
82 for (
int i = 0;
i < fNvert; ++
i) {
83 auto ivert = fIvert[
i];
85 for (
int j = 0;
j <
other.GetNvert(); ++
j) {
91 bool order1 = line1[1] == line1[0] + 1;
92 bool order2 = line2[1] == (line2[0] + 1) %
other.GetNvert();
93 flip = (order1 == order2);
110 fFacets.reserve(nfacets);
119 fVertices = vertices;
120 fNvert = fVertices.size();
128 fNfacets = tsl.GetNfacets();
129 fNvert = tsl.GetNvertices();
130 fNseg = tsl.GetNsegments();
133 fVertices.reserve(fNvert);
134 fFacets.reserve(fNfacets);
135 for (
int i = 0;
i < fNfacets; ++
i) {
136 fFacets.push_back(tsl.GetFacet(
i));
138 for (
int i = 0;
i < fNvert; ++
i) {
139 fVertices.push_back(tsl.GetVertex(
i));
150 constexpr double tolerance = 1.e-10;
155 auto hash_combine = [](
long seed,
const long value) {
156 return seed ^ (std::hash<long>{}(
value) + 0x9e3779b9 + (seed << 6) + (seed >> 2));
158 for (
int i = 0;
i < 3;
i++) {
165 auto hash = vertexHash(vert);
166 bool isAdded =
false;
169 auto range = fVerticesMap.equal_range(
hash);
170 for (
auto it =
range.first; it !=
range.second; ++it) {
172 if (fVertices[ivert] == vert) {
178 ivert = fVertices.size();
179 fVertices.push_back(vert);
180 fVerticesMap.insert(std::make_pair(
hash, ivert));
191 Error(
"AddFacet",
"Shape %s already fully defined. Not adding", GetName());
199 int nvert = TGeoFacet::CompactFacet(vert, 3);
201 Error(
"AddFacet",
"Triangular facet at index %d degenerated. Not adding.",
GetNfacets());
205 for (
auto i = 0;
i < 3; ++
i)
208 fFacets.emplace_back(ind[0], ind[1], ind[2]);
219 Error(
"AddFacet",
"Shape %s already fully defined. Not adding", GetName());
222 if (fVertices.empty()) {
223 Error(
"AddFacet",
"Shape %s Cannot add facets by indices without vertices. Not adding", GetName());
228 fFacets.emplace_back(i0, i1, i2);
238 Error(
"AddFacet",
"Shape %s already fully defined. Not adding", GetName());
246 int nvert = TGeoFacet::CompactFacet(vert, 4);
248 Error(
"AddFacet",
"Quadrilateral facet at index %d degenerated. Not adding.",
GetNfacets());
253 for (
auto i = 0;
i < nvert; ++
i)
257 fFacets.emplace_back(ind[0], ind[1], ind[2]);
259 fFacets.emplace_back(ind[0], ind[1], ind[2], ind[3]);
272 Error(
"AddFacet",
"Shape %s already fully defined. Not adding", GetName());
275 if (fVertices.empty()) {
276 Error(
"AddFacet",
"Shape %s Cannot add facets by indices without vertices. Not adding", GetName());
281 fFacets.emplace_back(i0, i1, i2, i3);
291 constexpr double kTolerance = 1.e-20;
292 auto const& facet = fFacets[ifacet];
293 int nvert = facet.GetNvert();
296 for (
int i = 0;
i < nvert - 1; ++
i) {
297 Vertex_t e1 = fVertices[facet[
i + 1]] - fVertices[facet[
i]];
298 if (e1.Mag2() < kTolerance)
300 for (
int j =
i + 1;
j < nvert; ++
j) {
301 Vertex_t e2 = fVertices[facet[(
j + 1) % nvert]] - fVertices[facet[
j]];
302 if (e2.Mag2() < kTolerance)
304 normal = Vertex_t::Cross(e1, e2);
306 if (normal.Mag2() < kTolerance)
323 constexpr double kTolerance = 1.e-10;
324 auto const& facet = fFacets[ifacet];
325 int nvert = facet.GetNvert();
326 bool degenerated =
true;
329 std::cout <<
"Facet: " << ifacet <<
" is degenerated\n";
334 double surfaceArea = 0.;
335 for (
int i = 1;
i < nvert - 1; ++
i) {
336 Vertex_t e1 = fVertices[facet[
i]] - fVertices[facet[0]];
337 Vertex_t e2 = fVertices[facet[
i + 1]] - fVertices[facet[0]];
338 surfaceArea += 0.5 * Vertex_t::Cross(e1, e2).Mag();
340 if (surfaceArea < kTolerance) {
341 std::cout <<
"Facet: " << ifacet <<
" has zero surface area\n";
353 if (fIsClosed && fBVH) {
358 fNvert = fVertices.size();
359 fNfacets = fFacets.size();
363 if (fOutwardNormals.size() == 0) {
367 if (fOutwardNormals.size() != fFacets.size()) {
368 std::cerr <<
"Inconsistency in normal container";
374 std::multimap<long, int>().swap(fVerticesMap);
376 if (fVertices.size() > 0) {
381 for (
auto i = 0;
i < fNfacets; ++
i)
393 int*
nn =
new int[fNfacets];
394 bool* flipped =
new bool[fNfacets];
395 bool hasorphans =
false;
396 bool hasflipped =
false;
397 for (
int i = 0;
i < fNfacets; ++
i) {
402 for (
int icrt = 0; icrt < fNfacets; ++icrt) {
404 if (
nn[icrt] >= fFacets[icrt].GetNvert())
406 for (
int i = icrt + 1;
i < fNfacets; ++
i) {
407 bool isneighbour = fFacets[icrt].IsNeighbour(fFacets[
i], flipped[
i]);
410 flipped[
i] = !flipped[
i];
415 if (
nn[icrt] == fFacets[icrt].GetNvert())
419 if (
nn[icrt] < fFacets[icrt].GetNvert())
423 if (hasorphans && verbose) {
424 Error(
"Check",
"Tessellated solid %s has following not fully connected facets:", GetName());
425 for (
int icrt = 0; icrt < fNfacets; ++icrt) {
426 if (
nn[icrt] < fFacets[icrt].GetNvert())
427 std::cout << icrt <<
" (" << fFacets[icrt].GetNvert() <<
" edges, " <<
nn[icrt] <<
" neighbours)\n";
430 fClosedBody = !hasorphans;
434 Warning(
"Check",
"Tessellated solid %s has following facets with flipped normals:", GetName());
435 for (
int icrt = 0; icrt < fNfacets; ++icrt) {
438 std::cout << icrt <<
"\n";
440 fFacets[icrt].Flip();
445 if (nfixed && verbose)
446 Info(
"Check",
"Automatically flipped %d facets to match first defined facet", nfixed);
459 const double kBig = TGeoShape::Big();
460 double vmin[3] = {kBig, kBig, kBig};
461 double vmax[3] = {-kBig, -kBig, -kBig};
462 for (
const auto& facet : fFacets) {
463 for (
int i = 0;
i < facet.GetNvert(); ++
i) {
464 for (
int j = 0;
j < 3; ++
j) {
465 vmin[
j] = TMath::Min(vmin[
j], fVertices[facet[
i]].
operator[](
j));
466 vmax[
j] = TMath::Max(vmax[
j], fVertices[facet[
i]].
operator[](
j));
470 fDX = 0.5 * (vmax[0] - vmin[0]);
471 fDY = 0.5 * (vmax[1] - vmin[1]);
472 fDZ = 0.5 * (vmax[2] - vmin[2]);
473 for (
int i = 0;
i < 3; ++
i)
474 fOrigin[
i] = 0.5 * (vmax[
i] + vmin[
i]);
492 if (
array ==
nullptr || npoints <= 0 || fVertices.empty()) {
495 const int vertexCount =
static_cast<int>(fVertices.size());
496 if (npoints < vertexCount) {
501 for (
int vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) {
502 fVertices[vertexIndex].CopyTo(&
array[3 * vertexIndex]);
505 const int extraCount = npoints - vertexCount;
506 const int facetCount =
static_cast<int>(fFacets.size());
507 if (extraCount == 0) {
510 if (facetCount == 0) {
511 for (
int extraIndex = 0; extraIndex < extraCount; ++extraIndex) {
512 fVertices[extraIndex % vertexCount].CopyTo(&
array[3 * (vertexCount + extraIndex)]);
519 constexpr double kAlpha1 = 0.7548776662466927;
520 constexpr double kAlpha2 = 0.5698402909980532;
521 for (
int extraIndex = 0; extraIndex < extraCount; ++extraIndex) {
522 const int facetIndex =
523 static_cast<int>((
static_cast<long long>(extraIndex) * facetCount) / extraCount) % facetCount;
524 const TGeoFacet& facet = fFacets[facetIndex];
525 const int facetVertices = facet.GetNvert();
526 double first = std::fmod(0.5 + kAlpha1 * (extraIndex + 1), 1.);
527 double second = std::fmod(0.5 + kAlpha2 * (extraIndex + 1), 1.);
528 if (
first + second > 1.) {
530 second = 1. - second;
534 const int cornerB = (facetVertices > 3 && (extraIndex & 1)) ? 2 : 1;
535 const int cornerC = (facetVertices > 3 && (extraIndex & 1)) ? 3 : ((facetVertices > 2) ? 2 : 1);
536 const Vertex_t& vertexA = fVertices[facet[0]];
537 const Vertex_t& vertexB = fVertices[facet[cornerB]];
538 const Vertex_t& vertexC = fVertices[facet[cornerC]];
539 const double weightA = 1. -
first - second;
540 double* slot = &
array[3 * (vertexCount + extraIndex)];
541 slot[0] = weightA * vertexA.x() +
first * vertexB.x() + second * vertexC.x();
542 slot[1] = weightA * vertexA.y() +
first * vertexB.y() + second * vertexC.y();
543 slot[2] = weightA * vertexA.z() +
first * vertexB.z() + second * vertexC.z();
554 const int nvert = fNvert;
555 const int nsegs = fNseg;
557 auto buff =
new TBuffer3D(TBuffer3DTypes::kGeneric, nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols);
570 std::cout <<
"=== Tessellated shape " << GetName() <<
" having " <<
GetNvertices() <<
" vertices and "
579 const int c = GetBasicColor();
580 int* segs = buff.fSegs;
581 int* pols = buff.fPols;
586 for (
const auto& facet : fFacets) {
587 auto nvert = facet.GetNvert();
589 pols[indpol++] = nvert;
590 for (
auto j = 0;
j < nvert; ++
j) {
591 int k = (
j + 1) % nvert;
594 segs[indseg++] = facet[
j];
595 segs[indseg++] = facet[k];
597 pols[indpol + nvert -
j - 1] = sind++;
609 for (
const auto&
vertex : fVertices) {
610 vertex.CopyTo(&points[ind]);
621 for (
const auto&
vertex : fVertices) {
622 points[ind++] =
vertex.x();
623 points[ind++] =
vertex.y();
624 points[ind++] =
vertex.z();
636 Error(
"ResizeCenter",
"Not all faces are defined");
639 Vector3_t
origin(fOrigin[0], fOrigin[1], fOrigin[2]);
640 double maxedge = TMath::Max(TMath::Max(fDX, fDY), fDZ);
641 double scale = maxsize / maxedge;
642 for (
size_t i = 0;
i < fVertices.size(); ++
i) {
643 fVertices[
i] = scale * (fVertices[
i] -
origin);
645 fOrigin[0] = fOrigin[1] = fOrigin[2] = 0;
656 static TBuffer3D
buffer(TBuffer3DTypes::kGeneric);
658 FillBuffer3D(
buffer, reqSections, localFrame);
660 const int nvert = fNvert;
661 const int nsegs = fNseg;
664 if (reqSections & TBuffer3D::kRawSizes) {
665 if (
buffer.SetRawSizes(nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols)) {
666 buffer.SetSectionsValid(TBuffer3D::kRawSizes);
669 if ((reqSections & TBuffer3D::kRaw) &&
buffer.SectionsValid(TBuffer3D::kRawSizes)) {
671 if (!
buffer.fLocalFrame) {
676 buffer.SetSectionsValid(TBuffer3D::kRaw);
687 using std::vector, std::string, std::ifstream, std::stringstream, std::endl;
689 vector<Vertex_t> vertices;
690 vector<string> sfacets;
698 FacetInd_t(
int a,
int b,
int c)
705 FacetInd_t(
int a,
int b,
int c,
int d)
715 vector<FacetInd_t> facets;
738 ifstream
file(objfile);
739 if (!
file.is_open()) {
740 ::Error(
"O2Tessellated::ImportFromObjFormat",
"Unable to open %s", objfile);
744 while (getline(
file, line)) {
745 stringstream ss(line);
749 if (line.rfind(
'v', 0) == 0 && line.rfind(
"vt", 0) != 0 && line.rfind(
"vn", 0) != 0 && line.rfind(
"vn", 0) != 0) {
751 double pos[4] = {0, 0, 0, 1};
756 else if (line.rfind(
'f', 0) == 0) {
762 sfacets.push_back(word);
763 if (sfacets.size() > 4 || sfacets.size() < 3) {
764 ::Error(
"O2Tessellated::ImportFromObjFormat",
"Detected face having unsupported %zu vertices",
769 for (
auto& sword : sfacets) {
770 stringstream ssword(sword);
772 getline(ssword, token,
'/');
775 ind[nvert++] = stoi(token) - 1;
776 if (ind[nvert - 1] < 0) {
777 ::Error(
"O2Tessellated::ImportFromObjFormat",
"Unsupported relative vertex index definition in %s",
783 facets.emplace_back(ind[0], ind[1], ind[2]);
785 facets.emplace_back(ind[0], ind[1], ind[2], ind[3]);
789 int nvertices = (int)vertices.size();
790 int nfacets = (int)facets.size();
792 ::Error(
"O2Tessellated::ImportFromObjFormat",
"Not enough faces detected in %s", objfile);
796 string sobjfile(objfile);
798 std::cout <<
"Read " << nvertices <<
" vertices and " << nfacets <<
" facets from " << sobjfile << endl;
800 auto tsl =
new O2Tessellated(sobjfile.erase(sobjfile.find_last_of(
'.')).c_str(), vertices);
802 for (
int i = 0;
i < nfacets; ++
i) {
803 auto facet = facets[
i];
804 if (facet.nvert == 3)
805 tsl->AddFacet(facet.i0, facet.i1, facet.i2);
807 tsl->AddFacet(facet.i0, facet.i1, facet.i2, facet.i3);
809 tsl->CloseShape(
check,
true, verbose);
818using Vertex_t = Tessellated::Vertex_t;
828 constexpr double EPS = 1
e-8;
829 const double INF = std::numeric_limits<double>::infinity();
832 auto p = Vertex_t::Cross(dir, e2);
833 auto det = e1.Dot(p);
834 if (std::abs(det) <= EPS) {
839 auto invDet = 1.0 / det;
840 auto u = tvec.Dot(p) * invDet;
841 if (u < 0.0 || u > 1.0) {
844 auto q = Vertex_t::Cross(tvec, e1);
845 auto v = dir.Dot(q) * invDet;
846 if (v < 0.0 || u + v > 1.0) {
849 auto t = e2.Dot(q) * invDet;
850 return (
t > rayEPS) ?
t : INF;
853template <
typename T =
float>
859inline Vec3f<T>
operator-(
const Vec3f<T>&
a,
const Vec3f<T>&
b)
861 return {
a.x -
b.x,
a.y -
b.y,
a.z -
b.z};
865inline Vec3f<T>
cross(
const Vec3f<T>&
a,
const Vec3f<T>&
b)
867 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};
871inline T dot(
const Vec3f<T>&
a,
const Vec3f<T>&
b)
873 return a.x *
b.x +
a.y *
b.y +
a.z *
b.z;
882template <
typename T =
float>
883T pointTriangleDistSq(
const Vec3f<T>& p,
const Vec3f<T>&
a,
const Vec3f<T>&
b,
const Vec3f<T>&
c)
890 auto d1 =
dot(ab, ap);
891 auto d2 =
dot(ac, ap);
892 if (d1 <=
T(0.0) && d2 <=
T(0.0)) {
897 auto d3 =
dot(ab, bp);
898 auto d4 =
dot(ac, bp);
899 if (d3 >=
T(0.0) && d4 <= d3) {
904 if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) {
906 Vec3f<T> proj = {
a.x +
v * ab.x,
a.y +
v * ab.y,
a.z +
v * ab.z};
907 Vec3f<T>
d =
p - proj;
914 if (d6 >=
T(0.0f) && d5 <= d6) {
918 T vb = d5 *
d2 -
d1 * d6;
919 if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) {
921 Vec3f<T> proj = {
a.x +
w * ac.x,
a.y +
w * ac.y,
a.z +
w * ac.z};
922 Vec3f<T>
d =
p - proj;
926 T va =
d3 * d6 - d5 * d4;
927 if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) {
928 T w = (d4 -
d3) / ((d4 - d3) + (d5 - d6));
929 Vec3f<T> proj = {
b.x +
w * (
c.x -
b.x),
b.y +
w * (
c.y -
b.y),
b.z +
w * (
c.z -
b.z)};
930 Vec3f<T>
d =
p - proj;
935 T denom =
T(1.0f) / (va + vb + vc);
939 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};
941 Vec3f<T>
d =
p - proj;
946inline Vec3f<T> normalize(
const Vec3f<T>&
v)
949 if (len2 ==
T(0.0f)) {
950 std::cerr <<
"Degnerate triangle. Cannot determine normal";
953 T invLen =
T(1.0f) / std::sqrt(len2);
954 return {
v.x * invLen,
v.y * invLen,
v.z * invLen};
958inline Vec3f<T> triangleNormal(
const Vec3f<T>&
a,
const Vec3f<T>&
b,
const Vec3f<T>&
c)
960 const Vec3f<T> e1 =
b -
a;
961 const Vec3f<T> e2 =
c -
a;
962 return normalize(
cross(e1, e2));
966constexpr float kFacetBoxPad = 0.001f;
970constexpr double kMaxPruneScale = kFacetBoxPad * (1 << 24) / 8.;
973template <
typename BBox>
974double pruneLimit(
const BBox&
bbox,
const double* point)
981 std::abs(
static_cast<double>(
bbox.max[
index]))});
995 double local_step = Big();
997 using Scalar =
float;
998 using Vec3 = bvh::v2::Vec<Scalar, 3>;
999 using Node = bvh::v2::Node<Scalar, 3>;
1000 using Bvh = bvh::v2::Bvh<Node>;
1001 using Ray = bvh::v2::Ray<Scalar, 3>;
1004 auto mybvh = (Bvh*)fBVH;
1010 auto truncate_roundup = [](
double orig) {
1011 float epsilon = std::numeric_limits<float>::epsilon() * std::fabs(orig);
1013 return static_cast<float>(orig + epsilon);
1017 const auto topnode_bbox = mybvh->get_root().get_bbox();
1018 if ((-point[0] + topnode_bbox.min[0]) > stepmax) {
1021 if ((-point[1] + topnode_bbox.min[1]) > stepmax) {
1024 if ((-point[2] + topnode_bbox.min[2]) > stepmax) {
1027 if ((point[0] - topnode_bbox.max[0]) > stepmax) {
1030 if ((point[1] - topnode_bbox.max[1]) > stepmax) {
1033 if ((point[2] - topnode_bbox.max[2]) > stepmax) {
1038 Ray ray(Vec3(point[0], point[1], point[2]),
1039 Vec3(dir[0], dir[1], dir[2]),
1041 truncate_roundup(local_step));
1043 static constexpr bool use_robust_traversal =
true;
1047 const double prune_limit = pruneLimit(topnode_bbox, point);
1049 Vertex_t dir_v{dir[0], dir[1], dir[2]};
1051 bvh::v2::GrowingStack<Bvh::Index>
stack;
1052 mybvh->intersect<
false, use_robust_traversal>(ray, mybvh->get_root().index,
stack, [&](
size_t begin,
size_t end) {
1053 for (
size_t prim_id = begin; prim_id <
end; ++prim_id) {
1054 auto objectid = mybvh->prim_ids[prim_id];
1055 const auto& facet = fFacets[objectid];
1056 const auto&
n = fOutwardNormals[objectid];
1059 if (
n.Dot(dir_v) > 0.) {
1063 auto thisdist = rayTriangle(
Vertex_t(point[0], point[1], point[2]), dir_v,
1064 fVertices[facet[0]], fVertices[facet[1]], fVertices[facet[2]], 0.);
1066 if (thisdist < local_step) {
1067 local_step = thisdist;
1068 if (local_step <= prune_limit) {
1069 ray.tmax = truncate_roundup(local_step);
1086 double local_step = Big();
1088 using Scalar =
float;
1089 using Vec3 = bvh::v2::Vec<Scalar, 3>;
1090 using Node = bvh::v2::Node<Scalar, 3>;
1091 using Bvh = bvh::v2::Bvh<Node>;
1092 using Ray = bvh::v2::Ray<Scalar, 3>;
1095 auto mybvh = (Bvh*)fBVH;
1101 auto truncate_roundup = [](
double orig) {
1102 float epsilon = std::numeric_limits<float>::epsilon() * std::fabs(orig);
1104 return static_cast<float>(orig + epsilon);
1108 Ray ray(Vec3(point[0], point[1], point[2]),
1109 Vec3(dir[0], dir[1], dir[2]),
1111 truncate_roundup(local_step));
1113 static constexpr bool use_robust_traversal =
true;
1116 const auto rootbox = mybvh->get_root().get_bbox();
1117 const double prune_limit = pruneLimit(rootbox, point);
1119 Vertex_t dir_v{dir[0], dir[1], dir[2]};
1121 bvh::v2::GrowingStack<Bvh::Index>
stack;
1122 mybvh->intersect<
false, use_robust_traversal>(ray, mybvh->get_root().index,
stack, [&](
size_t begin,
size_t end) {
1123 for (
size_t prim_id = begin; prim_id <
end; ++prim_id) {
1124 auto objectid = mybvh->prim_ids[prim_id];
1125 auto facet = fFacets[objectid];
1126 const auto&
n = fOutwardNormals[objectid];
1129 if (
n.Dot(dir_v) <= 0.) {
1133 const auto&
v0 = fVertices[facet[0]];
1134 const auto&
v1 = fVertices[facet[1]];
1135 const auto&
v2 = fVertices[facet[2]];
1138 rayTriangle(
Vertex_t{point[0], point[1], point[2]}, dir_v,
v0,
v1,
v2, 0.);
1139 if (
t < local_step) {
1141 if (local_step <= prune_limit) {
1142 ray.tmax = truncate_roundup(local_step);
1162 for (
size_t i = 0;
i < fFacets.size(); ++
i) {
1163 auto& facet = fFacets[
i];
1164 auto a = fVertices[facet[0]];
1165 auto b = fVertices[facet[1]];
1166 auto c = fVertices[facet[2]];
1168 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]);
1176void O2Tessellated::BuildBVH()
1178 using Scalar =
float;
1179 using BBox = bvh::v2::BBox<Scalar, 3>;
1180 using Vec3 = bvh::v2::Vec<Scalar, 3>;
1181 using Node = bvh::v2::Node<Scalar, 3>;
1182 using Bvh = bvh::v2::Bvh<Node>;
1185 auto GetBoundingBox = [
this](TGeoFacet
const& facet) {
1187 const auto nvertices = facet.GetNvert();
1188 assert(nvertices == 3);
1190 const auto&
v1 = fVertices[facet[0]];
1191 const auto&
v2 = fVertices[facet[1]];
1192 const auto&
v3 = fVertices[facet[2]];
1194 bbox.min[0] = std::min(std::min(
v1[0],
v2[0]),
v3[0]) - kFacetBoxPad;
1195 bbox.min[1] = std::min(std::min(
v1[1],
v2[1]),
v3[1]) - kFacetBoxPad;
1196 bbox.min[2] = std::min(std::min(
v1[2],
v2[2]),
v3[2]) - kFacetBoxPad;
1197 bbox.max[0] = std::max(std::max(
v1[0],
v2[0]),
v3[0]) + kFacetBoxPad;
1198 bbox.max[1] = std::max(std::max(
v1[1],
v2[1]),
v3[1]) + kFacetBoxPad;
1199 bbox.max[2] = std::max(std::max(
v1[2],
v2[2]),
v3[2]) + kFacetBoxPad;
1205 std::vector<BBox> bboxes;
1206 std::vector<Vec3> centers;
1209 int nd = fFacets.size();
1210 for (
int i = 0;
i < nd; ++
i) {
1211 auto& facet = fFacets[
i];
1214 (bboxes).push_back(GetBoundingBox(facet));
1215 centers.emplace_back((bboxes).back().get_center());
1225 typename bvh::v2::DefaultBuilder<Node>::Config config;
1226 config.quality = bvh::v2::DefaultBuilder<Node>::Quality::High;
1227 auto bvh = bvh::v2::DefaultBuilder<Node>::build(bboxes, centers, config);
1228 auto bvhptr =
new Bvh;
1229 *bvhptr = std::move(
bvh);
1230 fBVH = (
void*)(bvhptr);
1241 using Scalar =
float;
1242 using Vec3 = bvh::v2::Vec<Scalar, 3>;
1243 using Node = bvh::v2::Node<Scalar, 3>;
1244 using Bvh = bvh::v2::Bvh<Node>;
1245 using Ray = bvh::v2::Ray<Scalar, 3>;
1248 auto mybvh = (Bvh*)fBVH;
1254 auto truncate_roundup = [](
double orig) {
1255 float epsilon = std::numeric_limits<float>::epsilon() * std::fabs(orig);
1257 return static_cast<float>(orig + epsilon);
1261 if (!TGeoBBox::Contains(point)) {
1268 Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757};
1270 double local_step = Big();
1272 Ray ray(Vec3(point[0], point[1], point[2]),
1273 Vec3(test_dir[0], test_dir[1], test_dir[2]),
1275 truncate_roundup(local_step));
1277 static constexpr bool use_robust_traversal =
true;
1280 bvh::v2::GrowingStack<Bvh::Index>
stack;
1281 size_t crossings = 0;
1282 mybvh->intersect<
false, use_robust_traversal>(ray, mybvh->get_root().index,
stack, [&](
size_t begin,
size_t end) {
1283 for (
size_t prim_id = begin; prim_id <
end; ++prim_id) {
1284 auto objectid = mybvh->prim_ids[prim_id];
1285 auto& facet = fFacets[objectid];
1288 const auto&
v0 = fVertices[facet[0]];
1289 const auto&
v1 = fVertices[facet[1]];
1290 const auto&
v2 = fVertices[facet[2]];
1292 const double t = rayTriangle(
Vertex_t(point[0], point[1], point[2]),
1293 test_dir,
v0,
v1,
v2, 0.);
1295 if (
t != std::numeric_limits<double>::infinity()) {
1302 return crossings & 1;
1310struct BVHPrioElement {
1320template <
typename Comparator>
1321class BVHPrioQueue :
public std::priority_queue<BVHPrioElement, std::vector<BVHPrioElement>, Comparator>
1324 using std::priority_queue<BVHPrioElement, std::vector<BVHPrioElement>,
1328 void clear() { this->c.clear(); }
1334template <
bool returnFace>
1335inline Double_t O2Tessellated::SafetyKernel(
const Double_t* point,
bool in,
int* closest_facet_id)
const
1339 float smallest_safety_sq = TGeoShape::Big();
1341 using Scalar =
float;
1342 using Vec3 = bvh::v2::Vec<Scalar, 3>;
1343 using Node = bvh::v2::Node<Scalar, 3>;
1344 using Bvh = bvh::v2::Bvh<Node>;
1347 auto mybvh = (Bvh*)fBVH;
1350 Vec3 testpoint(point[0], point[1], point[2]);
1352 auto currnode = mybvh->nodes[0];
1354 bool outside_top =
false;
1360 return std::sqrt(safety_sq_to_top);
1365 auto cmp = [](BVHPrioElement
a, BVHPrioElement
b) {
return a.value >
b.value; };
1366 static thread_local BVHPrioQueue<
decltype(
cmp)> queue(
cmp);
1370 float current_safety_to_node_sq = 0.f;
1373 *closest_facet_id = -1;
1377 if (currnode.is_leaf()) {
1379 const auto begin_prim_id = currnode.index.first_id();
1380 const auto end_prim_id = begin_prim_id + currnode.index.prim_count();
1382 for (
auto p_id = begin_prim_id; p_id < end_prim_id; p_id++) {
1383 const auto object_id = mybvh->prim_ids[p_id];
1385 const auto& facet = fFacets[object_id];
1386 const auto&
v1 = fVertices[facet[0]];
1387 const auto&
v2 = fVertices[facet[1]];
1388 const auto&
v3 = fVertices[facet[2]];
1390 auto thissafetySQ = pointTriangleDistSq(Vec3f{point[0], point[1], point[2]}, Vec3f{
v1[0],
v1[1],
v1[2]},
1391 Vec3f{
v2[0],
v2[1],
v2[2]}, Vec3f{
v3[0],
v3[1],
v3[2]});
1393 if (thissafetySQ < smallest_safety_sq) {
1394 smallest_safety_sq = thissafetySQ;
1396 *closest_facet_id = object_id;
1403 const auto leftchild_id = currnode.index.first_id();
1404 const auto rightchild_id = leftchild_id + 1;
1406 for (
size_t childid : {leftchild_id, rightchild_id}) {
1407 if (childid >= mybvh->nodes.size()) {
1411 const auto&
node = mybvh->nodes[childid];
1416 queue.push(BVHPrioElement{childid, -1.});
1419 if (safety_to_node_square <= smallest_safety_sq) {
1421 queue.push(BVHPrioElement{childid, safety_to_node_square});
1427 if (queue.size() > 0) {
1428 auto currElement = queue.top();
1429 currnode = mybvh->nodes[currElement.bvh_node_id];
1430 current_safety_to_node_sq = currElement.value;
1435 }
while (current_safety_to_node_sq <= smallest_safety_sq);
1437 return std::nextafter(std::sqrt(smallest_safety_sq), 0.0f);
1451 const auto xd =
float(point[0]) - mLast_x;
1452 const auto yd =
float(point[1]) - mLast_y;
1453 const auto zd =
float(point[2]) - mLast_z;
1454 const auto d2 = xd * xd + yd * yd + zd * zd;
1456 if (d2 < mCachedSafety * mCachedSafety) {
1459 return mCachedSafety - std::sqrt(d2);
1464 const auto safety = SafetyKernel<false>(point, in);
1469 mCachedSafety = safety;
1485 int closest_face_id = -1;
1486 SafetyKernel<true>(point,
true, &closest_face_id);
1488 if (closest_face_id < 0) {
1495 const auto&
n = fOutwardNormals[closest_face_id];
1501 if (norm[0] * dir[0] + norm[1] * dir[1] + norm[2] * dir[2] < 0) {
1514 Vertex_t p(point[0], point[1], point[2]);
1517 double dist = Big();
1518 for (
size_t i = 0;
i < fFacets.size(); ++
i) {
1519 const auto& facet = fFacets[
i];
1520 const auto&
n = fOutwardNormals[
i];
1523 if (
n.Dot(
d) <= 0.0) {
1527 const auto&
v0 = fVertices[facet[0]];
1528 const auto&
v1 = fVertices[facet[1]];
1529 const auto&
v2 = fVertices[facet[2]];
1531 const double t = rayTriangle(p,
d,
v0,
v1,
v2, 0.);
1545 Vertex_t p(point[0], point[1], point[2]);
1548 double dist = Big();
1549 for (
size_t i = 0;
i < fFacets.size(); ++
i) {
1550 const auto& facet = fFacets[
i];
1551 const auto&
n = fOutwardNormals[
i];
1554 if (
n.Dot(
d) > 0.0) {
1558 const auto&
v0 = fVertices[facet[0]];
1559 const auto&
v1 = fVertices[facet[1]];
1560 const auto&
v2 = fVertices[facet[2]];
1562 const double t = rayTriangle(p,
d,
v0,
v1,
v2, 0.);
1577 const Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757};
1579 Vertex_t p(point[0], point[1], point[2]);
1582 for (
size_t i = 0;
i < fFacets.size(); ++
i) {
1583 const auto& facet = fFacets[
i];
1585 const auto&
v0 = fVertices[facet[0]];
1586 const auto&
v1 = fVertices[facet[1]];
1587 const auto&
v2 = fVertices[facet[2]];
1589 const double t = rayTriangle(p, test_dir,
v0,
v1,
v2, 0.);
1590 if (
t != std::numeric_limits<double>::infinity()) {
1594 return (crossings & 1);
1601void O2Tessellated::Streamer(TBuffer&
b)
1603 if (
b.IsReading()) {
1604 b.ReadClassBuffer(O2Tessellated::Class(),
this);
1607 b.WriteClassBuffer(O2Tessellated::Class(),
this);
1614void O2Tessellated::CalculateNormals()
1616 fOutwardNormals.clear();
1617 for (
auto& facet : fFacets) {
1618 auto&
v1 = fVertices[facet[0]];
1619 auto&
v2 = fVertices[facet[1]];
1620 auto&
v3 = fVertices[facet[2]];
1621 using Vec3d = Vec3f<double>;
1622 auto norm = triangleNormal(Vec3d{
v1[0],
v1[1],
v1[2]}, Vec3d{
v2[0],
v2[1],
v2[2]}, Vec3d{
v3[0],
v3[1],
v3[2]});
header::DataOrigin origin
std::function< int(const o2::mch::mapping::CathodeSegmentation &, int, rapidjson::Value &)> Comparator
std::unique_ptr< expressions::Node > node
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate increase X h configStandalone Camera window renderer OpenGL display display_camera Orient the camera such that the y axis is always upwards Full Screen MultiSample Anti Downsampling Anti Enable Vertical Sync Target frame or if the backend cannot limit to the refresh Use OpenGL indirect draws to reduce number of draw calls Display h configStandalone Vulkan display display_vulkan Have uniform buffers in host accessible device memory h configStandalone Internal display display_internal Number of updates the Qt GUI may send ahead of the last one the display has before it waits for the display Opacity of the console background Number of console lines the history is shortened to this when it reaches twice the size Time in seconds an info message fades out after setInfoTime h OpenGL display display Font size Do not use Freetype for font Renderer for event Frontend to use for event ROOT macros used as track filter Console script file
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events d
configStandalone Reconstruction rec_tpc multiplier for the combined cluster track error during track following area in cm for the search of for z only used if decrease removal by when above this local occupancy rowx Shift of TPC revert cluster flag edge bit distance to edge exceeds this ZOffset for secondary multiple of radius max P of electron K BB bands crossing max P of electron p BB bands crossing max P of electron d BB bands crossing max P of electron t BB bands crossing min accepted dEdxMaxTPC of the track Additive extra cluster error for Y2 if edge flag set Additive extra cluster error for Y2 if single set Additive extra cluster error for Y2 if or single set Additive extra cluster error for Z2 if or single set Extra errors to add to track when crossing depending on addErrorsCECrossing Inner portion of y range in sector that is not used in searching for extrapolated track candidates factor2YS for track merging factor2K for track merging Margin in cm of Y position when rejecting edge clusters based on uncorrected track Y Max fraction of shared clusters for tracklet Except pad from cluster finding if total number of charges in a fragment is above this Except pad from cluster finding if number of consecutive charges in a fragment is above this Threshold where a timebin is considered disabling the noisy pad check for that pad Treat signal above this minimum as saturated Smoothing factor for the exponential Highly Ionising Particle tail filter Number of timebins to use for the averaging Add additional custom track errors when crossing relative as fraction of Do N fit passes in final fit of< 0:reject at max -n hits") AddOptionRTC(rejectIFCLowRadiusCluster, uint8_t, 1, "", 0, "Reject clusters that get the IFC mask error during refit") AddOptionRTC(dEdxTruncLow, uint8_t, 2, "", 0, "Low truncation threshold, fraction of 128") AddOptionRTC(dEdxTruncHigh, uint8_t, 77, "", 0, "High truncation threshold, fraction of 128") AddOptionRTC(extrapolationTracking, int8_t, 1, "", 0, "Enable Extrapolation Tracking(prolong tracks to adjacent sectors to find short segments)") AddOptionRTC(disableRefitAttachment, uint8_t, 0, "", 0, "Bitmask to disable certain attachment steps during refit(1:attachment, 2:propagation, 4:loop following, 8:mirroring)") AddOptionRTC(rejectionStrategy, uint8_t, o2::gpu::GPUSettings::RejectionStrategyA, "", 0, "Enable rejection of TPC clusters for compression(0=no, 1=strategy A, 2=strategy B)") AddOptionRTC(mergeLoopersAfterburner, uint8_t, 1, "", 0, "Run afterburner for additional looper merging") AddOptionRTC(compressionTypeMask, uint8_t, o2::gpu::GPUSettings::CompressionFull, "", 0, "TPC Compression mode bits(1=truncate charge/width LSB, 2=differences, 4=track-model)") AddOptionRTC(compressionSortOrder, uint8_t, o2::gpu::GPUSettings::SortTime, "", 0, "Sort order of TPC compression(0=time, 1=pad, 2=Z-time-pad, 3=Z-pad-time, 4=no sorting(use incoming order))") AddOptionRTC(sigBitsCharge, uint8_t, 4, "", 0, "Number of significant bits for TPC cluster charge in compression mode 1") AddOptionRTC(sigBitsWidth, uint8_t, 3, "", 0, "Number of significant bits for TPC cluster width in compression mode 1") AddOptionRTC(dropLoopers, uint8_t, 0, "", 0, "Drop looping tracks starting from second loop") AddOptionRTC(mergerInterpolateErrors, uint8_t, 1, "", 0, "Use interpolation instead of extrapolation for chi2 based cluster rejection") AddOptionRTC(mergerInterpolateRejectAlsoOnCurrentPosition, uint8_t, 1, "", 0, "When using mergerInterpolateErrors, reject based on chi2 twice computed with interpolated and current track position starting from NDF > mergerNonInterpolateRejectMinNDF Merge tracks accross the central electrode Enable PID response Add sub threshold clusters in TPC dEdx computation OR mask of TPC flags that will reject the cluster in alternative dEdx Reject edge clusters based on uncorrected track Y during track fit Increase decrease removal by when on this number of pads from the edge h configStandalone Reconstruction rec_trd Max chi2 for TRD tracklets to be matched to a track Chi2 cut for strict matching mode Number of sigmas for ITS TPC track time error estimate Addition to search road around track prolongation along Y in cm Optimal tracklet rphi resolution in maximum chi2 per attached tracklet for TRD tracks take into account the full likelihood of the slope including exponential tails Tracks with less attached tracklets are discarded after the tracking Post trigger Pile up integration time in BCs fits all iterations but the last one in projections Use material lookup table for TPC refit Apply workarounds to avoid cyclic merge should not be needed tpc dyn configStandalone Processing proc_rtc Replace constant variables static by constexpr expressions Compile RTC in deterministic with NO_FAST_MATH flags and GPUCA_DETERMINISTIC_MODE define Use RTC to optimize GPU code h configStandalone Processing proc_rtctech Use a file lock to serialize access to the cache folder Print launch bounds used for RTC code as debugging for exit after Allow RTC with slave GPUReconstruction instances with optConstexpr and optSpecialcode Maximum number of RTC cache files per backend in the cache the oldest are Prepend RTC compilation commands by this string Load a parameter object containing the launch bounds from a file h configStandalone Processing proc_param TPC Cluster Z Error Parameterization h nn
configStandalone Reconstruction rec_tpc float
Tessellated::Vertex_t Vertex_t
void SetSegsAndPols(TBuffer3D &buff) const override
Fills TBuffer3D structure for segments and polygons.
Double_t Safety(const Double_t *point, Bool_t in=kTRUE) const override
Safety.
bool Contains(const Double_t *point) const override
Contains.
void GetMeshNumbers(int &nvert, int &nsegs, int &npols) const override
Returns numbers of vertices, segments and polygons composing the shape mesh.
Double_t DistFromOutside_Loop(const Double_t *point, const Double_t *dir) const
trivial (non-BVH) DistFromOutside function
bool AddFacet(const Vertex_t &pt0, const Vertex_t &pt1, const Vertex_t &pt2)
Adding a triangular facet from vertex positions in absolute coordinates.
bool FacetCheck(int ifacet) const
Check validity of facet.
const TBuffer3D & GetBuffer3D(int reqSections, Bool_t localFrame) const override
Fills a static 3D buffer and returns a reference.
Double_t Capacity() const override
Capacity.
void ComputeBBox() override
Compute bounding box.
void CloseShape(bool check=true, bool fixFlipped=true, bool verbose=true)
Close the shape: calculate bounding box and compact vertices.
static O2Tessellated * ImportFromObjFormat(const char *objfile, bool check=false, bool verbose=false)
Reader from .obj format.
Double_t DistFromInside_Loop(const Double_t *point, const Double_t *dir) const
trivial (non-BVH) DistFromInside function
bool CheckClosure(bool fixFlipped=true, bool verbose=true)
Check closure of the solid and check/fix flipped normals.
void ComputeNormal(const Double_t *point, const Double_t *dir, Double_t *norm) const override
ComputeNormal interface.
void Print(Option_t *option="") const override
Prints basic info.
void ResizeCenter(double maxsize)
Resize and center the shape in a box of size maxsize.
TBuffer3D * MakeBuffer3D() const override
int AddVertex(const Vertex_t &vert)
Add a vertex checking for duplicates, returning the vertex index.
Bool_t GetPointsOnSegments(Int_t npoints, Double_t *array) const override
Fill array with npoints points on this solid's boundary: every vertex, then deterministic R2 samples ...
Vertex_t FacetComputeNormal(int ifacet, bool °enerated) const
Compute normal for a given facet.
bool Contains_Loop(const Double_t *point) const
trivial (non-BVH) Contains
Tessellated::Vertex_t Vertex_t
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 SetPoints(double *points) const override
Fill tessellated points to an array.
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.
GLuint const GLchar * name
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * value
GLfloat GLfloat GLfloat GLfloat v3
GLboolean GLboolean GLboolean GLboolean a
GLubyte GLubyte GLubyte GLubyte w
GLfloat GLfloat GLfloat v2
GLsizei const GLint * box
GLdouble GLdouble GLdouble z
Vec3 operator-(const Vec3 &firstVector, const Vec3 &secondVector)
double dot(const Vec3 &firstVector, const Vec3 &secondVector)
Vec3 cross(const Vec3 &firstVector, const Vec3 &secondVector)
double norm(const Vec3 &vector)
std::variant< OriginValueMatcher, DescriptionValueMatcher, SubSpecificationTypeValueMatcher, std::unique_ptr< DataDescriptorMatcher >, ConstantValueMatcher, StartTimeValueMatcher > Node
void check(const std::vector< std::string > &arguments, const std::vector< ConfigParamSpec > &workflowOptions, const std::vector< DeviceSpec > &deviceSpecs, CheckMatrix &matrix)
VectorOfTObjectPtrs other
char const *restrict const cmp