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 = 1e-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]);
1515 Vertex_t d(dir[0], dir[1], dir[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]);
1546 Vertex_t d(dir[0], dir[1], dir[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
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