17#include "TGeoManager.h"
18#include "TGeoMatrix.h"
20#include "TGeoVolume.h"
37using BVHScalar =
float;
38using BVHBBox = bvh::v2::BBox<BVHScalar, 3>;
39using BVHVec3 = bvh::v2::Vec<BVHScalar, 3>;
40using BVHNode = bvh::v2::Node<BVHScalar, 3>;
41using BVH = bvh::v2::Bvh<BVHNode>;
42using BVHRay = bvh::v2::Ray<BVHScalar, 3>;
45constexpr double kBoxTolerance = 1.e-3;
48inline float roundOutward(
double value,
bool up)
50 return std::nextafterf(
static_cast<float>(
value),
51 up ? std::numeric_limits<float>::infinity() : -
std::numeric_limits<
float>::infinity());
55inline float truncateRoundUp(
double value)
57 const float rounded =
static_cast<float>(
value);
58 return rounded <
value ? std::nextafterf(rounded, std::numeric_limits<float>::infinity()) : rounded;
62inline double boxDistanceSq(
const BVHBBox&
box,
const double* point)
65 for (
int dimension = 0; dimension < 3; ++dimension) {
66 const double lower =
static_cast<double>(
box.min[dimension]);
67 const double upper =
static_cast<double>(
box.max[dimension]);
82constexpr double kSafetyBoundShare = 1. / 3.;
84inline bool boxContains(
const BVHBBox&
box,
const double* point)
86 return point[0] >=
static_cast<double>(
box.min[0]) && point[0] <=
static_cast<double>(
box.max[0]) &&
87 point[1] >=
static_cast<double>(
box.min[1]) && point[1] <=
static_cast<double>(
box.max[1]) &&
88 point[2] >=
static_cast<double>(
box.min[2]) && point[2] <=
static_cast<double>(
box.max[2]);
98constexpr unsigned kSmallStackCapacity = 64;
101template <
typename T,
typename Traverse>
102auto withTraversalStack(
int treeDepth, Traverse&& traverse)
104 if (treeDepth + 2 <=
static_cast<int>(kSmallStackCapacity)) {
105 bvh::v2::SmallStack<T, kSmallStackCapacity>
stack;
106 return traverse(
stack);
108 bvh::v2::GrowingStack<T>
stack;
109 return traverse(
stack);
117 if (volume !=
nullptr) {
124 delete static_cast<BVH*
>(fBVH);
130 const auto*
bvh =
static_cast<const BVH*
>(fBVH);
131 if (
bvh ==
nullptr) {
134 return bvh->nodes.size() *
sizeof(BVHNode) +
bvh->prim_ids.size() *
sizeof(size_t);
142 delete static_cast<BVH*
>(fBVH);
146 if (fVolume ==
nullptr) {
150 const int nDaughters = fVolume->GetNdaughters();
151 fNbuilt = nDaughters;
152 if (nDaughters == 0) {
156 std::vector<BVHBBox> boxes;
157 std::vector<BVHVec3> centers;
158 boxes.reserve(nDaughters);
159 centers.reserve(nDaughters);
164 TGeoNode*
node = fVolume->GetNode(
index);
165 TGeoShape* shape =
node->GetVolume()->GetShape();
168 if (
node->GetVolume()->IsAssembly() || TGeoShape::IsSameWithinTolerance(((
TGeoBBox*)shape)->GetDX(), 0.)) {
169 shape->ComputeBBox();
171 ((
TGeoBBox*)shape)->SetBoxPoints(corners);
172 double lower[3] = {TGeoShape::Big(), TGeoShape::Big(), TGeoShape::Big()};
173 double upper[3] = {-TGeoShape::Big(), -TGeoShape::Big(), -TGeoShape::Big()};
174 for (
int corner = 0; corner < 8; ++corner) {
175 node->LocalToMaster(&corners[3 * corner], master);
176 for (
int dimension = 0; dimension < 3; ++dimension) {
177 lower[dimension] = std::min(
lower[dimension], master[dimension]);
178 upper[dimension] = std::max(
upper[dimension], master[dimension]);
182 for (
int dimension = 0; dimension < 3; ++dimension) {
183 box.min[dimension] = roundOutward(
lower[dimension] - kBoxTolerance,
false);
184 box.max[dimension] = roundOutward(
upper[dimension] + kBoxTolerance,
true);
186 boxes.push_back(
box);
187 centers.emplace_back(
box.get_center());
190 typename bvh::v2::DefaultBuilder<BVHNode>::Config config;
191 config.quality = bvh::v2::DefaultBuilder<BVHNode>::Quality::High;
193 config.max_leaf_size = 1;
194 auto* built =
new BVH(bvh::v2::DefaultBuilder<BVHNode>::build(boxes, centers, config));
195 fBVH =
static_cast<void*
>(built);
198 std::vector<std::pair<size_t, int>> pending{{0, 1}};
199 while (!pending.empty()) {
202 fTreeDepth = std::max(fTreeDepth,
level);
204 if (!
node.is_leaf()) {
205 const size_t firstChild =
node.index.first_id();
206 for (
size_t child : {firstChild, firstChild + 1}) {
207 if (child < built->
nodes.size()) {
208 pending.push_back({child,
level + 1});
215void O2BVHAssembly::EnsureBuilt()
const
217 const int nDaughters = fVolume !=
nullptr ? fVolume->GetNdaughters() : 0;
218 if (fNbuilt == nDaughters && (fBVH !=
nullptr || nDaughters == 0)) {
233 if (!TGeoBBox::Contains(point)) {
236 const auto*
bvh =
static_cast<const BVH*
>(fBVH);
237 if (
bvh ==
nullptr) {
242 withTraversalStack<size_t>(fTreeDepth, [&](
auto&
stack) {
245 while (!
stack.is_empty()) {
246 const auto& node = bvh->nodes[stack.pop()];
247 if (!boxContains(node.get_bbox(), point)) {
250 if (
node.is_leaf()) {
251 const auto beginPrimitive = node.index.first_id();
252 const auto endPrimitive = beginPrimitive + node.index.prim_count();
253 for (auto primitive = beginPrimitive; primitive < endPrimitive; ++primitive) {
254 const int daughter = static_cast<int>(bvh->prim_ids[primitive]);
257 if (best >= 0 && daughter > best) {
260 TGeoNode* geoNode = fVolume->GetNode(daughter);
261 geoNode->MasterToLocal(point, local);
262 if (geoNode->GetVolume()->GetShape()->Contains(local)) {
267 const auto firstChild =
node.index.first_id();
268 for (
size_t child : {firstChild, firstChild + 1}) {
269 if (child < bvh->
nodes.size()) {
281 fVolume->SetCurrentNodeIndex(best);
282 fVolume->SetNextNodeIndex(best);
291 if (!TGeoBBox::Contains(point)) {
295 const int nDaughters = fVolume !=
nullptr ? fVolume->GetNdaughters() : 0;
297 TGeoNode* geoNode = fVolume->GetNode(
index);
298 geoNode->MasterToLocal(point, local);
299 if (geoNode->GetVolume()->GetShape()->Contains(local)) {
300 fVolume->SetCurrentNodeIndex(
index);
301 fVolume->SetNextNodeIndex(
index);
312 Double_t* safe)
const
318 if (iact < 3 && safe !=
nullptr) {
319 *safe =
Safety(point, kFALSE);
321 return TGeoShape::Big();
323 if (iact == 1 && step <= *safe) {
324 return TGeoShape::Big();
327 const auto*
bvh =
static_cast<const BVH*
>(fBVH);
328 if (
bvh ==
nullptr) {
329 return TGeoShape::Big();
332 double best = TGeoShape::Big();
334 BVHRay ray(BVHVec3(
static_cast<float>(point[0]),
static_cast<float>(point[1]),
static_cast<float>(point[2])),
335 BVHVec3(
static_cast<float>(dir[0]),
static_cast<float>(dir[1]),
static_cast<float>(dir[2])), 0.f,
336 truncateRoundUp(step + kBoxTolerance));
337 static constexpr bool useRobustTraversal =
true;
338 auto* volume = fVolume;
339 withTraversalStack<BVH::Index>(fTreeDepth, [&](
auto&
stack) {
340 bvh->intersect<
false, useRobustTraversal>(
341 ray,
bvh->get_root().
index,
stack, [&](
size_t beginPrimitive,
size_t endPrimitive) {
344 for (
size_t primitive = beginPrimitive; primitive < endPrimitive; ++primitive) {
345 const int daughter =
static_cast<int>(
bvh->prim_ids[primitive]);
346 TGeoNode* geoNode = volume->GetNode(daughter);
347 geoNode->MasterToLocal(point, local);
348 geoNode->MasterToLocalVect(dir, localDir);
349 const double distance = geoNode->GetVolume()->GetShape()->DistFromOutside(local, localDir, 3, step);
352 bestIndex = daughter;
353 }
else if (
distance == best && daughter < bestIndex) {
354 bestIndex = daughter;
359 if (bestIndex >= 0) {
360 ray.tmax = std::min(ray.tmax, truncateRoundUp(best + kBoxTolerance));
366 if (bestIndex < 0 || best >= step) {
367 return TGeoShape::Big();
369 volume->SetNextNodeIndex(bestIndex);
378 double best = TGeoShape::Big();
382 const int nDaughters = fVolume !=
nullptr ? fVolume->GetNdaughters() : 0;
384 TGeoNode* geoNode = fVolume->GetNode(
index);
385 geoNode->MasterToLocal(point, local);
386 geoNode->MasterToLocalVect(dir, localDir);
387 const double distance = geoNode->GetVolume()->GetShape()->DistFromOutside(local, localDir, 3, step);
393 if (bestIndex < 0 || best >= step) {
394 return TGeoShape::Big();
396 fVolume->SetNextNodeIndex(bestIndex);
406 return TGeoShapeAssembly::Safety(point, in);
412 const auto*
bvh =
static_cast<const BVH*
>(fBVH);
413 if (
bvh ==
nullptr) {
414 return TGeoShape::Big();
417 return withTraversalStack<SafetyEntry>(fTreeDepth, [&](
auto&
stack) {
418 double best = TGeoShape::Big();
419 stack.push({boxDistanceSq(
bvh->nodes[0].get_bbox(), point), size_t(0)});
420 while (!
stack.is_empty()) {
422 if (
entry.distanceSq * kSafetyBoundShare >= best * best) {
426 if (
node.is_leaf()) {
427 const auto beginPrimitive =
node.index.first_id();
428 const auto endPrimitive = beginPrimitive +
node.index.prim_count();
429 for (
auto primitive = beginPrimitive; primitive < endPrimitive; ++primitive) {
430 const int daughter =
static_cast<int>(
bvh->prim_ids[primitive]);
431 const double safety = fVolume->GetNode(daughter)->Safety(point, kFALSE);
435 best = std::min(best, safety);
438 const auto firstChild =
node.index.first_id();
439 const size_t children[2] = {firstChild, firstChild + 1};
440 double distancesSq[2] = {TGeoShape::Big(), TGeoShape::Big()};
442 if (children[
side] <
bvh->nodes.size()) {
443 distancesSq[
side] = boxDistanceSq(
bvh->nodes[children[
side]].get_bbox(), point);
447 const bool leftIsFarther = distancesSq[0] >= distancesSq[1];
448 const int order[2] = {leftIsFarther ? 0 : 1, leftIsFarther ? 1 : 0};
450 const int which = order[
side];
451 if (children[which] <
bvh->nodes.size() && distancesSq[which] * kSafetyBoundShare < best * best) {
452 stack.push({distancesSq[which], children[which]});
464 return TGeoShapeAssembly::Safety(point, in);
466 double best = TGeoShape::Big();
467 const int nDaughters = fVolume !=
nullptr ? fVolume->GetNdaughters() : 0;
469 const double safety = fVolume->GetNode(
index)->Safety(point, kFALSE);
473 best = std::min(best, safety);
483 if (volume ==
nullptr) {
487 volume->SetShape(shape);
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 coordinate
configStandalone Reconstruction rec_tpc float
static O2BVHAssembly * MakeBVHAssembly(TGeoVolumeAssembly *volume)
Replace volume's shape by an O2BVHAssembly and return it.
Double_t DistFromOutside_Loop(const Double_t *point, const Double_t *dir, Double_t step=TGeoShape::Big()) const
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 – daughters are queried with the fixed query bound, so the answer is visit-order inde...
void BuildBVH()
(Re)build the acceleration structure from the volume's current daughter list.
size_t GetBVHMemory() const
Bytes held by the BVH nodes and the primitive-index permutation.
Double_t Safety(const Double_t *point, Bool_t in=kTRUE) const override
Safety – from inside as ROOT; from outside a nearest-daughter descent of the BVH.
Double_t Safety_Loop(const Double_t *point, Bool_t in=kTRUE) const
Bool_t Contains_Loop(const Double_t *point) const
Bool_t Contains(const Double_t *point) const override
Contains.
~O2BVHAssembly() override
GLsizei GLsizei GLfloat distance
GLsizei const GLfloat * value
GLsizei const GLint * box
double distanceSq(const Vec2 &firstPoint, const Vec2 &secondPoint)