24#include "TBuffer3DTypes.h"
41using BVHScalar = float;
42using BVHBBox = bvh::v2::BBox<BVHScalar, 3>;
43using BVHVec3 = bvh::v2::Vec<BVHScalar, 3>;
44using BVHNode = bvh::v2::Node<BVHScalar, 3>;
45using BVH = bvh::v2::Bvh<BVHNode>;
46using BVHRay = bvh::v2::Ray<BVHScalar, 3>;
50 return {point[0], point[1]};
55 return {point[0], point[1], point[2]};
58Vec3 makeVec3(
const Double_t* point)
60 return {point[0], point[1], point[2]};
65const Vec3 kContainsTestDirection =
normalized({1., 1.41421356237, 1.73205080757});
68const std::array<Vec3, 5>& reshootDirections()
70 static const std::array<Vec3, 5> directions = [] {
71 std::array<Vec3, 5> spiral{};
73 const double cosTheta = 1. - 2. * (
index + 0.5) / 5.;
74 const double sinTheta = std::sqrt(1. - cosTheta * cosTheta);
75 const double phi = 2.399963229728653 *
index;
84bool gRayTMaxPruning =
true;
86thread_local long long gRayCandidateCount = 0;
89thread_local long long gSafetyCandidateCount = 0;
92bool gSafetyBoundUnsound =
false;
96thread_local std::vector<unsigned long long> gSurfaceVisitStamps;
97thread_local unsigned long long gSurfaceVisitEpoch = 0;
101class SurfaceVisitMarker
104 explicit SurfaceVisitMarker(
size_t surfaceCount) : mStamps(gSurfaceVisitStamps), mEpoch(++gSurfaceVisitEpoch)
106 if (mStamps.size() < surfaceCount) {
107 mStamps.resize(surfaceCount, 0);
112 bool firstVisit(
size_t index)
114 if (mStamps[
index] == mEpoch) {
117 mStamps[
index] = mEpoch;
123 std::vector<unsigned long long>& mStamps;
124 unsigned long long mEpoch;
129inline double boxDistanceSq(
const BVHBBox&
box,
const Vec3& point,
bool unsoundBound)
133 for (
int dimension = 0; dimension < 3; ++dimension) {
134 const double lower =
static_cast<double>(
box.min[dimension]);
135 const double upper =
static_cast<double>(
box.max[dimension]);
136 const double value = coordinates[dimension];
145 double centreDistanceSq = 0.;
146 for (
int dimension = 0; dimension < 3; ++dimension) {
147 const double centre =
148 0.5 * (
static_cast<double>(
box.min[dimension]) +
static_cast<double>(
box.max[dimension]));
149 const double gap = coordinates[dimension] - centre;
150 centreDistanceSq += gap * gap;
152 return centreDistanceSq;
158inline BVHScalar truncateRoundUp(
double bound)
160 const double clamped = std::min(bound,
static_cast<double>(std::numeric_limits<BVHScalar>::max()));
161 const double biased = clamped + std::numeric_limits<BVHScalar>::epsilon() * std::abs(clamped);
162 return static_cast<BVHScalar
>(biased);
169enum class CrossingSense { Entering,
174inline double clusterMargin(
double distance)
179inline CrossingSense crossingSense(
const RayHit& hit,
const Vec3& rayDirection)
181 const double alignment =
dot(hit.
normal, rayDirection);
183 return CrossingSense::Entering;
186 return CrossingSense::Exiting;
188 return CrossingSense::Tangential;
192template <
typename ClusterVisitor>
193void forEachCrossingCluster(std::vector<RayHit>& hits,
const Vec3& rayDirection, ClusterVisitor&& visitor)
195 std::sort(hits.begin(), hits.end(),
196 [](
const RayHit& firstHit,
const RayHit& secondHit) { return firstHit.distance < secondHit.distance; });
199 while (hitIndex < hits.size()) {
200 bool entering =
false;
201 bool exiting =
false;
202 size_t clusterEnd = hitIndex;
204 while (clusterEnd < hits.size() &&
206 switch (crossingSense(hits[clusterEnd], rayDirection)) {
207 case CrossingSense::Entering:
210 case CrossingSense::Exiting:
213 case CrossingSense::Tangential:
219 const CrossingSense sense = entering == exiting ? CrossingSense::Tangential
220 : (entering ? CrossingSense::Entering : CrossingSense::Exiting);
221 if (!visitor(hitIndex, clusterEnd, sense)) {
224 hitIndex = clusterEnd;
229template <
bool wantEntering>
230double nearestCrossingInHits(std::vector<RayHit>& hits,
const Vec3& rayDirection,
bool& grazedFirst)
232 constexpr CrossingSense wanted = wantEntering ? CrossingSense::Entering : CrossingSense::Exiting;
235 forEachCrossingCluster(hits, rayDirection, [&](
size_t firstIndex,
size_t, CrossingSense sense) {
236 if (sense == CrossingSense::Tangential) {
240 if (sense != wanted) {
268 std::vector<double> scalars,
bool innerWall,
bool trimmed)
273 fillPoint3(record.
axisA, axisA);
274 fillPoint3(record.
axisB, axisB);
275 record.
scalars = std::move(scalars);
284 record.
kind =
static_cast<int>(curve.
kind);
296 for (
const auto& pole : curve.poles) {
297 record.
poles.push_back(pole[0]);
298 record.
poles.push_back(pole[1]);
326void storeCurveWires(
BVHSurfaceRecord& record,
const std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>& outerWire,
327 const std::vector<std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>>& innerWires)
329 record.
wireSizes.push_back(
static_cast<int>(outerWire.size()));
330 for (
const auto& curve : outerWire) {
331 record.
curves.push_back(makeCurveRecord(curve));
333 for (
const auto& innerWire : innerWires) {
334 record.
wireSizes.push_back(
static_cast<int>(innerWire.size()));
335 for (
const auto& curve : innerWire) {
336 record.
curves.push_back(makeCurveRecord(curve));
343bool loadCurveWires(
const BVHSurfaceRecord& record, std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>& outerWire,
344 std::vector<std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>>& innerWires)
347 for (
size_t wireIndex = 0; wireIndex < record.
wireSizes.size(); ++wireIndex) {
348 const int wireSize = record.
wireSizes[wireIndex];
349 if (wireSize < 0 || consumed +
static_cast<size_t>(wireSize) > record.
curves.size()) {
352 auto& wire = wireIndex == 0 ? outerWire : innerWires.emplace_back();
353 for (
int curveIndex = 0; curveIndex < wireSize; ++curveIndex) {
354 wire.push_back(makeBoundaryCurve(record.
curves[consumed + curveIndex]));
356 consumed +=
static_cast<size_t>(wireSize);
358 return consumed == record.
curves.size();
362void storePolygonWires(
BVHSurfaceRecord& record,
const std::vector<O2BVHSurfaceSolid::Point2D>& outerWire,
363 const std::vector<std::vector<O2BVHSurfaceSolid::Point2D>>& innerWires)
365 const auto append = [&record](
const std::vector<O2BVHSurfaceSolid::Point2D>& wire) {
366 record.
wireSizes.push_back(
static_cast<int>(wire.size()));
367 for (
const auto&
vertex : wire) {
373 for (
const auto& innerWire : innerWires) {
378bool loadPolygonWires(
const BVHSurfaceRecord& record, std::vector<O2BVHSurfaceSolid::Point2D>& outerWire,
379 std::vector<std::vector<O2BVHSurfaceSolid::Point2D>>& innerWires)
382 for (
size_t wireIndex = 0; wireIndex < record.
wireSizes.size(); ++wireIndex) {
383 const int wireSize = record.
wireSizes[wireIndex];
384 if (wireSize < 0 || 2 * (consumed +
static_cast<size_t>(wireSize)) > record.
polygonPoints.size()) {
387 auto& wire = wireIndex == 0 ? outerWire : innerWires.emplace_back();
388 for (
int vertexIndex = 0; vertexIndex < wireSize; ++vertexIndex) {
389 const size_t offset = 2 * (consumed + vertexIndex);
392 consumed +=
static_cast<size_t>(wireSize);
399bool oddCrossingParity(std::vector<RayHit>& hits,
const Vec3& rayDirection)
402 forEachCrossingCluster(hits, rayDirection, [&](
size_t,
size_t, CrossingSense sense) {
403 if (sense != CrossingSense::Tangential) {
408 return (crossings & 1) != 0;
416 case RimState::Matched:
417 return Reliability::Reliable;
418 case RimState::Reversed:
419 return Reliability::ReversedFaces;
420 case RimState::Boundary:
421 return Reliability::OpenSurfaceSet;
422 case RimState::NonManifold:
423 return Reliability::NonManifold;
425 return Reliability::Undetermined;
430 std::vector<std::unique_ptr<BoundedSurface>>
surfaces;
457 std::vector<BVHBBox> primitiveBoxes;
458 std::vector<BVHVec3> primitiveCenters;
459 std::vector<BoundedSurface::CoverBox> coverBoxes;
460 std::vector<int> coverSurface;
461 for (
size_t surfaceIndex = 0; surfaceIndex <
surfaces.size(); ++surfaceIndex) {
463 surfaces[surfaceIndex]->appendCoverBoxes(coverBoxes);
464 for (
const auto& coverBox : coverBoxes) {
465 BVHBBox primitiveBox;
466 for (
int dimension = 0; dimension < 3; ++dimension) {
467 primitiveBox.min[dimension] = std::nextafterf(
469 -std::numeric_limits<float>::infinity());
470 primitiveBox.max[dimension] = std::nextafterf(
472 std::numeric_limits<float>::infinity());
474 primitiveBoxes.push_back(primitiveBox);
475 primitiveCenters.emplace_back(primitiveBox.get_center());
476 coverSurface.push_back(
static_cast<int>(surfaceIndex));
480 typename bvh::v2::DefaultBuilder<BVHNode>::Config config;
481 config.quality = bvh::v2::DefaultBuilder<BVHNode>::Quality::High;
483 config.max_leaf_size = 1;
484 bvh = std::make_unique<BVH>(bvh::v2::DefaultBuilder<BVHNode>::build(primitiveBoxes, primitiveCenters, config));
486 for (
size_t leaf = 0; leaf <
bvh->prim_ids.size(); ++leaf) {
494 return static_cast<size_t>(
leafSurface[primitive]);
500 constexpr size_t kAnchorCount = 24;
515 double bestDistanceSq = std::numeric_limits<double>::infinity();
517 bestDistanceSq = std::min(bestDistanceSq,
normSq(point - anchor));
519 if (!std::isfinite(bestDistanceSq)) {
520 return bestDistanceSq;
523 const double inflated = std::sqrt(bestDistanceSq) * (1. + 1.e-12) + 1.e-10;
524 return inflated * inflated;
529 template <
typename SurfaceVisitor>
534 std::numeric_limits<BVHScalar>::max());
535 static constexpr bool useRobustTraversal =
true;
536 static thread_local bvh::v2::GrowingStack<BVH::Index>
stack;
539 bvh->intersect<
false, useRobustTraversal>(ray,
bvh->get_root().index,
stack,
540 [&](
size_t beginPrimitive,
size_t endPrimitive) {
541 for (
size_t primitive = beginPrimitive; primitive < endPrimitive;
544 if (
marker.firstVisit(surfaceIndex)) {
554 template <
bool wantEntering>
557 static thread_local std::vector<RayHit> collectedHits;
558 constexpr CrossingSense wanted = wantEntering ? CrossingSense::Entering : CrossingSense::Exiting;
562 long long candidates = 0;
563 for (
int attempt = 0; attempt < 2; ++attempt) {
564 const bool pruning = gRayTMaxPruning && attempt == 0;
565 collectedHits.clear();
567 double bestCandidate = TGeoShape::Big();
570 truncateRoundUp(stepmax));
571 static constexpr bool useRobustTraversal =
true;
573 static thread_local bvh::v2::GrowingStack<BVH::Index>
stack;
578 bvh->intersect<
false, useRobustTraversal>(
579 ray,
bvh->get_root().index,
stack, [&](
size_t beginPrimitive,
size_t endPrimitive) {
580 for (
size_t primitive = beginPrimitive; primitive < endPrimitive; ++primitive) {
582 if (!
marker.firstVisit(surfaceIndex)) {
589 pruning ? std::min(stepmax, bestCandidate + clusterMargin(bestCandidate)) : stepmax;
590 const size_t firstNewHit = collectedHits.size();
591 surface.
appendIntersections(rayOrigin, rayDirection, kDistanceRayTolerance, bound, collectedHits);
592 for (
size_t hitIndex = firstNewHit; hitIndex < collectedHits.size(); ++hitIndex) {
593 const RayHit& hit = collectedHits[hitIndex];
594 if (crossingSense(hit, rayDirection) == wanted && hit.
distance < bestCandidate) {
599 if (pruning && bestCandidate < stepmax) {
600 ray.tmax = std::min(ray.tmax, truncateRoundUp(bestCandidate +
kBVHBoxTolerance));
605 bool grazedFirst =
false;
606 const double distance = nearestCrossingInHits<wantEntering>(collectedHits, rayDirection, grazedFirst);
607 if (!pruning || !grazedFirst) {
608 gRayCandidateCount += candidates;
612 gRayCandidateCount += candidates;
613 return TGeoShape::Big();
617 template <
bool wantEntering>
620 static thread_local std::vector<RayHit> collectedLoopHits;
624 collectedLoopHits.clear();
625 for (
const auto& surface :
surfaces) {
626 surface->appendIntersections(rayOrigin, rayDirection, kDistanceRayTolerance, stepmax, collectedLoopHits);
628 bool grazedFirst =
false;
629 return nearestCrossingInHits<wantEntering>(collectedLoopHits, rayDirection, grazedFirst);
637 static thread_local std::vector<RayHit> parityHits;
644 for (
const auto& surface :
surfaces) {
645 surface->appendIntersections(point, direction,
kRayTolerance, TGeoShape::Big(), parityHits);
648 if (ambiguous !=
nullptr) {
649 *ambiguous = std::any_of(parityHits.begin(), parityHits.end(),
650 [](
const RayHit& hit) { return hit.onTrimBoundary; });
652 return oddCrossingParity(parityHits, direction);
659 constexpr int kMajority = 3;
662 int insideOnBoundary = 0;
663 int outsideOnBoundary = 0;
664 for (
const auto& direction : reshootDirections()) {
665 bool ambiguous =
false;
666 const bool answer =
parityAlong(point, direction, useBVH, &ambiguous);
668 answer ? ++insideOnBoundary : ++outsideOnBoundary;
670 answer ? ++inside : ++outside;
672 if (inside >= kMajority || outside >= kMajority) {
676 if (allTiedOnBoundary !=
nullptr) {
677 *allTiedOnBoundary = (inside == outside);
680 if (inside != outside) {
681 return inside > outside;
683 return (inside + insideOnBoundary) > (outside + outsideOnBoundary);
687 template <
typename SurfaceVisitor>
692 static thread_local std::vector<size_t> nodeStack;
694 nodeStack.push_back(0);
695 while (!nodeStack.empty()) {
696 const auto&
node =
bvh->nodes[nodeStack.back()];
697 nodeStack.pop_back();
698 if (!bvh::v2::extra::contains(
node.get_bbox(), testPoint)) {
701 if (
node.is_leaf()) {
702 const auto beginPrimitive =
node.index.first_id();
703 const auto endPrimitive = beginPrimitive +
node.index.prim_count();
704 for (
auto primitive = beginPrimitive; primitive < endPrimitive; ++primitive) {
706 if (
marker.firstVisit(surfaceIndex) && visitor(*
surfaces[surfaceIndex])) {
711 const auto firstChild =
node.index.first_id();
712 for (
size_t child : {firstChild, firstChild + 1}) {
713 if (child < bvh->
nodes.size()) {
714 nodeStack.push_back(child);
725 double bestDistanceSq = std::numeric_limits<double>::infinity();
728 const double patchDistanceSq =
surfaces[
index]->distanceSqToPatch(point);
729 if (patchDistanceSq < bestDistanceSq) {
730 bestDistanceSq = patchDistanceSq;
734 if (closestIndex !=
nullptr) {
735 *closestIndex = bestIndex;
737 return bestDistanceSq;
742 template <
bool TrackIndex>
745 if (
bvh ==
nullptr) {
754 static thread_local std::vector<StackEntry> nodeStack;
761 const bool unsound = gSafetyBoundUnsound;
762 long long candidates = 0;
764 auto pruned = [](
double lowerBoundSq,
double bestSoFarSq) {
765 return TrackIndex ? lowerBoundSq > bestSoFarSq : lowerBoundSq >= bestSoFarSq;
768 nodeStack.push_back({0, boxDistanceSq(
bvh->nodes[0].get_bbox(), point, unsound)});
769 while (!nodeStack.empty()) {
770 const StackEntry
entry = nodeStack.back();
771 nodeStack.pop_back();
772 if (pruned(
entry.lowerBoundSq, bestDistanceSq)) {
776 if (
node.is_leaf()) {
777 const auto beginPrimitive =
node.index.first_id();
778 const auto endPrimitive = beginPrimitive +
node.index.prim_count();
779 for (
auto primitive = beginPrimitive; primitive < endPrimitive; ++primitive) {
781 if (!
marker.firstVisit(surfaceIndex)) {
785 const double patchDistanceSq =
surfaces[surfaceIndex]->distanceSqToPatch(point);
786 if (patchDistanceSq < bestDistanceSq) {
787 bestDistanceSq = patchDistanceSq;
788 bestIndex = surfaceIndex;
789 }
else if (TrackIndex && patchDistanceSq == bestDistanceSq && surfaceIndex < bestIndex) {
790 bestIndex = surfaceIndex;
795 const size_t firstChild =
node.index.first_id();
796 const size_t secondChild = firstChild + 1;
797 if (secondChild >=
bvh->nodes.size()) {
798 if (firstChild < bvh->
nodes.size()) {
799 nodeStack.push_back({firstChild, boxDistanceSq(
bvh->nodes[firstChild].get_bbox(), point, unsound)});
803 double nearBound = boxDistanceSq(
bvh->nodes[firstChild].get_bbox(), point, unsound);
804 double farBound = boxDistanceSq(
bvh->nodes[secondChild].get_bbox(), point, unsound);
805 size_t nearChild = firstChild;
806 size_t farChild = secondChild;
807 if (farBound < nearBound) {
808 std::swap(nearBound, farBound);
809 std::swap(nearChild, farChild);
813 if (!pruned(farBound, bestDistanceSq)) {
814 nodeStack.push_back({farChild, farBound});
816 if (!pruned(nearBound, bestDistanceSq)) {
817 nodeStack.push_back({nearChild, nearBound});
821 gSafetyCandidateCount += candidates;
822 if (closestIndex !=
nullptr) {
823 *closestIndex = bestIndex;
825 return bestDistanceSq;
834 owner.Error(method,
"Shape %s already fully defined. Not adding", owner.GetName());
840 std::vector<BVHSurfaceRecord>&
records)
842 records.push_back(std::move(record));
843 surfaces.emplace_back(std::move(surface));
850 switch (recordKind) {
879 const std::vector<Point2D>& outerWire,
880 const std::vector<std::vector<Point2D>>& innerWires)
886 std::vector<Vec2> convertedOuterWire;
887 convertedOuterWire.reserve(outerWire.size());
888 for (
const auto&
vertex : outerWire) {
889 convertedOuterWire.push_back(makeVec2(
vertex));
892 std::vector<std::vector<Vec2>> convertedInnerWires;
893 convertedInnerWires.reserve(innerWires.size());
894 for (
const auto& innerWire : innerWires) {
895 auto& convertedInnerWire = convertedInnerWires.emplace_back();
896 convertedInnerWire.reserve(innerWire.size());
897 for (
const auto&
vertex : innerWire) {
898 convertedInnerWire.push_back(makeVec2(
vertex));
902 auto surface = std::make_unique<PlanarBoundedSurface>();
903 std::string errorMessage;
904 if (!surface->initialize(makeVec3(
origin), makeVec3(axisU), makeVec3(axisV), convertedOuterWire, convertedInnerWires,
906 Error(
"AddPlanarSurface",
"%s", errorMessage.c_str());
909 if (surface->wasReoriented()) {
910 Warning(
"AddPlanarSurface",
"Shape %s: planar surface %d had a wire re-oriented to match its role", GetName(),
911 static_cast<int>(fImpl->
surfaces.size()));
915 storePolygonWires(record, outerWire, innerWires);
916 return fImpl->
commit(std::move(surface), std::move(record), fRecords);
922std::vector<Curve2D> makeCurveWire(
const std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>& wire)
924 std::vector<Curve2D> curves;
925 curves.reserve(wire.size());
926 for (
const auto&
c : wire) {
930 std::vector<Vec2> poles;
931 poles.reserve(
c.poles.size());
932 for (
const auto& pole :
c.poles) {
933 poles.push_back({pole[0], pole[1]});
944std::vector<std::vector<Curve2D>> makeCurveWires(
945 const std::vector<std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>>& wires)
947 std::vector<std::vector<Curve2D>> loops;
948 loops.reserve(wires.size());
949 for (
const auto& wire : wires) {
950 loops.push_back(makeCurveWire(wire));
957 const std::vector<PlanarBoundaryCurve>& outerWire,
958 const std::vector<std::vector<PlanarBoundaryCurve>>& innerWires)
964 const std::vector<Curve2D> outerCurves = makeCurveWire(outerWire);
965 const std::vector<std::vector<Curve2D>> innerCurves = makeCurveWires(innerWires);
967 auto surface = std::make_unique<CurvedPlanarBoundedSurface>();
968 std::string errorMessage;
969 if (!surface->initialize(makeVec3(
origin), makeVec3(axisU), makeVec3(axisV), outerCurves, innerCurves,
971 Error(
"AddCurvedPlanarSurface",
"%s", errorMessage.c_str());
976 storeCurveWires(record, outerWire, innerWires);
977 return fImpl->
commit(std::move(surface), std::move(record), fRecords);
981 const Point3D& referenceAxisU,
double radius,
double heightMin,
982 double heightMax,
double phiStart,
double phiSweep,
bool innerWall)
988 auto surface = std::make_unique<CylindricalBoundedSurface>();
989 std::string errorMessage;
990 if (!surface->initialize(makeVec3(centerPoint), makeVec3(axis), makeVec3(referenceAxisU), radius, heightMin,
991 heightMax, phiStart, phiSweep, innerWall, errorMessage)) {
992 Error(
"AddCylindricalSurface",
"%s", errorMessage.c_str());
996 return fImpl->
commit(std::move(surface),
998 {radius, heightMin, heightMax, phiStart, phiSweep}, innerWall,
false),
1003 const Point3D& referenceAxisU,
double radius,
double heightMin,
1004 double heightMax,
double phiStart,
double phiSweep,
bool innerWall,
1005 const std::vector<PlanarBoundaryCurve>& outerTrim,
1006 const std::vector<std::vector<PlanarBoundaryCurve>>& innerTrims)
1012 const std::vector<Curve2D> outerCurves = makeCurveWire(outerTrim);
1013 const std::vector<std::vector<Curve2D>> innerCurves = makeCurveWires(innerTrims);
1015 auto surface = std::make_unique<CylindricalBoundedSurface>();
1016 std::string errorMessage;
1017 if (!surface->initialize(makeVec3(centerPoint), makeVec3(axis), makeVec3(referenceAxisU), radius, heightMin,
1018 heightMax, phiStart, phiSweep, innerWall, outerCurves, innerCurves, errorMessage,
1020 Error(
"AddCylindricalSurface",
"%s", errorMessage.c_str());
1025 {radius, heightMin, heightMax, phiStart, phiSweep}, innerWall,
true);
1026 storeCurveWires(record, outerTrim, innerTrims);
1027 return fImpl->
commit(std::move(surface), std::move(record), fRecords);
1031 const Point3D& referenceAxisU,
double radius,
double thetaMin,
1032 double thetaMax,
double phiStart,
double phiSweep,
bool innerWall)
1038 auto surface = std::make_unique<SphericalBoundedSurface>();
1039 std::string errorMessage;
1040 if (!surface->initialize(makeVec3(
center), makeVec3(polarAxis), makeVec3(referenceAxisU), radius, thetaMin,
1041 thetaMax, phiStart, phiSweep, innerWall, errorMessage)) {
1042 Error(
"AddSphericalSurface",
"%s", errorMessage.c_str());
1046 return fImpl->
commit(std::move(surface),
1048 {radius, thetaMin, thetaMax, phiStart, phiSweep}, innerWall,
false),
1053 const Point3D& referenceAxisU,
double radius,
double thetaMin,
1054 double thetaMax,
double phiStart,
double phiSweep,
bool innerWall,
1055 const std::vector<PlanarBoundaryCurve>& outerTrim,
1056 const std::vector<std::vector<PlanarBoundaryCurve>>& innerTrims)
1062 const std::vector<Curve2D> outerCurves = makeCurveWire(outerTrim);
1063 const std::vector<std::vector<Curve2D>> innerCurves = makeCurveWires(innerTrims);
1065 auto surface = std::make_unique<SphericalBoundedSurface>();
1066 std::string errorMessage;
1067 if (!surface->initialize(makeVec3(
center), makeVec3(polarAxis), makeVec3(referenceAxisU), radius, thetaMin,
1068 thetaMax, phiStart, phiSweep, innerWall, outerCurves, innerCurves, errorMessage,
1070 Error(
"AddSphericalSurface",
"%s", errorMessage.c_str());
1075 {radius, thetaMin, thetaMax, phiStart, phiSweep}, innerWall,
true);
1076 storeCurveWires(record, outerTrim, innerTrims);
1077 return fImpl->
commit(std::move(surface), std::move(record), fRecords);
1081 const Point3D& referenceAxisU,
double radiusAtMin,
double radiusAtMax,
1082 double heightMin,
double heightMax,
double phiStart,
double phiSweep,
1089 auto surface = std::make_unique<ConicalBoundedSurface>();
1090 std::string errorMessage;
1091 if (!surface->initialize(makeVec3(centerPoint), makeVec3(axis), makeVec3(referenceAxisU), radiusAtMin,
1092 radiusAtMax, heightMin, heightMax, phiStart, phiSweep, innerWall, errorMessage)) {
1093 Error(
"AddConicalSurface",
"%s", errorMessage.c_str());
1097 return fImpl->
commit(std::move(surface),
1099 {radiusAtMin, radiusAtMax, heightMin, heightMax, phiStart, phiSweep}, innerWall,
1105 const Point3D& referenceAxisU,
double radiusAtMin,
double radiusAtMax,
1106 double heightMin,
double heightMax,
double phiStart,
double phiSweep,
1107 bool innerWall,
const std::vector<PlanarBoundaryCurve>& outerTrim,
1108 const std::vector<std::vector<PlanarBoundaryCurve>>& innerTrims)
1114 const std::vector<Curve2D> outerCurves = makeCurveWire(outerTrim);
1115 const std::vector<std::vector<Curve2D>> innerCurves = makeCurveWires(innerTrims);
1117 auto surface = std::make_unique<ConicalBoundedSurface>();
1118 std::string errorMessage;
1119 if (!surface->initialize(makeVec3(centerPoint), makeVec3(axis), makeVec3(referenceAxisU), radiusAtMin,
1120 radiusAtMax, heightMin, heightMax, phiStart, phiSweep, innerWall, outerCurves,
1122 Error(
"AddConicalSurface",
"%s", errorMessage.c_str());
1127 {radiusAtMin, radiusAtMax, heightMin, heightMax, phiStart, phiSweep}, innerWall,
true);
1128 storeCurveWires(record, outerTrim, innerTrims);
1129 return fImpl->
commit(std::move(surface), std::move(record), fRecords);
1133 const Point3D& referenceAxisU,
double majorRadius,
double minorRadius,
1134 double phiStart,
double phiSweep,
double tubeStart,
double tubeSweep,
1141 auto surface = std::make_unique<TorusBoundedSurface>();
1142 std::string errorMessage;
1143 if (!surface->initialize(makeVec3(centerPoint), makeVec3(axis), makeVec3(referenceAxisU), majorRadius, minorRadius,
1144 phiStart, phiSweep, tubeStart, tubeSweep, innerWall, errorMessage)) {
1145 Error(
"AddToroidalSurface",
"%s", errorMessage.c_str());
1149 return fImpl->
commit(std::move(surface),
1151 {majorRadius, minorRadius, phiStart, phiSweep, tubeStart, tubeSweep}, innerWall,
1157 const Point3D& referenceAxisU,
double majorRadius,
double minorRadius,
1158 double phiStart,
double phiSweep,
double tubeStart,
double tubeSweep,
1159 bool innerWall,
const std::vector<PlanarBoundaryCurve>& outerTrim,
1160 const std::vector<std::vector<PlanarBoundaryCurve>>& innerTrims)
1166 const std::vector<Curve2D> outerCurves = makeCurveWire(outerTrim);
1167 const std::vector<std::vector<Curve2D>> innerCurves = makeCurveWires(innerTrims);
1169 auto surface = std::make_unique<TorusBoundedSurface>();
1170 std::string errorMessage;
1171 if (!surface->initialize(makeVec3(centerPoint), makeVec3(axis), makeVec3(referenceAxisU), majorRadius, minorRadius,
1172 phiStart, phiSweep, tubeStart, tubeSweep, innerWall, outerCurves, innerCurves,
1174 Error(
"AddToroidalSurface",
"%s", errorMessage.c_str());
1179 {majorRadius, minorRadius, phiStart, phiSweep, tubeStart, tubeSweep}, innerWall,
true);
1180 storeCurveWires(record, outerTrim, innerTrims);
1181 return fImpl->
commit(std::move(surface), std::move(record), fRecords);
1188 Error(
"CloseShape",
"Shape %s has no bounded surfaces; it stays undefined and reports itself not navigable",
1199 for (
size_t surfaceIndex = 0; surfaceIndex < fImpl->
surfaces.size(); ++surfaceIndex) {
1213 report.rimOnSurface = record.rimIndexOnSurface;
1214 report.closed = record.closed;
1215 report.chords = record.chords;
1216 report.unmatchedChords = record.unmatchedChords;
1217 report.length = record.length;
1218 report.unmatchedLength = record.unmatchedLength;
1219 report.maxIsolation = record.maxIsolation;
1220 report.maxIsolationPoint = {record.maxIsolationPoint.xCoord, record.maxIsolationPoint.yCoord,
1221 record.maxIsolationPoint.zCoord};
1222 report.maxIsolationFace = record.maxIsolationFace;
1223 report.state = rimStateToReliability(record.state);
1230 const auto& closure = fImpl->
closure;
1232 if (closure.edgeIdentityAvailable && closure.boundaryRims > 0) {
1235 "Shape %s is NOT a closed surface: %d of its %d source edge(s) have only one face and %d more than two, "
1236 "leaving %d of %d trim loop(s) open; navigation is unreliable, see GetRimReports().",
1237 GetName(), closure.edgeBoundaryCount, closure.edgeIncidences, closure.edgeNonManifoldCount,
1238 closure.boundaryRims, closure.rims);
1239 }
else if (closure.boundaryRims > 0) {
1241 "Shape %s is NOT a closed surface: %d of %d trim loop(s) have no neighbouring face within %g cm, leaving "
1242 "%g cm of %g cm of boundary open (loneliest chord %g cm); navigation is unreliable, see GetRimReports().",
1243 GetName(), closure.boundaryRims, closure.rims, closure.rimEpsilon, closure.unmatchedRimLength,
1244 closure.totalRimLength, closure.maxRimIsolation);
1246 if (closure.nonManifoldRims > 0) {
1248 "Shape %s is NOT a 2-manifold: %d of %d trim loop(s) run along two or more other faces; navigation is "
1249 "unreliable, see GetRimReports().",
1250 GetName(), closure.nonManifoldRims, closure.rims);
1252 if (!closure.orientationConsistent) {
1254 "Shape %s has %d inconsistently oriented (reversed) trim loop(s); navigation is unreliable, see "
1256 GetName(), closure.reversedRims);
1258 if (closure.closed && closure.signedVolume < 0.) {
1259 Warning(
"CloseShape",
1260 "Shape %s has inward-pointing surface normals (signed volume %g); navigation expects outward normals",
1261 GetName(), closure.signedVolume);
1268 return static_cast<int>(fImpl->
surfaces.size());
1278 if (!(toleranceCm >= 0.) || !std::isfinite(toleranceCm)) {
1279 Error(
"SetModelTolerance",
"Shape %s: ignoring a non-finite or negative model tolerance %g; it stays %g",
1280 GetName(), toleranceCm, fModelTolerance);
1283 fModelTolerance = toleranceCm;
1288 return fImpl->
bvh !=
nullptr;
1296 const auto rootBox = fImpl->
bvh->get_root().get_bbox();
1297 for (
int dimension = 0; dimension < 3; ++dimension) {
1298 lower[dimension] = rootBox.min[dimension];
1299 upper[dimension] = rootBox.max[dimension];
1330 const auto& closure = fImpl->
closure;
1332 if (closure.edgeIdentityAvailable) {
1333 if (closure.edgeNonManifoldCount > 0) {
1336 if (closure.edgeBoundaryCount > 0) {
1339 if (closure.edgeReversedCount > 0) {
1344 if (closure.nonManifoldRims > 0) {
1347 if (closure.boundaryRims > 0) {
1350 if (closure.reversedRims > 0) {
1363 switch (reliability) {
1365 return "undetermined";
1369 return "reversed-faces";
1371 return "open-surface-set";
1373 return "non-manifold";
1399 const std::vector<unsigned char>& edgeFlags)
1401 if (surfaceIndex < 0 || surfaceIndex >=
static_cast<int>(fImpl->
surfaces.size()) ||
1402 surfaceIndex >=
static_cast<int>(fRecords.size())) {
1403 Error(
"SetSurfaceBoundaryEdges",
"Shape %s: surface index %d is out of range (%d surface(s))", GetName(),
1407 if (edgeIds.size() != edgeFlags.size()) {
1408 Error(
"SetSurfaceBoundaryEdges",
"Shape %s: surface %d was given %d edge id(s) and %d flag(s)", GetName(),
1409 surfaceIndex,
static_cast<int>(edgeIds.size()),
static_cast<int>(edgeFlags.size()));
1412 std::vector<BoundedSurface::BoundaryEdgeRef> refs;
1413 refs.reserve(edgeIds.size());
1420 refs.push_back(
ref);
1422 fImpl->
surfaces[
static_cast<size_t>(surfaceIndex)]->setBoundaryEdges(std::move(refs));
1423 fRecords[
static_cast<size_t>(surfaceIndex)].boundaryEdgeIds = edgeIds;
1424 fRecords[
static_cast<size_t>(surfaceIndex)].boundaryEdgeFlags = edgeFlags;
1530 contributions.clear();
1531 contributions.reserve(fImpl->
surfaces.size());
1532 for (
const auto& surface : fImpl->
surfaces) {
1533 contributions.push_back(surface ==
nullptr ? 0. : surface->capacityContribution());
1540 fDX = fDY = fDZ = 0.;
1541 fOrigin[0] = fOrigin[1] = fOrigin[2] = 0.;
1545 Vec3 lowerCorner{TGeoShape::Big(), TGeoShape::Big(), TGeoShape::Big()};
1546 Vec3 upperCorner{-TGeoShape::Big(), -TGeoShape::Big(), -TGeoShape::Big()};
1547 for (
const auto& surface : fImpl->
surfaces) {
1548 surface->conservativeBounds(lowerCorner, upperCorner);
1551 for (
int dimension = 0; dimension < 3; ++dimension) {
1554 fOrigin[dimension] = 0.5 * (lowerValue + upperValue);
1555 const double halfLength = 0.5 * (upperValue - lowerValue);
1556 if (dimension == 0) {
1558 }
else if (dimension == 1) {
1581void r2Pair(
long long index,
double& firstCoordinate,
double& secondCoordinate)
1583 constexpr double kAlpha1 = 0.7548776662466927;
1584 constexpr double kAlpha2 = 0.5698402909980532;
1585 const double shifted =
static_cast<double>(
index + 1);
1586 firstCoordinate = std::fmod(0.5 + kAlpha1 * shifted, 1.);
1587 secondCoordinate = std::fmod(0.5 + kAlpha2 * shifted, 1.);
1592bool O2BVHSurfaceSolid::ProjectOntoPatch(
int surfaceIndex,
double* point)
const
1594 if (surfaceIndex < 0 ||
static_cast<size_t>(surfaceIndex) >= fImpl->
surfaces.size()) {
1600 Vec3 current = makeVec3(point);
1602 for (
int iteration = 0; iteration < 8 && currentDistanceSq > kToleranceSquared; ++iteration) {
1603 const double distance = std::sqrt(currentDistanceSq);
1611 const double bestDistanceSq = std::min(inwardDistanceSq, outwardDistanceSq);
1613 if (!(bestDistanceSq < currentDistanceSq)) {
1616 current = (inwardDistanceSq < outwardDistanceSq) ? inward :
outward;
1617 currentDistanceSq = bestDistanceSq;
1620 if (currentDistanceSq > kToleranceSquared) {
1623 point[0] = current.
xCoord;
1624 point[1] = current.
yCoord;
1625 point[2] = current.
zCoord;
1631 if (
array ==
nullptr || npoints <= 0 || fImpl->displayVertices.empty()) {
1634 const int vertexCount =
static_cast<int>(fImpl->
displayVertices.size());
1636 if (npoints < vertexCount) {
1640 auto writeVertex = [&](
int slot,
const Vec3&
vertex) {
1646 for (
int vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) {
1650 const int extraCount = npoints - vertexCount;
1651 const int triangleCount =
static_cast<int>(fImpl->
displayTriangles.size());
1652 if (extraCount == 0) {
1658 for (
int extraIndex = 0; extraIndex < extraCount; ++extraIndex) {
1659 writeVertex(vertexCount + extraIndex, fImpl->
displayVertices[extraIndex % vertexCount]);
1664 for (
int extraIndex = 0; extraIndex < extraCount; ++extraIndex) {
1667 const int triangleIndex =
1668 static_cast<int>((
static_cast<long long>(extraIndex) * triangleCount) / extraCount) % triangleCount;
1674 double firstCoordinate = 0.;
1675 double secondCoordinate = 0.;
1676 r2Pair(extraIndex, firstCoordinate, secondCoordinate);
1677 if (firstCoordinate + secondCoordinate > 1.) {
1678 firstCoordinate = 1. - firstCoordinate;
1679 secondCoordinate = 1. - secondCoordinate;
1681 const double weightA = 1. - firstCoordinate - secondCoordinate;
1682 double candidate[3] = {weightA * cornerA.
xCoord + firstCoordinate * cornerB.
xCoord + secondCoordinate * cornerC.
xCoord,
1683 weightA * cornerA.
yCoord + firstCoordinate * cornerB.
yCoord + secondCoordinate * cornerC.
yCoord,
1684 weightA * cornerA.
zCoord + firstCoordinate * cornerB.
zCoord + secondCoordinate * cornerC.
zCoord};
1688 candidate[0] = cornerA.
xCoord;
1689 candidate[1] = cornerA.
yCoord;
1690 candidate[2] = cornerA.
zCoord;
1692 array[3 * (vertexCount + extraIndex) + 0] = candidate[0];
1693 array[3 * (vertexCount + extraIndex) + 1] = candidate[1];
1694 array[3 * (vertexCount + extraIndex) + 2] = candidate[2];
1705 auto buff =
new TBuffer3D(TBuffer3DTypes::kGeneric, nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols);
1706 if (buff !=
nullptr) {
1715 std::cout <<
"=== BVH surface solid " << GetName() <<
" having " <<
GetNsurfaces() <<
" bounded surfaces\n";
1722 std::cout <<
"\n model tolerance: ";
1723 if (fModelTolerance > 0.) {
1724 std::cout << fModelTolerance <<
" cm (from the source model)";
1726 std::cout <<
"not stated";
1742 std::cout <<
"\n rim isolation: max " <<
GetMaxRimIsolation() <<
" cm (chord resolution "
1754 int coordinateIndex = 0;
1756 points[coordinateIndex++] =
vertex.xCoord;
1757 points[coordinateIndex++] =
vertex.yCoord;
1758 points[coordinateIndex++] =
vertex.zCoord;
1764 int coordinateIndex = 0;
1766 points[coordinateIndex++] =
vertex.xCoord;
1767 points[coordinateIndex++] =
vertex.yCoord;
1768 points[coordinateIndex++] =
vertex.zCoord;
1774 const int color = GetBasicColor();
1775 int* segs = buff.fSegs;
1776 int* pols = buff.fPols;
1777 int segmentDataIndex = 0;
1778 int polygonDataIndex = 0;
1779 int segmentIndex = 0;
1781 pols[polygonDataIndex++] =
color;
1782 pols[polygonDataIndex++] = 3;
1783 for (
int triangleEdge = 0; triangleEdge < 3; ++triangleEdge) {
1784 const int nextTriangleEdge = (triangleEdge + 1) % 3;
1785 segs[segmentDataIndex++] =
color;
1786 segs[segmentDataIndex++] = triangle[triangleEdge];
1787 segs[segmentDataIndex++] = triangle[nextTriangleEdge];
1788 pols[polygonDataIndex + 2 - triangleEdge] = segmentIndex++;
1790 polygonDataIndex += 3;
1796 static TBuffer3D
buffer(TBuffer3DTypes::kGeneric);
1798 FillBuffer3D(
buffer, reqSections, localFrame);
1805 if (reqSections & TBuffer3D::kRawSizes) {
1806 if (
buffer.SetRawSizes(nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols)) {
1807 buffer.SetSectionsValid(TBuffer3D::kRawSizes);
1810 if ((reqSections & TBuffer3D::kRaw) &&
buffer.SectionsValid(TBuffer3D::kRawSizes)) {
1812 if (!
buffer.fLocalFrame) {
1816 buffer.SetSectionsValid(TBuffer3D::kRaw);
1828 if (fImpl->
bvh ==
nullptr) {
1833 const Vec3 testPoint = makeVec3(point);
1842 testPoint, [&](
const BoundedSurface& surface) { return surface.containsPointOnSurface(testPoint); })) {
1846 return containsByParity(point,
true);
1854 const Vec3 testPoint = makeVec3(point);
1855 for (
const auto& surface : fImpl->
surfaces) {
1869 const Vec3 testPoint = makeVec3(point);
1870 for (
const auto& surface : fImpl->
surfaces) {
1876 return containsByParity(point,
false);
1879bool O2BVHSurfaceSolid::containsByParity(
const Double_t* point,
bool useBVH)
const
1882 const Vec3 testPoint = makeVec3(point);
1884 bool ambiguous =
false;
1885 const bool answer = fImpl->
parityAlong(testPoint, kContainsTestDirection, useBVH, &ambiguous);
1897 std::vector<ContainsCrossing>& bvhCrossings,
1898 std::vector<ContainsCrossing>& loopCrossings)
const
1901 kContainsTestDirection.
zCoord};
1906 std::vector<ContainsCrossing>& bvhCrossings,
1907 std::vector<ContainsCrossing>& loopCrossings)
const
1909 bvhCrossings.clear();
1910 loopCrossings.clear();
1914 const Vec3 testPoint = makeVec3(point.data());
1915 const Vec3 testDirection =
normalized(makeVec3(direction.data()));
1917 auto collect = [&](std::vector<RayHit>& hits, std::vector<ContainsCrossing>& out) {
1918 std::sort(hits.begin(), hits.end(),
1919 [](
const RayHit&
first,
const RayHit& second) { return first.distance < second.distance; });
1920 out.reserve(hits.size());
1921 for (
const auto& hit : hits) {
1926 std::vector<RayHit> loopHits;
1927 for (
const auto& surface : fImpl->
surfaces) {
1930 collect(loopHits, loopCrossings);
1932 if (fImpl->
bvh !=
nullptr) {
1933 std::vector<RayHit> bvhHits;
1937 collect(bvhHits, bvhCrossings);
1942 Double_t* safe)
const
1944 if (iact < 3 && safe !=
nullptr) {
1945 *safe =
Safety(point, kFALSE);
1947 return TGeoShape::Big();
1949 if (iact == 1 && stepmax < *safe) {
1950 return TGeoShape::Big();
1954 return TGeoShape::Big();
1956 if (fImpl->
bvh ==
nullptr) {
1962 const Double_t halfLengths[3] = {fDX, fDY, fDZ};
1963 for (
int dimension = 0; dimension < 3; ++dimension) {
1964 const Double_t
lower = fOrigin[dimension] - halfLengths[dimension];
1965 const Double_t
upper = fOrigin[dimension] + halfLengths[dimension];
1968 return TGeoShape::Big();
1972 return fImpl->
nearestCrossing<
true>(makeVec3(point), makeVec3(dir), stepmax);
1976 Double_t* safe)
const
1978 if (iact < 3 && safe !=
nullptr) {
1979 *safe =
Safety(point, kTRUE);
1981 return TGeoShape::Big();
1983 if (iact == 1 && stepmax < *safe) {
1984 return TGeoShape::Big();
1988 return TGeoShape::Big();
1990 if (fImpl->
bvh ==
nullptr) {
1994 return fImpl->
nearestCrossing<
false>(makeVec3(point), makeVec3(dir), stepmax);
2000 return TGeoShape::Big();
2008 return TGeoShape::Big();
2015 gRayTMaxPruning =
enable;
2020 return gRayTMaxPruning;
2025 gRayCandidateCount = 0;
2030 return gRayCandidateCount;
2035 gSafetyCandidateCount = 0;
2040 return gSafetyCandidateCount;
2045 gSafetyBoundUnsound =
enable;
2050 return gSafetyBoundUnsound;
2057 return TGeoShape::Big();
2060 return std::nextafter(std::sqrt(bestDistanceSq), 0.);
2066 return TGeoShape::Big();
2069 return std::nextafter(std::sqrt(bestDistanceSq), 0.);
2074 computeNormalFrom(point, dir,
norm,
false);
2079 computeNormalFrom(point, dir,
norm,
true);
2082void O2BVHSurfaceSolid::computeNormalFrom(
const Double_t* point,
const Double_t* dir, Double_t*
norm,
2092 const Vec3 testPoint = makeVec3(point);
2093 size_t closestIndex = fImpl->
surfaces.size();
2100 if (closestIndex >= fImpl->
surfaces.size()) {
2107 Vec3 normal = fImpl->
surfaces[closestIndex]->normalAt(testPoint);
2108 if (dir !=
nullptr) {
2109 const Vec3 direction = makeVec3(dir);
2110 if (
dot(normal, direction) < 0.) {
2111 normal = normal * -1.;
2121 double capacity = 0.;
2122 for (
const auto& surface : fImpl->
surfaces) {
2125 return std::abs(capacity);
2128bool O2BVHSurfaceSolid::RebuildFromRecords()
2132 std::vector<BVHSurfaceRecord>
records;
2137 const auto discard = [
this]() {
2145 Error(
"RebuildFromRecords",
"Shape %s carries no surface records, so it stays undefined and not navigable.",
2150 for (
size_t recordIndex = 0; recordIndex <
records.size(); ++recordIndex) {
2151 const auto& record =
records[recordIndex];
2153 if (expectedScalars < 0 || record.
scalars.size() !=
static_cast<size_t>(expectedScalars)) {
2154 Error(
"RebuildFromRecords",
"Shape %s: surface record %d has kind %d with %d scalar(s), expected %d", GetName(),
2155 static_cast<int>(recordIndex), record.
kind,
static_cast<int>(record.
scalars.size()), expectedScalars);
2164 std::vector<PlanarBoundaryCurve> outerWire;
2165 std::vector<std::vector<PlanarBoundaryCurve>> innerWires;
2166 std::vector<Point2D> outerPolygon;
2167 std::vector<std::vector<Point2D>> innerPolygons;
2169 ? loadPolygonWires(record, outerPolygon, innerPolygons)
2170 : loadCurveWires(record, outerWire, innerWires);
2174 Error(
"RebuildFromRecords",
"Shape %s: surface record %d has inconsistent wire sizes", GetName(),
2175 static_cast<int>(recordIndex));
2177 switch (record.
kind) {
2186 record.
innerWall, outerWire, innerWires)
2192 record.
innerWall, outerWire, innerWires)
2198 record.
innerWall, outerWire, innerWires)
2204 record.
innerWall, outerWire, innerWires)
2215 Error(
"RebuildFromRecords",
2216 "Shape %s: surface record %d (kind %d) did not rebuild, so the shape is discarded and stays undefined.",
2217 GetName(),
static_cast<int>(recordIndex), record.
kind);
2233void O2BVHSurfaceSolid::Streamer(TBuffer&
buffer)
2235 if (
buffer.IsReading()) {
2236 buffer.ReadClassBuffer(O2BVHSurfaceSolid::Class(),
this);
2237 RebuildFromRecords();
2239 buffer.WriteClassBuffer(O2BVHSurfaceSolid::Class(),
this);
header::DataOrigin origin
std::vector< o2::soa::IndexRecord > records
Private analytic bounded surfaces, trim wires and closure checks behind O2BVHSurfaceSolid.
std::unique_ptr< expressions::Node > node
ClassImp(O2BVHSurfaceSolid)
Double_t DistFromOutside_Loop(const Double_t *point, const Double_t *dir, Double_t stepmax=TGeoShape::Big()) const
Non-BVH DistFrom* over all surfaces: the oracles the BVH paths must match exactly.
@ Reliable
closed, consistently oriented 2-manifold: parity is well defined
@ Undetermined
CloseShape() has not run yet: no diagnostics exist.
bool AddCylindricalSurface(const Point3D ¢erPoint, const Point3D &axis, const Point3D &referenceAxisU, double radius, double heightMin, double heightMax, double phiStart=0., double phiSweep=6.283185307179586, bool innerWall=false)
Add a cylindrical wall of radius around axis over a height range and a phi sweep; innerWall points th...
double GetRimMatchTolerance() const
The declared rim match tolerance in cm, the model's own or a fallback: the floor of each chord's matc...
void Print(Option_t *option="") const override
static void ResetSafetyCandidateCounter()
Per-thread count of surfaces handed to distanceSqToPatch by Safety and ComputeNormal since the last r...
TBuffer3D * MakeBuffer3D() const override
static bool GetSafetyBoundUnsoundForTest()
int GetReversedSourceEdgeCount() const
bool ContainsAlongDirection(const Double_t *point, const Double_t *direction) const
Diagnostic: the parity answer for one explicit direction, bypassing Contains()'s re-shoot policy.
void GetMeshNumbers(int &nvert, int &nsegs, int &npols) const override
bool Contains_Loop(const Double_t *point) const
int GetNonManifoldEdgeCount() const
Double_t DistFromInside_Loop(const Double_t *point, const Double_t *dir, Double_t stepmax=TGeoShape::Big()) const
double GetUnmatchedRimLength() const
int GetRimCount() const
Rim counts: total, and split by the same four states as the edge counters above.
const TBuffer3D & GetBuffer3D(int reqSections, Bool_t localFrame) const override
bool HasEdgeIdentity() 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
int GetReversedEdgeCount() const
int GetNonManifoldRimCount() const
static void SetSafetyBoundUnsoundForTest(bool enable)
Test-only sabotage: prune on the distance to the box centre, which bounds nothing,...
void CloseShape(bool check=true)
Finalize the shape: bounding box, display mesh, BVH and closure diagnostics, reported when check is s...
void ComputeNormal_Loop(const Double_t *point, const Double_t *dir, Double_t *norm) const
static bool GetRayTMaxPruning()
int CountBVHRayCandidates(const Point3D &point, const Point3D &direction) const
Test hook: distinct surfaces whose cover boxes the ray traverses; -1 without a BVH.
static long long GetSafetyCandidateCount()
double GetRimChordResolution() const
double GetMaxRimIsolation() const
int GetMatchedRimCount() const
void SetPoints(double *points) const override
int GetDegenerateSourceEdgeCount() const
Double_t Safety_Loop(const Double_t *point, Bool_t in=kTRUE) const
Non-BVH Safety/ComputeNormal over all surfaces: the oracles the BVH traversal must match bit for bit.
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
void ComputeNormal(const Double_t *point, const Double_t *dir, Double_t *norm) const override
int GetBoundarySourceEdgeCount() const
Bool_t GetPointsOnSegments(Int_t npoints, Double_t *array) const override
Fill array with npoints points on the solid's exact boundary; kFALSE below GetNmeshVertices() so ROOT...
bool Contains(const Double_t *point) const override
void SetSegsAndPols(TBuffer3D &buff) const override
void GetSurfaceCapacityContributions(std::vector< double > &contributions) const
Each face's divergence-theorem contribution to Capacity(), in record order.
Double_t Capacity() const override
std::array< double, 3 > Point3D
int GetNonManifoldSourceEdgeCount() const
bool GetBVHRootBounds(Point3D &lower, Point3D &upper) const
Fill the BVH root-node bounding box; returns false when no BVH has been built.
NavigationReliability GetNavigationReliability() const
The reliability state derived from the last CloseShape(); Undetermined before it has run.
std::array< double, 2 > Point2D
int GetMeasuredSharedEdgeCount() const
static void SetRayTMaxPruning(bool enable)
Ray tmax tightening in the distance queries, on by default; it never changes an answer....
int GetBoundaryRimCount() const
int GetSourceEdgeCount() const
Distinct source edges and their incidence: shared, boundary, non-manifold, reversed and degenerate.
bool IsOrientationConsistent() const
bool AddCurvedPlanarSurface(const Point3D &origin, const Point3D &axisU, const Point3D &axisV, const std::vector< PlanarBoundaryCurve > &outerWire, const std::vector< std::vector< PlanarBoundaryCurve > > &innerWires={})
Add an exact planar surface bounded by line/arc wires; axisU and axisV are orthonormal and axisU x ax...
static long long GetRayCandidateCount()
int GetSharedSourceEdgeCount() const
Double_t Safety(const Double_t *point, Bool_t in=kTRUE) const override
The distance to the nearest patch, rounded down by one ulp so that Safety is never too large.
int GetNmeshVertices() const override
double GetMaxSharedEdgeDeviation() const
Largest Hausdorff distance between the two faces' realisations of one shared edge,...
static const char * GetNavigationReliabilityName(NavigationReliability reliability)
const std::vector< RimReport > & GetRimReports() const
bool AddConicalSurface(const Point3D ¢erPoint, const Point3D &axis, const Point3D &referenceAxisU, double radiusAtMin, double radiusAtMax, double heightMin, double heightMax, double phiStart=0., double phiSweep=6.283185307179586, bool innerWall=false)
Add a conical wall whose radius runs linearly from radiusAtMin to radiusAtMax; one radius may be zero...
static constexpr double kSurfacePointTolerance
void SetModelTolerance(double toleranceCm)
~O2BVHSurfaceSolid() override
int GetReversedRimCount() const
bool SetSurfaceBoundaryEdges(int surfaceIndex, const std::vector< unsigned int > &edgeIds, const std::vector< unsigned char > &edgeFlags)
Attach surface surfaceIndex's edge identities in trim-curve order; false on a bad index or mismatched...
bool AddSphericalSurface(const Point3D ¢er, const Point3D &polarAxis, const Point3D &referenceAxisU, double radius, double thetaMin=0., double thetaMax=3.141592653589793, double phiStart=0., double phiSweep=6.283185307179586, bool innerWall=false)
Add a spherical surface of radius trimmed to a theta range and a phi sweep; the defaults give a full ...
bool AddToroidalSurface(const Point3D ¢erPoint, const Point3D &axis, const Point3D &referenceAxisU, double majorRadius, double minorRadius, double phiStart=0., double phiSweep=6.283185307179586, double tubeStart=0., double tubeSweep=6.283185307179586, bool innerWall=false)
Add a toroidal surface trimmed to a phiRing x phiTube rectangle; the defaults give a full torus,...
double GetTotalRimLength() const
Summed trim-boundary length, and the part with no other face within the match band,...
void DescribeContainsCrossings(const Point3D &point, std::vector< ContainsCrossing > &bvhCrossings, std::vector< ContainsCrossing > &loopCrossings) const
Diagnostic: the parity ray's crossings at point from the BVH and from the loop, sorted by distance.
bool AddPlanarSurface(const Point3D &origin, const Point3D &axisU, const Point3D &axisV, const std::vector< Point2D > &outerWire, const std::vector< std::vector< Point2D > > &innerWires={})
static void ResetRayCandidateCounter()
Per-thread count of surfaces handed to the BVH leaf callback by DistFrom* since the last reset.
@ kEdgeAnchored
entry i is trim curve i of this face, so it can be measured
@ kEdgeReversed
the face runs against the edge's own direction
@ kEdgeDegenerate
cone apex / sphere pole: a point, so it has no second face
bool HasBVH() const
Whether the BVH acceleration structure has been built (after CloseShape).
int GetUnmeasuredSharedEdgeCount() const
void ComputeBBox() override
int GetBoundaryEdgeCount() const
Per-chord closure counts: diagnostics only; GetNavigationReliability() reads the rim counts below.
Abstract analytic surface patch: one support surface plus its trim, with the kernels the navigation n...
virtual void appendIntersections(const Vec3 &rayOrigin, const Vec3 &rayDirection, double minDistance, double maxDistance, std::vector< RayHit > &hits) const =0
Append every hit of the ray with the trimmed patch in [minDistance, maxDistance], with the outward no...
virtual bool containsPointOnSurface(const Vec3 &point) const =0
True if the 3D point lies on the trimmed patch within tolerance.
virtual double capacityContribution() const =0
Signed divergence-theorem contribution to the enclosed volume.
virtual Vec3 normalAt(const Vec3 &point) const =0
Outward-oriented normal at (or nearest to) the given point.
virtual double distanceSqToPatch(const Vec3 &point) const =0
Squared distance from a 3D point to the trimmed patch (used for Safety).
GLuint const GLchar * name
GLsizei GLsizei GLchar * source
GLsizei GLsizei GLfloat distance
GLsizei const GLfloat * value
GLint GLenum GLboolean normalized
GLint GLenum GLboolean GLsizei stride
GLsizei const GLint * box
void report(gsl::span< o2::InteractionTimeRecord > irs, int threshold, bool verbose)
constexpr double wireJoinToleranceFor(double modelTolerance)
The wire-join band for a model with a declared tolerance: that tolerance when looser than kWireJoinTo...
double distanceSq(const Vec2 &firstPoint, const Vec2 &secondPoint)
double dot(const Vec3 &firstVector, const Vec3 &secondVector)
bool sameIntersection(double firstDistance, double secondDistance)
constexpr double kRayTolerance
minimum positive ray parameter t
constexpr double kBVHBoxTolerance
Widening of the BVH leaf boxes before the outward float rounding; it dominates every navigation lengt...
ClosureReport validateClosure(const std::vector< std::unique_ptr< BoundedSurface > > &surfaces, double modelTolerance=0.)
Validate closure and orientation of surfaces by half-edges, measure the rims, and count edge identiti...
double normSq(const Vec3 &vector)
double component(const Vec3 &vector, int dimension)
constexpr double kTolerance
generic length tolerance
constexpr double kIntersectionTolerance
clustering of near-equal intersections
double norm(const Vec3 &vector)
void check(const std::vector< std::string > &arguments, const std::vector< ConfigParamSpec > &workflowOptions, const std::vector< DeviceSpec > &deviceSpecs, CheckMatrix &matrix)
void append(const char *msg, std::string &to)
bool visitPointCandidates(const Vec3 &point, SurfaceVisitor &&visitor) const
Visit every surface whose widened leaf box holds the point, until the visitor returns true.
std::vector< std::unique_ptr< BoundedSurface > > surfaces
std::vector< int > leafSurface
The surface of each BVH leaf primitive, in leaf order.
bool commit(std::unique_ptr< BoundedSurface > surface, BVHSurfaceRecord record, std::vector< BVHSurfaceRecord > &records)
Append a built surface, and to records the record that rebuilds it.
double nearestPatchDistanceSqLoop(const Vec3 &point, size_t *closestIndex) const
The brute-force nearest patch and its index; the lowest index wins an exact tie, which ComputeNormal ...
bool containsByVote(const Vec3 &point, bool useBVH, bool *allTiedOnBoundary=nullptr) const
std::vector< std::array< int, 3 > > displayTriangles
void visitRayCandidates(const Vec3 &rayOrigin, const Vec3 &rayDirection, SurfaceVisitor &&visitor) const
double nearestPatchDistanceSq(const Vec3 &point, size_t *closestIndex) const
std::vector< Vec3 > displayVertices
bool parityAlong(const Vec3 &point, const Vec3 &direction, bool useBVH, bool *ambiguous=nullptr) const
Parity of the ray's crossings with the surface set, through the BVH or the loop; ambiguous reports a ...
void buildBVH()
Build the BVH over the surfaces' cover boxes, widened by kBVHBoxTolerance and rounded outward to floa...
size_t surfaceOfPrimitive(size_t primitive) const
The surface a BVH leaf primitive belongs to.
std::unique_ptr< BVH > bvh
double nearestCrossing(const Vec3 &rayOrigin, const Vec3 &rayDirection, double stepmax) const
std::vector< RimReport > rimReports
double anchorSeedDistanceSq(const Vec3 &point) const
double nearestCrossingLoop(const Vec3 &rayOrigin, const Vec3 &rayDirection, double stepmax) const
Same query without the BVH: visit every surface. Oracle and baseline for nearestCrossing.
std::vector< int > displayTriangleSurface
The surface each display triangle came from, parallel to displayTriangles; see GetPointsOnSegments.
bool refuseIfDefined(const O2BVHSurfaceSolid &owner, const char *method) const
True, after reporting it for method of owner, if the shape is defined and takes no more surfaces.
void collectSafetyAnchors()
Subsample the display vertices, which lie on their patches, as safety anchors.
bool reliable
GetNavigationReliability() is Reliable; set by CloseShape.
std::vector< Vec3 > safetyAnchors
A few on-patch display vertices, seeding the nearest-patch traversal's upper bound; see anchorSeedDis...
One boundary curve of a BVHSurfaceRecord in the flat form ROOT streams: a segment,...
std::vector< double > poles
B-spline control points, flattened (u, v) pairs.
std::vector< double > knots
B-spline clamped flat knot vector.
int degree
B-spline degree.
std::vector< double > weights
B-spline weights (empty => non-rational)
int kind
PlanarBoundaryCurve::Kind: 0 = Line, 1 = Arc, 2 = BSpline.
The persistent record of one successful Add*Surface call; reading a solid back replays the records.
std::vector< int > wireSizes
std::vector< BVHSurfaceCurveRecord > curves
std::vector< double > scalars
std::vector< double > polygonPoints
The wires, outer first: PlanarPolygon stores (u, v) pairs in polygonPoints, the others curves; wireSi...
bool trimmed
the wire-trim overload was used (quadrics only)
double axisA[3]
axisU / axis / polarAxis
double axisB[3]
axisV / referenceAxisU
std::vector< unsigned char > boundaryEdgeFlags
static int expectedScalarCount(int recordKind)
How many entries scalars must hold for kind, or -1 for an unknown kind.
std::vector< unsigned int > boundaryEdgeIds
Sidecar v3 boundary edge identities in curve order: an edge-table index and a BoundaryEdgeFlag byte; ...
double origin[3]
origin / centerPoint / center
One boundary curve in the surface's local (u, v) frame: a line segment, a circular arc or a clamped (...
std::vector< double > weights
B-spline weights (empty ⇒ non-rational)
std::vector< double > knots
B-spline clamped flat knot vector.
std::vector< Point2D > poles
B-spline control points.
int degree
B-spline degree.
One trim loop of one face as the closure measurement saw it, naming the rim and its worst chord.
int surface
index into GetSurfaceRecords() of the face owning this rim
Whether a set of bounded surfaces forms a closed, consistently oriented 2-manifold,...
double rimChordResolution
int sharedEdgesUnmeasured
int rims
total number of trim loops over all faces
int reversedEdges
edges shared by two faces in the same direction
int boundaryEdges
edges present on only one face (e.g. a missing face)
bool orientationConsistent
shared edges are traversed in opposite directions
double totalRimLength
summed length in cm of every face's trim boundary
int edgeSharedCount
appearing exactly twice, opposite sense: a properly shared edge
int edgeBoundaryCount
appearing once: a face is missing on the other side
double rimEpsilon
the declared matching tolerance, in cm
std::vector< RimRecord > rimRecords
double maxSharedEdgeDeviation
Largest Hausdorff distance between two faces' realisations of a shared edge, in cm; a measurement,...
int nonManifoldEdges
edges shared by more than two faces
int nonManifoldRims
some chord has two or more other faces within rimEpsilon
bool closed
every boundary edge is shared by exactly two faces
double unmatchedRimLength
how much of it has no other face within the match band, cm
bool edgeIdentityAvailable
int edgeReversedCount
appearing exactly twice, but with the same sense
int sharedEdgesMeasured
shared edges both of whose faces could be sampled
int edgeNonManifoldCount
appearing three or more times
int edgeIncidences
distinct edge identifiers seen over all faces
static Curve2D makeBSpline(int splineDegree, std::vector< Vec2 > splinePoles, std::vector< double > splineWeights, std::vector< double > splineKnots)
static Curve2D makeLine(const Vec2 &start, const Vec2 &end)
static Curve2D makeArc(const Vec2 &arcCenter, double arcRadius, double arcStartAngle, double arcEndAngle)
One ray/surface intersection: the ray parameter and the outward normal; a quadric patch can give seve...
bool onTrimBoundary
The hit lies within the trim's on-boundary band, so its inside/outside side is a tie-break,...
One trim loop of one face as measureRimClosure saw it, naming the rim and its worst chord.
A 2D point/vector in a surface's parametric (u, v) domain.
A 3D point/vector in the solid's local frame.