42constexpr int kMaxRootsPerHalfspace = 4;
45constexpr int kMaxCubifySplits = 10;
48int maxPairsForCell(
int halfspaceCount)
50 return 2 + kMaxRootsPerHalfspace * halfspaceCount;
54using BVHScalar =
float;
55using BVHBBox = bvh::v2::BBox<BVHScalar, 3>;
56using BVHVec3 = bvh::v2::Vec<BVHScalar, 3>;
57using BVHNode = bvh::v2::Node<BVHScalar, 3>;
58using BVH = bvh::v2::Bvh<BVHNode>;
61thread_local long long gUnprunedRetryCount = 0;
64inline float roundOutward(
double value,
bool up)
66 return std::nextafterf(
static_cast<float>(
value), up ? std::numeric_limits<float>::infinity()
67 : -
std::numeric_limits<
float>::infinity());
72bool slabWindow(
const double* boxMin,
const double* boxMax,
const double*
origin,
const double* dir,
73 double& tlo,
double& thi)
76 if (std::abs(dir[
index]) < 1.e-300) {
89 tlo = std::max(tlo, low);
90 thi = std::min(thi, high);
99inline bool nodeWindow(
const BVHBBox&
box,
const double*
origin,
const double* dir,
double& tlo,
102 const double lo[3] = {
box.min[0],
box.min[1],
box.min[2]};
103 const double hi[3] = {
box.max[0],
box.max[1],
box.max[2]};
104 return slabWindow(lo, hi,
origin, dir, tlo, thi);
108inline bool boxHoldsPoint(
const FlatCSGBox&
box,
const double* point)
110 return point[0] >=
box.min[0] && point[0] <=
box.max[0] && point[1] >=
box.min[1] &&
111 point[1] <=
box.max[1] && point[2] >=
box.min[2] && point[2] <=
box.max[2];
115void halfspaceGradient(
const FlatCSGHalfspace& halfspace,
const double* point,
double grad[3])
118 const double*
c = halfspace.c;
119 const double axis[3] = {
c[3],
c[4],
c[5]};
120 const double major =
c[6];
121 const double offset[3] = {point[0] -
c[0], point[1] -
c[1], point[2] -
c[2]};
127 const double rho = std::sqrt(radial[0] * radial[0] + radial[1] * radial[1] + radial[2] * radial[2]);
129 const double s = std::hypot(u, along);
130 if (
s < 1.e-300 || rho < 1.e-300) {
132 grad[0] = grad[1] = grad[2] = 0.;
135 const double du = u /
s;
136 const double dv = along /
s;
142 const double*
c = halfspace.c;
143 const double a[3][3] = {{
c[0],
c[1],
c[2]}, {
c[1],
c[3],
c[4]}, {
c[2],
c[4],
c[5]}};
144 const double b[3] = {
c[6],
c[7],
c[8]};
147 for (
int column = 0; column < 3; ++column) {
150 grad[
row] = halfspace.sign * 2. *
value;
157template <
typename Visit>
158void traverseRay(
const BVH&
bvh,
const double*
origin,
const double* dir,
double cap,
const double& tmax,
159 bool nearFirst,
double* culled, Visit&& visit)
167 thread_local std::vector<Entry>
stack;
169 const auto entersWithin = [&](
size_t index,
double& tlo) {
172 return nodeWindow(
bvh.nodes[
index].get_bbox(),
origin, dir, tlo, thi);
175 const auto skip = [&](
double tlo) {
176 if (culled !=
nullptr && tlo < *culled) {
181 if (entersWithin(0, rootTlo)) {
182 stack.push_back({0, rootTlo});
184 while (!
stack.empty()) {
187 if (
entry.tlo > tmax) {
192 if (
node.is_leaf()) {
193 const auto beginPrimitive =
node.index.first_id();
194 const auto endPrimitive = beginPrimitive +
node.index.prim_count();
195 for (
auto primitive = beginPrimitive; primitive < endPrimitive; ++primitive) {
196 visit(
static_cast<int>(
bvh.prim_ids[primitive]));
199 const auto firstChild =
node.index.first_id();
202 for (
size_t child : {firstChild, firstChild + 1}) {
204 if (child <
bvh.nodes.size() && entersWithin(child, tlo)) {
208 children[
count++] = {child, tlo};
213 if (nearFirst &&
count == 2 && children[0].tlo < children[1].tlo) {
214 std::swap(children[0], children[1]);
224template <
typename Visit>
225bool traversePoint(
const BVH&
bvh,
const double* point, Visit&& visit)
227 const BVHVec3 query(
static_cast<float>(point[0]),
static_cast<float>(point[1]),
228 static_cast<float>(point[2]));
229 thread_local std::vector<size_t>
stack;
232 while (!
stack.empty()) {
233 const size_t current =
stack.back();
235 const auto&
node =
bvh.nodes[current];
236 if (!bvh::v2::extra::contains(
node.get_bbox(), query)) {
239 if (
node.is_leaf()) {
240 const auto beginPrimitive =
node.index.first_id();
241 const auto endPrimitive = beginPrimitive +
node.index.prim_count();
242 for (
auto primitive = beginPrimitive; primitive < endPrimitive; ++primitive) {
243 if (visit(
static_cast<int>(
bvh.prim_ids[primitive]))) {
248 const auto firstChild =
node.index.first_id();
249 for (
size_t child : {firstChild, firstChild + 1}) {
250 if (child <
bvh.nodes.size()) {
251 stack.push_back(child);
260inline double boxDistanceSquared(
const FlatCSGBox&
box,
const double* point)
275inline double distanceToFaces(
const FlatCSGBox&
box,
const double* point)
277 double toFace = TGeoShape::Big();
291 delete static_cast<BVH*
>(
fBVH);
297 const auto*
bvh =
static_cast<const BVH*
>(
fBVH);
298 if (
bvh ==
nullptr) {
301 return bvh->nodes.size() *
sizeof(BVHNode) +
bvh->prim_ids.size() *
sizeof(size_t);
308 halfspace.
sign = sign < 0. ? -1. : 1.;
321 halfspace.
sign = sign < 0. ? -1. : 1.;
323 const double axisNorm = std::sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
324 assert(axisNorm > 0. &&
"O2FlatCSG::AddTorus: axis must not be the zero vector");
342 return static_cast<int>(
fCells.size()) - 1;
358 Error(
"SetCellBBox",
"Shape %s: cell %d is out of range (%d cell(s) so far); ignoring",
372 const bool set = cell >= 0 && cell < GetNcells() && static_cast<size_t>(cell) <
fCellBBoxSet.size() &&
383 const double*
c = halfspace.
c;
384 const double offset[3] = {point[0] -
c[0], point[1] -
c[1], point[2] -
c[2]};
386 const double radial[3] = {
offset[0] - along *
c[3],
offset[1] - along *
c[4],
388 const double rho = std::sqrt(radial[0] * radial[0] + radial[1] * radial[1] +
389 radial[2] * radial[2]);
391 return halfspace.
sign * (std::hypot(rho -
c[6], along) -
c[7]);
393 const double*
c = halfspace.
c;
394 const double x = point[0];
395 const double y = point[1];
396 const double z = point[2];
397 const double quadratic =
c[0] *
x *
x +
c[3] *
y *
y +
c[5] *
z *
z +
398 2. * (
c[1] *
x *
y +
c[2] *
x *
z +
c[4] *
y *
z);
399 const double linear = 2. * (
c[6] *
x +
c[7] *
y +
c[8] *
z);
400 return halfspace.
sign * (quadratic + linear +
c[9]);
404 const double* hi,
double& rangeLo,
double& rangeHi)
407 assert(std::isfinite(lo[0]) && std::isfinite(lo[1]) && std::isfinite(lo[2]) &&
408 std::isfinite(hi[0]) && std::isfinite(hi[1]) && std::isfinite(hi[2]) &&
409 lo[0] <= hi[0] && lo[1] <= hi[1] && lo[2] <= hi[2] &&
410 "O2FlatCSG::HalfspaceRange: lo/hi must be finite and lo[i] <= hi[i] on every axis");
421 constexpr double kPadFactor = 64. * std::numeric_limits<double>::epsilon();
428 const double*
c = halfspace.
c;
429 const double offset[3] = {centre[0] -
c[0], centre[1] -
c[1], centre[2] -
c[2]};
431 const double radial[3] = {
offset[0] - along *
c[3],
offset[1] - along *
c[4],
433 const double rho = std::sqrt(radial[0] * radial[0] + radial[1] * radial[1] +
434 radial[2] * radial[2]);
436 mag = rho + std::abs(
c[6]) + std::abs(along) + std::abs(
c[7]);
438 const double*
c = halfspace.
c;
439 const double a[3][3] = {{
c[0],
c[1],
c[2]}, {
c[1],
c[3],
c[4]}, {
c[2],
c[4],
c[5]}};
440 const double b[3] = {
c[6],
c[7],
c[8]};
442 mag = std::abs(
c[9]);
444 double gradient =
b[
row];
445 mag += 2. * std::abs(
b[
row] * centre[
row]);
446 for (
int column = 0; column < 3; ++column) {
447 gradient +=
a[
row][column] * centre[column];
450 mag += std::abs(
a[
row][column] * centre[
row] * centre[column]);
452 slack += 2. * std::abs(gradient) *
half[
row];
458 halfWidth += kPadFactor * mag;
459 rangeLo = middle - halfWidth;
460 rangeHi = middle + halfWidth;
478 std::vector<int> stillActive;
479 stillActive.reserve(
active.size());
480 for (
int halfspace :
active) {
488 stillActive.push_back(halfspace);
494 double shortest = TGeoShape::Big();
498 if (extent > longest) {
502 shortest = std::min(shortest, extent);
506 const bool farFromCubic = longest > 2. * std::max(shortest,
minSize);
507 const bool keep = stillActive.empty() ||
depth <= 0 || longest <=
minSize ||
508 (farFromCubic && cubifyBudget <= 0);
516 box.firstActive =
static_cast<int>(
fActive.size());
517 box.nActive =
static_cast<int>(stillActive.size());
518 fActive.insert(
fActive.end(), stillActive.begin(), stillActive.end());
523 const int childDepth = farFromCubic ?
depth :
depth - 1;
524 const int childCubifyBudget = farFromCubic ? cubifyBudget - 1 : cubifyBudget;
525 const double middle = 0.5 * (lo[axis] + hi[axis]);
526 double childLo[3] = {lo[0], lo[1], lo[2]};
527 double childHi[3] = {hi[0], hi[1], hi[2]};
528 childHi[axis] = middle;
529 SplitBox(cell, childLo, childHi, stillActive, childDepth,
minSize, childCubifyBudget);
530 childHi[axis] = hi[axis];
531 childLo[axis] = middle;
532 SplitBox(cell, childLo, childHi, stillActive, childDepth,
minSize, childCubifyBudget);
542 delete static_cast<BVH*
>(
fBVH);
547 bool anyProblem =
false;
548 for (
int cell = 0; cell <
GetNcells(); ++cell) {
551 "Shape %s cell %d has no bounding box (SetCellBBox was never called for it); it would "
552 "silently vanish from the solid. Not building any boxes -- IsClosed() stays false.",
560 if (!std::isfinite(loValue) || !std::isfinite(hiValue)) {
562 "Shape %s cell %d has a non-finite bounding box on axis %d (lo %g, hi %g). Not "
563 "building any boxes -- IsClosed() stays false.",
564 GetName(), cell,
index, loValue, hiValue);
568 if (hiValue < loValue) {
570 "Shape %s cell %d has an inverted bounding box on axis %d (lo %g > hi %g); "
571 "SetCellBBox's arguments look swapped. Not building any boxes -- IsClosed() stays "
573 GetName(), cell,
index, loValue, hiValue);
582 double partLo[3] = {TGeoShape::Big(), TGeoShape::Big(), TGeoShape::Big()};
583 double partHi[3] = {-TGeoShape::Big(), -TGeoShape::Big(), -TGeoShape::Big()};
584 for (
int cell = 0; cell <
GetNcells(); ++cell) {
590 const double diagonal = std::sqrt((partHi[0] - partLo[0]) * (partHi[0] - partLo[0]) +
591 (partHi[1] - partLo[1]) * (partHi[1] - partLo[1]) +
592 (partHi[2] - partLo[2]) * (partHi[2] - partLo[2]));
598 const double reach = 1.e-6 * (diagonal > 0. ? diagonal : 1.);
599 for (
int cell = 0; cell <
GetNcells(); ++cell) {
600 const double* cellLo = &
fCellLo[3 * cell];
601 const double* cellHi = &
fCellHi[3 * cell];
602 for (
int axis = 0; axis < 3; ++axis) {
603 const int first = (axis + 1) % 3;
604 const int second = (axis + 2) % 3;
606 for (
int step1 = 0; step1 <= 4; ++step1) {
607 for (
int step2 = 0; step2 <= 4; ++step2) {
609 probe[axis] =
side == 0 ? cellLo[axis] - reach : cellHi[axis] + reach;
611 probe[second] = cellLo[second] + 0.25 * step2 * (cellHi[second] - cellLo[second]);
613 "O2FlatCSG::CloseShape: a cell reaches past the bounding box SetCellBBox was "
614 "given, so this shape and its own _Loop twins answer differently out there. "
615 "The converter's box is the CAD piece's own bbox, so the cell is larger than "
616 "the part: close the cell's halfspaces or refuse the part -- do NOT widen "
617 "the box, which would ship the phantom material");
626 for (
int cell = 0; cell <
GetNcells(); ++cell) {
637 std::vector<BVHBBox> boxes;
638 std::vector<BVHVec3> centers;
639 boxes.reserve(
fBoxes.size());
640 centers.reserve(
fBoxes.size());
649 boxes.push_back(bounds);
650 centers.emplace_back(bounds.get_center());
652 typename bvh::v2::DefaultBuilder<BVHNode>::Config config;
653 config.quality = bvh::v2::DefaultBuilder<BVHNode>::Quality::High;
655 config.max_leaf_size = 1;
656 fBVH =
static_cast<void*
>(
657 new BVH(bvh::v2::DefaultBuilder<BVHNode>::build(boxes, centers, config)));
683 std::vector<int> boundaryBoxes;
686 boundaryBoxes.push_back(
index);
689 if (boundaryBoxes.empty()) {
693 constexpr double kAlpha1 = 0.7548776662466927;
694 constexpr double kAlpha2 = 0.5698402909980532;
695 constexpr double kFlipProbe = 1.e-6;
696 const double zAxis[3] = {0., 0., 1.};
697 std::vector<double> pairs;
699 const long long maxAttempts = 64LL * npoints;
700 for (
long long attempt = 0; attempt < maxAttempts && produced < npoints; ++attempt) {
701 const FlatCSGBox&
box =
fBoxes[boundaryBoxes[attempt %
static_cast<long long>(boundaryBoxes.size())]];
702 const double u = std::fmod(0.5 + kAlpha1 *
static_cast<double>(attempt + 1), 1.);
703 const double v = std::fmod(0.5 + kAlpha2 *
static_cast<double>(attempt + 1), 1.);
704 const double cosTheta = 1. - 2. * u;
705 const double sinTheta = std::sqrt(std::max(0., 1. - cosTheta * cosTheta));
707 const double dir[3] = {sinTheta * std::cos(phi), sinTheta * std::sin(phi), cosTheta};
708 const double centre[3] = {0.5 * (
box.min[0] +
box.max[0]), 0.5 * (
box.min[1] +
box.max[1]),
709 0.5 * (
box.min[2] +
box.max[2])};
711 double thi = TGeoShape::Big();
712 if (!slabWindow(
box.min,
box.max, centre, dir, tlo, thi)) {
715 const int capacity = maxPairsForCell(
box.nActive);
716 pairs.resize(2 *
static_cast<size_t>(capacity));
718 pairs.data(), capacity);
720 double crossing = -1.;
721 for (
int pair = 0; pair < found && crossing < 0.; ++pair) {
722 if (pairs[2 * pair] > tlo) {
723 crossing = pairs[2 * pair];
724 }
else if (pairs[2 * pair + 1] < thi) {
725 crossing = pairs[2 * pair + 1];
731 double* slot = &
array[3 *
static_cast<size_t>(produced)];
732 for (
int axis = 0; axis < 3; ++axis) {
733 slot[axis] = centre[axis] + crossing * dir[axis];
736 double normal[3] = {0., 0., 0.};
740 for (
int axis = 0; axis < 3; ++axis) {
741 below[axis] = slot[axis] - kFlipProbe * normal[axis];
742 above[axis] = slot[axis] + kFlipProbe * normal[axis];
748 return produced == npoints ? kTRUE : kFALSE;
757 const bool inside = traversePoint(*
static_cast<const BVH*
>(
fBVH), point, [&](
int index) {
759 if (!boxHoldsPoint(
box, point)) {
762 if (
box.nActive == 0) {
766 for (
int slot = 0; slot <
box.nActive && inCell; ++slot) {
771 return inside ? kTRUE : kFALSE;
775 const double* dir,
double* roots)
780 assert(std::abs(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2] - 1.) < 1.e-9 &&
781 "O2FlatCSG::HalfspaceRoots: torus branch requires a unit direction");
782 const double*
c = halfspace.
c;
783 const double axis[3] = {
c[3],
c[4],
c[5]};
784 const double major =
c[6];
785 const double minor =
c[7];
789 const double dz = dir[0] * axis[0] + dir[1] * axis[1] + dir[2] * axis[2];
796 const double pp = pPerp[0] * pPerp[0] + pPerp[1] * pPerp[1] + pPerp[2] * pPerp[2];
797 const double dd = dPerp[0] * dPerp[0] + dPerp[1] * dPerp[1] + dPerp[2] * dPerp[2];
798 const double pd = pPerp[0] * dPerp[0] + pPerp[1] * dPerp[1] + pPerp[2] * dPerp[2];
801 const double f = pd + pz * dz;
802 const double a4 = 1.;
803 const double a3 = 4. *
f;
804 const double a2 = 2. *
e + 4. *
f *
f - 4. *
major *
major * dd;
810 for (
double value : found) {
811 if (
count < kMaxRootsPerHalfspace) {
817 const double*
c = halfspace.
c;
819 const double ad[3] = {
c[0] * dir[0] +
c[1] * dir[1] +
c[2] * dir[2],
820 c[1] * dir[0] +
c[3] * dir[1] +
c[4] * dir[2],
821 c[2] * dir[0] +
c[4] * dir[1] +
c[5] * dir[2]};
826 const double alpha = dir[0] * ad[0] + dir[1] * ad[1] + dir[2] * ad[2];
827 const double beta = dir[0] * aob[0] + dir[1] * aob[1] + dir[2] * aob[2];
832 const double reference = std::abs(beta) + std::abs(gamma) + 1.e-300;
834 if (std::abs(beta) <= 1.e-300) {
837 roots[0] = -0.5 * gamma / beta;
840 const double disc = beta * beta -
alpha * gamma;
844 const double root = std::sqrt(disc);
846 const double q = -(beta + (beta >= 0. ? root : -root));
852 roots[0] = q /
alpha;
853 roots[1] = gamma / q;
858 const double* dir,
double tlo,
double thi,
double* out,
868 thread_local std::vector<double> breakBuffer;
869 const std::size_t needed = 2 +
static_cast<std::size_t
>(kMaxRootsPerHalfspace) *
static_cast<std::size_t
>(
count);
870 if (breakBuffer.size() < needed) {
871 breakBuffer.resize(needed);
873 double* breaks = breakBuffer.data();
875 breaks[nBreaks++] = tlo;
876 breaks[nBreaks++] = thi;
877 for (
int slot = 0; slot <
count; ++slot) {
879 double roots[kMaxRootsPerHalfspace];
881 for (
int root = 0; root < found; ++root) {
882 if (roots[root] > tlo && roots[root] < thi) {
883 breaks[nBreaks++] = roots[root];
887 std::sort(breaks, breaks + nBreaks);
894 const double lo = breaks[
index];
895 const double hi = breaks[
index + 1];
899 const double middle = 0.5 * (lo + hi);
900 double probe[3] = {
origin[0] + middle * dir[0],
origin[1] + middle * dir[1],
901 origin[2] + middle * dir[2]};
903 for (
int slot = 0; slot <
count && inside; ++slot) {
909 out[2 * (pairs - 1) + 1] = hi;
910 }
else if (pairs < maxOut) {
912 out[2 * pairs + 1] = hi;
931int mergeIntervals(
double* pairs,
int count,
double glue)
937 for (
int outer = 1; outer <
count; ++outer) {
938 const double lo = pairs[2 * outer];
939 const double hi = pairs[2 * outer + 1];
940 int inner = outer - 1;
941 while (inner >= 0 && pairs[2 * inner] > lo) {
942 pairs[2 * (inner + 1)] = pairs[2 * inner];
943 pairs[2 * (inner + 1) + 1] = pairs[2 * inner + 1];
946 pairs[2 * (inner + 1)] = lo;
947 pairs[2 * (inner + 1) + 1] = hi;
951 if (pairs[2 *
index] <= pairs[2 * (
kept - 1) + 1] + glue) {
952 pairs[2 * (
kept - 1) + 1] = std::max(pairs[2 * (
kept - 1) + 1], pairs[2 *
index + 1]);
955 pairs[2 *
kept + 1] = pairs[2 *
index + 1];
967 thread_local std::vector<double> pairBuffer;
968 double best = TGeoShape::Big();
969 for (
int cell = 0; cell <
GetNcells(); ++cell) {
971 const int capacity = maxPairsForCell(
fCells[cell].
count);
972 if (
static_cast<int>(pairBuffer.size()) < 2 * capacity) {
973 pairBuffer.resize(2 * capacity);
975 const int found =
CellIntervals(cell,
nullptr, -1, point, dir, 0., step,
976 pairBuffer.data(), capacity);
979 for (
int pair = 0; pair < found; ++pair) {
981 if (pairBuffer[2 * pair + 1] > TGeoShape::Tolerance() && pairBuffer[2 * pair] < best) {
982 best = std::max(pairBuffer[2 * pair], 0.);
993 thread_local std::vector<double> pairBuffer;
994 int totalCapacity = 0;
995 for (
int cell = 0; cell <
GetNcells(); ++cell) {
996 totalCapacity += maxPairsForCell(
fCells[cell].
count);
998 if (
static_cast<int>(pairBuffer.size()) < 2 * totalCapacity) {
999 pairBuffer.resize(2 * totalCapacity);
1002 for (
int cell = 0; cell <
GetNcells(); ++cell) {
1004 const int found =
CellIntervals(cell,
nullptr, -1, point, dir, 0., step,
1005 pairBuffer.data() + 2 *
count, totalCapacity -
count);
1007 Error(
"DistFromInside_Loop",
1008 "CellIntervals overflowed for cell %d: the maxPairsForCell bound no longer holds",
1010 return TGeoShape::Big();
1014 count = mergeIntervals(pairBuffer.data(),
count, TGeoShape::Tolerance());
1015 for (
int pair = 0; pair <
count; ++pair) {
1016 if (pairBuffer[2 * pair] <= TGeoShape::Tolerance()) {
1017 return pairBuffer[2 * pair + 1];
1027 std::vector<double>& pairs, std::vector<int>&
cells,
RayBound bound,
1028 double& smallestPruned)
const
1032 smallestPruned = TGeoShape::Big();
1033 const BVH&
bvh = *
static_cast<const BVH*
>(
fBVH);
1036 thread_local std::vector<double> boxPairs;
1037 bool overflowed =
false;
1040 double limit = step;
1043 double* culled = bound ==
RayBound::kExit ? &smallestPruned :
nullptr;
1048 if (!slabWindow(
box.min,
box.max, point, dir, tlo, thi) || thi <= tlo) {
1052 if (culled !=
nullptr && tlo < smallestPruned) {
1053 smallestPruned = tlo;
1059 const int capacity = maxPairsForCell(
box.nActive);
1060 if (
static_cast<int>(boxPairs.size()) < 2 * capacity) {
1061 boxPairs.resize(2 * capacity);
1067 boxPairs.data(), capacity);
1072 for (
int pair = 0; pair < found; ++pair) {
1073 const double enter = boxPairs[2 * pair];
1074 const double exit = boxPairs[2 * pair + 1];
1075 pairs.push_back(enter);
1076 pairs.push_back(exit);
1079 limit = std::min(limit, std::max({enter, 0., TGeoShape::Tolerance()}));
1081 (reach < 0. ? enter <= TGeoShape::Tolerance() : enter <= reach + TGeoShape::Tolerance())) {
1083 reach = std::max(reach, exit);
1084 limit = std::min(step, reach + TGeoShape::Tolerance());
1095 Double_t step)
const
1097 thread_local std::vector<double> pairs;
1098 thread_local std::vector<int>
cells;
1099 double smallestPruned = TGeoShape::Big();
1101 Error(
"DistFromOutside",
1102 "Shape %s: CellIntervals overflowed a per-box buffer sized from that box's own active "
1103 "list; the maxPairsForCell bound no longer holds. Answering from the loop twin.",
1110 const int count =
static_cast<int>(
cells.size());
1111 thread_local std::vector<int> order;
1112 order.resize(
count);
1113 std::iota(order.begin(), order.end(), 0);
1114 std::sort(order.begin(), order.end(), [&](
int left,
int right) {
1115 if (cells[left] != cells[right]) {
1116 return cells[left] < cells[right];
1118 return pairs[2 *
left] < pairs[2 *
right];
1121 double best = TGeoShape::Big();
1125 const double enter = pairs[2 * order[
index]];
1126 double exit = pairs[2 * order[
index] + 1];
1130 exit = std::max(exit, pairs[2 * order[
index] + 1]);
1135 if (exit > TGeoShape::Tolerance() && enter < best) {
1136 best = std::max(enter, 0.);
1145Double_t O2FlatCSG::DistFromInsideBVH(
const Double_t* point,
const Double_t* dir,
1146 Double_t step)
const
1148 thread_local std::vector<double> pairs;
1149 thread_local std::vector<int>
cells;
1150 for (
int attempt = 0; attempt < 2; ++attempt) {
1153 const RayBound bound = attempt == 0 ? RayBound::kExit : RayBound::kNone;
1154 double smallestPruned = TGeoShape::Big();
1155 if (!GatherRayPieces(point, dir, step, pairs,
cells, bound, smallestPruned)) {
1156 Error(
"DistFromInside",
1157 "Shape %s: CellIntervals overflowed a per-box buffer sized from that box's own active "
1158 "list; the maxPairsForCell bound no longer holds. Answering from the loop twin.",
1160 return DistFromInside_Loop(point, dir, step);
1162 const int count = mergeIntervals(pairs.data(),
static_cast<int>(
cells.size()),
1163 TGeoShape::Tolerance());
1165 for (
int pair = 0; pair <
count; ++pair) {
1166 if (pairs[2 * pair] <= TGeoShape::Tolerance()) {
1167 answer = pairs[2 * pair + 1];
1171 if (attempt == 1 || smallestPruned > answer + TGeoShape::Tolerance()) {
1174 ++gUnprunedRetryCount;
1179void O2FlatCSG::ResetUnprunedRetryCounter()
1181 gUnprunedRetryCount = 0;
1184long long O2FlatCSG::GetUnprunedRetryCount()
1186 return gUnprunedRetryCount;
1189Double_t O2FlatCSG::DistFromOutside(
const Double_t* point,
const Double_t* dir, Int_t iact,
1190 Double_t step, Double_t* safe)
const
1192 if (iact < 3 && safe !=
nullptr) {
1193 *safe = Safety(point, kFALSE);
1195 return TGeoShape::Big();
1197 if (iact == 1 && step < *safe) {
1198 return TGeoShape::Big();
1201 if (!fClosed || fBVH ==
nullptr) {
1204 return DistFromOutside_Loop(point, dir, step);
1206 return DistFromOutsideBVH(point, dir, step);
1209Double_t O2FlatCSG::DistFromInside(
const Double_t* point,
const Double_t* dir, Int_t iact,
1210 Double_t step, Double_t* safe)
const
1212 if (iact < 3 && safe !=
nullptr) {
1213 *safe = Safety(point, kTRUE);
1215 return TGeoShape::Big();
1217 if (iact == 1 && step < *safe) {
1218 return TGeoShape::Big();
1221 if (!fClosed || fBVH ==
nullptr) {
1222 return DistFromInside_Loop(point, dir, step);
1224 return DistFromInsideBVH(point, dir, step);
1230Double_t O2FlatCSG::Safety_Loop(
const Double_t* point, Bool_t in)
const
1233 double best = TGeoShape::Big();
1234 for (
const auto&
box : fBoxes) {
1235 best = std::min(best, boxDistanceSquared(
box, point));
1237 return best >= TGeoShape::Big() ? 0. : std::sqrt(best);
1241 for (
const auto&
box : fBoxes) {
1242 if (boxHoldsPoint(
box, point) &&
box.nActive == 0) {
1243 best = std::max(best, distanceToFaces(
box, point));
1246 return std::max(best, 0.);
1252Double_t O2FlatCSG::Safety(
const Double_t* point, Bool_t in)
const
1254 if (!fClosed || fBVH ==
nullptr) {
1256 return Safety_Loop(point, in);
1258 const BVH&
bvh = *
static_cast<const BVH*
>(fBVH);
1262 using DVec3 = bvh::v2::Vec<double, 3>;
1263 using DBBox = bvh::v2::BBox<double, 3>;
1264 const DVec3 dpoint(point[0], point[1], point[2]);
1265 const auto nodeDistanceSquared = [&
bvh, &dpoint](
size_t index) {
1266 const auto& fbox =
bvh.nodes[
index].get_bbox();
1267 const DBBox dbox(DVec3(
static_cast<double>(fbox.min[0]),
static_cast<double>(fbox.min[1]),
1268 static_cast<double>(fbox.min[2])),
1269 DVec3(
static_cast<double>(fbox.max[0]),
static_cast<double>(fbox.max[1]),
1270 static_cast<double>(fbox.max[2])));
1271 return bvh::v2::extra::SafetySqToNode(dbox, dpoint);
1277 thread_local std::vector<NodeEntry> nearStack;
1279 nearStack.push_back({0, nodeDistanceSquared(0)});
1280 double best = TGeoShape::Big();
1281 while (!nearStack.empty()) {
1282 const NodeEntry
entry = nearStack.back();
1283 nearStack.pop_back();
1285 if (
entry.squared >= best) {
1288 if (
node.is_leaf()) {
1289 const auto beginPrimitive =
node.index.first_id();
1290 const auto endPrimitive = beginPrimitive +
node.index.prim_count();
1291 for (
auto primitive = beginPrimitive; primitive < endPrimitive; ++primitive) {
1292 best = std::min(best, boxDistanceSquared(fBoxes[
bvh.prim_ids[primitive]], point));
1296 const auto firstChild =
node.index.first_id();
1297 size_t children[2] = {firstChild, firstChild + 1};
1298 double childSquared[2] = {TGeoShape::Big(), TGeoShape::Big()};
1300 if (children[
index] <
bvh.nodes.size()) {
1301 childSquared[
index] = nodeDistanceSquared(children[
index]);
1304 const int nearer = childSquared[0] <= childSquared[1] ? 0 : 1;
1305 const int farther = 1 - nearer;
1307 if (children[farther] <
bvh.nodes.size() && childSquared[farther] < best) {
1308 nearStack.push_back({children[farther], childSquared[farther]});
1310 if (children[nearer] <
bvh.nodes.size() && childSquared[nearer] < best) {
1311 nearStack.push_back({children[nearer], childSquared[nearer]});
1315 return best >= TGeoShape::Big() ? 0. : std::sqrt(best);
1319 traversePoint(
bvh, point, [&](
int index) {
1321 if (boxHoldsPoint(
box, point) &&
box.nActive == 0) {
1322 best = std::max(best, distanceToFaces(box, point));
1326 return std::max(best, 0.);
1329Double_t O2FlatCSG::Capacity()
const
1332 return std::accumulate(fCells.begin(), fCells.end(), 0.,
1333 [](
double sum,
const FlatCSGCell& cell) { return sum + cell.volume; });
1340void O2FlatCSG::ComputeNormal(
const Double_t* point,
const Double_t* dir, Double_t* norm)
const
1342 norm[0] = norm[1] = norm[2] = 0.;
1343 if (fHalfspaces.empty()) {
1349 const int* activeList =
nullptr;
1351 int nCandidates = GetNhalfspaces();
1352 if (fClosed && fBVH !=
nullptr) {
1353 traversePoint(*
static_cast<const BVH*
>(fBVH), point, [&](
int index) {
1355 if (!boxHoldsPoint(
box, point)) {
1358 if (
box.nActive > 0) {
1359 activeList = fActive.data() + box.firstActive;
1360 nCandidates = box.nActive;
1362 rangeFirst = fCells[box.cell].first;
1363 nCandidates = fCells[box.cell].count;
1368 const auto indexAt = [&](
int slot) {
return activeList !=
nullptr ? activeList[slot] : rangeFirst + slot; };
1371 double bestValue = std::numeric_limits<double>::infinity();
1372 double bestGrad[3] = {0., 0., 0.};
1373 for (
int slot = 0; slot < nCandidates; ++slot) {
1374 const int candidate = indexAt(slot);
1376 const double f = EvalHalfspace(halfspace, point);
1378 halfspaceGradient(halfspace, point, grad);
1379 const double gradLength = std::sqrt(grad[0] * grad[0] + grad[1] * grad[1] + grad[2] * grad[2]);
1380 if (gradLength < 1.e-300) {
1383 const double value = std::abs(
f) / gradLength;
1384 if (
value < bestValue) {
1387 bestGrad[0] = grad[0] / gradLength;
1388 bestGrad[1] = grad[1] / gradLength;
1389 bestGrad[2] = grad[2] / gradLength;
1395 const double dirLength = std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
1396 if (dirLength > 1.e-300) {
1407 const double dot = norm[0] * dir[0] + norm[1] * dir[1] + norm[2] * dir[2];
1415void O2FlatCSG::ComputeBBox()
1418 if (fBoxes.empty()) {
1421 double lo[3] = {TGeoShape::Big(), TGeoShape::Big(), TGeoShape::Big()};
1422 double hi[3] = {-TGeoShape::Big(), -TGeoShape::Big(), -TGeoShape::Big()};
1432 fDX = 0.5 * (hi[0] - lo[0]);
1433 fDY = 0.5 * (hi[1] - lo[1]);
1434 fDZ = 0.5 * (hi[2] - lo[2]);
header::DataOrigin origin
header::DataDescription description
Private analytic bounded surfaces, trim wires and closure checks behind O2BVHSurfaceSolid.
std::unique_ptr< expressions::Node > node
std::vector< double > sum
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 kept
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 s
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 pp
configStandalone Reconstruction rec_tpc float
ClassImp(o2::cad::O2FlatCSG)
void SplitBox(int cell, const double *lo, const double *hi, const std::vector< int > &active, int depth, double minSize, int cubifyBudget)
std::vector< int > fActive
void * fBVH
The BVH over fBoxes, rebuilt by CloseShape; not streamed.
int AddCell(int first, int count, double volume)
Append a cell over [first, first + count) of the halfspace array; returns its index.
Double_t DistFromOutside_Loop(const Double_t *point, const Double_t *dir, Double_t step=TGeoShape::Big()) const
std::vector< double > fCellLo
each cell's AABB low corner, 3 doubles per cell
int AddQuadric(double sign, const double coeff[10])
Append a quadric halfspace; returns its index. sign is +1 or -1, inside is sign*Q <= 0.
int CellIntervals(int cell, const int *active, int nActive, const double *origin, const double *dir, double tlo, double thi, double *out, int maxOut) const
static int HalfspaceRoots(const FlatCSGHalfspace &halfspace, const double *origin, const double *dir, double *roots)
Real roots of sign * f(origin + t*dir) = 0, unsorted, at most four; returns the count.
bool fClosed
Set by a successful CloseShape; not streamed. The #pragma read rule closes every shape ROOT reads bac...
Bool_t GetPointsOnSegments(Int_t npoints, Double_t *array) const override
Points on the solid's own boundary, for the overlap checkers; kFALSE if fewer than npoints were found...
bool GatherRayPieces(const Double_t *point, const Double_t *dir, Double_t step, std::vector< double > &pairs, std::vector< int > &cells, RayBound bound, double &smallestPruned) const
GatherRayPieces – each box's window is its own slab intersected with [0, step], never pooled across b...
void EnsureCellBBoxStorage()
Grow the per-cell bounding-box storage to the cell count.
std::vector< bool > fCellBBoxSet
static double EvalHalfspace(const FlatCSGHalfspace &halfspace, const double *point)
sign * f(point); the halfspace contains the point when this is <= 0.
void CloseShape()
Build the sub-cell boxes and their BVH. Call once, after the last AddCell.
Bool_t Contains_Loop(const Double_t *point) const
static void HalfspaceRange(const FlatCSGHalfspace &halfspace, const double *lo, const double *hi, double &rangeLo, double &rangeHi)
void ComputeBBox() override
The union of the retained sub-cell boxes, tighter than the union of the cell AABBs.
std::vector< double > fCellHi
Double_t DistFromInside_Loop(const Double_t *point, const Double_t *dir, Double_t step=TGeoShape::Big()) const
int AddTorus(double sign, const double *centre, const double *axis, double major, double minor)
Append a torus halfspace, inside sign * (sqrt((rho - major)^2 + z^2) - minor) <= 0 about unit axis; r...
void ComputeNormal(const Double_t *point, const Double_t *dir, Double_t *norm) const override
The normal of the halfspace nearest to equality at point, oriented along dir.
Bool_t Contains(const Double_t *point) const override
void GetCellBBox(int cell, double *lo, double *hi) const
std::vector< FlatCSGCell > fCells
the DNF's cells, indexing into it
size_t GetBVHMemory() const
Bytes held by the BVH nodes and the primitive-index permutation.
bool CellContains(int index, const double *point) const
True when every halfspace of cell index contains point.
Double_t DistFromOutsideBVH(const Double_t *point, const Double_t *dir, Double_t step) const
The accelerated DistFromOutside/DistFromInside bodies; each clips the ray to a box before using its a...
std::vector< FlatCSGBox > fBoxes
The sub-cell boxes, rebuilt by CloseShape; not streamed.
std::vector< FlatCSGHalfspace > fHalfspaces
the flat halfspace array
void SetCellBBox(int cell, const double *lo, const double *hi)
GLfloat GLfloat GLfloat alpha
GLuint const GLchar * name
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * value
GLint GLint GLsizei GLsizei GLsizei depth
GLboolean GLboolean GLboolean GLboolean a
GLsizei const GLint * box
GLdouble GLdouble GLdouble z
QuarticRoots solveQuarticReal(double a4, double a3, double a2, double a1, double a0, QuarticBranch *takenBranch=nullptr)
const bool const bool const int FollowDirection BestTrial TrackITSInternal< NLayers > & best
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
One DNF cell: [first, first + count) of the halfspace array, intersected; volume is its own volume.
std::vector< Cell > cells