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;
805 const double a1 = 4. * e *
f - 8. *
major *
major * pd;
806 const double a0 = e * e - 4. *
major *
major * pp;
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);
892 bool overflow =
false;
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;
925 return overflow ? -1 : pairs;
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]);
954 pairs[2 * kept] = pairs[2 *
index];
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
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)
float sum(float s, o2::dcs::DataPointValue v)
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