23#include "TGeoMatrix.h"
24#include "TGeoVolume.h"
46 return "INTERPENETRATING";
64MasterBox masterBox(
const TGeoShape* shape,
const TGeoMatrix* matrix,
double pad)
67 const auto* boundingBox =
dynamic_cast<const TGeoBBox*
>(shape);
68 if (boundingBox ==
nullptr) {
71 const double*
origin = boundingBox->GetOrigin();
72 const double halfLengths[3] = {boundingBox->GetDX(), boundingBox->GetDY(), boundingBox->GetDZ()};
73 for (
int dimension = 0; dimension < 3; ++dimension) {
74 box.lower[dimension] = std::numeric_limits<double>::max();
75 box.upper[dimension] = -std::numeric_limits<double>::max();
77 for (
int corner = 0; corner < 8; ++corner) {
78 const double local[3] = {
origin[0] + ((corner & 1) ? halfLengths[0] : -halfLengths[0]),
79 origin[1] + ((corner & 2) ? halfLengths[1] : -halfLengths[1]),
80 origin[2] + ((corner & 4) ? halfLengths[2] : -halfLengths[2])};
81 double master[3] = {0., 0., 0.};
82 matrix->LocalToMaster(local, master);
83 for (
int dimension = 0; dimension < 3; ++dimension) {
84 box.lower[dimension] = std::min(
box.lower[dimension], master[dimension]);
85 box.upper[dimension] = std::max(
box.upper[dimension], master[dimension]);
88 for (
int dimension = 0; dimension < 3; ++dimension) {
89 box.lower[dimension] -= pad;
90 box.upper[dimension] += pad;
96bool boxesOverlap(
const MasterBox&
first,
const MasterBox& second)
98 if (!
first.valid || !second.valid) {
101 for (
int dimension = 0; dimension < 3; ++dimension) {
102 if (
first.upper[dimension] < second.lower[dimension] || second.upper[dimension] <
first.lower[dimension]) {
110inline double halton(
unsigned int index,
unsigned int base)
113 double fraction = 1.;
124bool containmentFlips(
const TGeoShape* shape,
const double* point,
double eps)
126 const auto flipsAlong = [&](
const double* direction) {
129 for (
int axis = 0; axis < 3; ++axis) {
130 below[axis] = point[axis] - eps * direction[axis];
131 above[axis] = point[axis] + eps * direction[axis];
133 return shape->Contains(below) != shape->Contains(above);
135 const double zAxis[3] = {0., 0., 1.};
136 double normal[3] = {0., 0., 0.};
137 shape->ComputeNormal(point, zAxis, normal);
138 const double length = std::sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
139 if (std::isfinite(
length) &&
length > 0.5 && flipsAlong(normal)) {
142 for (
int axis = 0; axis < 3; ++axis) {
143 double direction[3] = {0., 0., 0.};
144 direction[axis] = 1.;
145 if (flipsAlong(direction)) {
155 std::vector<double>& points,
int& rejected,
double& worstResidual,
156 bool* usedPointsOnSegments)
161 if (usedPointsOnSegments !=
nullptr) {
162 *usedPointsOnSegments =
false;
164 if (shape ==
nullptr || npoints <= 0) {
168 int meshVertices = 0;
169 int meshSegments = 0;
170 int meshPolygons = 0;
171 shape->GetMeshNumbers(meshVertices, meshSegments, meshPolygons);
174 const int capacity = std::max(npoints, meshVertices);
175 std::vector<double>
raw(3 *
static_cast<size_t>(std::max(capacity, 1)), 0.);
177 if (shape->GetPointsOnSegments(npoints,
raw.data())) {
179 if (usedPointsOnSegments !=
nullptr) {
180 *usedPointsOnSegments =
true;
183 if (meshVertices <= 0) {
186 shape->SetPoints(
raw.data());
187 rawCount = meshVertices;
191 const bool flatCSG =
dynamic_cast<const O2FlatCSG*
>(shape) !=
nullptr;
192 points.reserve(3 *
static_cast<size_t>(rawCount));
194 const double* candidate = &
raw[3 *
static_cast<size_t>(
index)];
197 const double residual = shape->Safety(candidate, shape->Contains(candidate));
198 if (!(residual <= residualTolerance) || (flatCSG && !containmentFlips(shape, candidate, residualTolerance))) {
202 worstResidual = std::max(worstResidual, residual);
203 points.push_back(candidate[0]);
204 points.push_back(candidate[1]);
205 points.push_back(candidate[2]);
207 return static_cast<int>(points.size() / 3);
215struct DirectionResult {
223DirectionResult probeDirection(
const std::vector<double>& points,
const TGeoMatrix* matFrom,
224 const TGeoShape*
target,
const TGeoMatrix* matTo,
double depthTolerance)
227 const size_t count = points.size() / 3;
229 double master[3] = {0., 0., 0.};
230 double local[3] = {0., 0., 0.};
231 matFrom->LocalToMaster(&points[3 *
index], master);
232 matTo->MasterToLocal(master, local);
233 if (
target->Contains(local)) {
236 if (
depth > depthTolerance) {
241 std::memcpy(
result.deepestMaster, master, 3 *
sizeof(
double));
251OverlapPair assemblePair(
const std::string& nameA,
const std::vector<double>& pointsA,
const TGeoShape* shapeA,
252 const TGeoMatrix* matA,
const std::string& nameB,
const std::vector<double>& pointsB,
253 const TGeoShape* shapeB,
const TGeoMatrix* matB,
const OverlapOptions& options)
258 pair.sampledA =
static_cast<int>(pointsA.size() / 3);
259 pair.sampledB =
static_cast<int>(pointsB.size() / 3);
261 const DirectionResult aInB = probeDirection(pointsA, matA, shapeB, matB, options.depthTolerance);
262 const DirectionResult bInA = probeDirection(pointsB, matB, shapeA, matA, options.depthTolerance);
264 pair.pointsAInsideB = aInB.contained;
265 pair.pointsBInsideA = bInA.contained;
266 pair.deepPointsAInsideB = aInB.deep;
267 pair.deepPointsBInsideA = bInA.deep;
269 if (aInB.maxDepth >= bInA.maxDepth) {
270 pair.depthCm = aInB.maxDepth;
271 std::copy(aInB.deepestMaster, aInB.deepestMaster + 3, pair.deepestPoint.begin());
272 pair.deepestPointFrom = nameA;
274 pair.depthCm = bInA.maxDepth;
275 std::copy(bInA.deepestMaster, bInA.deepestMaster + 3, pair.deepestPoint.begin());
276 pair.deepestPointFrom = nameB;
281 const bool allAInside = pair.sampledA > 0 && aInB.contained == pair.sampledA && aInB.deep == pair.sampledA;
282 const bool allBInside = pair.sampledB > 0 && bInA.contained == pair.sampledB && bInA.deep == pair.sampledB;
284 if (allAInside || allBInside) {
286 }
else if (aInB.deep > 0 || bInA.deep > 0) {
288 }
else if (aInB.contained > 0 || bInA.contained > 0) {
292 const double separation = std::min(aInB.minSeparation, bInA.minSeparation);
293 if (separation < std::numeric_limits<double>::max()) {
294 pair.separationCm = separation;
301void estimateSharedVolume(
const TGeoShape* shapeA,
const TGeoMatrix* matA,
const TGeoShape* shapeB,
302 const TGeoMatrix* matB,
int samples, OverlapPair& pair)
304 const MasterBox boxA = masterBox(shapeA, matA, 0.);
305 const MasterBox boxB = masterBox(shapeB, matB, 0.);
306 if (!boxA.valid || !boxB.valid) {
311 double boxVolume = 1.;
312 for (
int dimension = 0; dimension < 3; ++dimension) {
313 lower[dimension] = std::max(boxA.lower[dimension], boxB.lower[dimension]);
314 upper[dimension] = std::min(boxA.upper[dimension], boxB.upper[dimension]);
315 boxVolume *= std::max(0.,
upper[dimension] -
lower[dimension]);
317 if (!(boxVolume > 0.)) {
321 for (
int sample = 0; sample <
samples; ++sample) {
322 const double master[3] = {
lower[0] + (
upper[0] -
lower[0]) * halton(sample + 1, 2),
326 matA->MasterToLocal(master, local);
327 if (!shapeA->Contains(local)) {
330 matB->MasterToLocal(master, local);
331 if (shapeB->Contains(local)) {
335 const double fraction = double(hits) /
samples;
336 pair.sharedVolumeHits = hits;
337 pair.sharedVolumeCm3 = fraction * boxVolume;
338 pair.sharedVolumeErrCm3 = std::sqrt(std::max(1.,
double(hits))) /
samples * boxVolume;
344 const TGeoShape* shapeB,
const TGeoMatrix* matB,
const std::string& nameB,
350 if (shapeA ==
nullptr || shapeB ==
nullptr || matA ==
nullptr || matB ==
nullptr) {
356 double residualA = 0.;
357 double residualB = 0.;
358 std::vector<double> pointsA;
359 std::vector<double> pointsB;
362 pair = assemblePair(nameA, pointsA, shapeA, matA, nameB, pointsB, shapeB, matB, options);
365 estimateSharedVolume(shapeA, matA, shapeB, matB, options.
volumeSamples, pair);
372 const auto startTime = std::chrono::steady_clock::now();
374 if (volume ==
nullptr) {
377 const int daughters = volume->GetNdaughters();
379 census.
nPairsTotal = daughters * (daughters - 1) / 2;
381 std::vector<const TGeoShape*> shapes(daughters,
nullptr);
382 std::vector<const TGeoMatrix*> matrices(daughters,
nullptr);
383 std::vector<std::string> names(daughters);
384 std::vector<MasterBox> boxes(daughters);
385 std::vector<std::vector<double>> points(daughters);
389 shapes[
index] =
node->GetVolume()->GetShape();
391 names[
index] =
node->GetVolume()->GetName();
396 report.shapeClass = shapes[
index] !=
nullptr ? shapes[
index]->ClassName() :
"none";
398 bool usedSegments =
false;
401 report.usedPointsOnSegments = usedSegments;
408 for (
int second =
first + 1; second < daughters; ++second) {
409 if (!boxesOverlap(boxes[
first], boxes[second])) {
415 points[second], shapes[second], matrices[second], options);
435 census.
pairs.push_back(pair);
440 if (options.
checkExtrusion && volume->GetShape() !=
nullptr && !volume->IsAssembly()) {
441 TGeoIdentity identity;
445 pair.
nameB = volume->GetName();
447 const TGeoShape* mother = volume->GetShape();
450 double worstMaster[3] = {0., 0., 0.};
451 for (
size_t point = 0; point < points[
index].size() / 3; ++point) {
452 double master[3] = {0., 0., 0.};
453 matrices[
index]->LocalToMaster(&points[
index][3 * point], master);
454 if (!mother->Contains(master)) {
455 const double depth = mother->Safety(master, kFALSE);
460 std::memcpy(worstMaster, master, 3 *
sizeof(
double));
470 std::copy(worstMaster, worstMaster + 3, pair.
deepestPoint.begin());
478 std::chrono::duration<double>(std::chrono::steady_clock::now() - startTime).count();
header::DataOrigin origin
std::unique_ptr< expressions::Node > node
o2::raw::RawFileWriter * raw
GLuint GLsizei GLsizei * length
GLint GLint GLsizei GLsizei GLsizei depth
GLsizei const GLint * box
void report(gsl::span< o2::InteractionTimeRecord > irs, int threshold, bool verbose)
OverlapCensus CheckWorldOverlaps(const TGeoVolume *volume, const OverlapOptions &options=OverlapOptions())
Census every pair of volume's immediate daughters, and optionally each daughter against volume.
const char * OverlapVerdictName(OverlapVerdict verdict)
OverlapVerdict
Whether two placed solids may legally coexist: disjoint and touching are legal, interpenetrating and ...
@ Contained
every sampled boundary point of the smaller solid is inside the other
@ Disjoint
no sampled boundary point of either solid lies inside the other
@ Interpenetrating
a boundary point of one solid lies strictly inside the other: illegal
@ Touching
boundary points coincide, but none is deeper than the depth tolerance
OverlapPair CheckPairOverlap(const TGeoShape *shapeA, const TGeoMatrix *matA, const std::string &nameA, const TGeoShape *shapeB, const TGeoMatrix *matB, const std::string &nameB, const OverlapOptions &options=OverlapOptions())
Test one placed pair. matA / matB take each shape's local frame to the common frame.
int SampleBoundaryPoints(const TGeoShape *shape, int npoints, double residualTolerance, std::vector< double > &points, int &rejected, double &worstResidual, bool *usedPointsOnSegments=nullptr)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::vector< OverlapSolidReport > solids
int nPairsTotal
N (N - 1) / 2.
std::vector< OverlapPair > extrusions
int nPairsTested
after the bounding-box rejection
std::vector< OverlapPair > pairs
only the pairs that survived the bounding-box rejection
double depthTolerance
A containment shallower than this is a shared boundary, not an overlap. In cm.
double padCm
Bounding-box inflation before the pairwise rejection, in cm; it decides which disjoint pairs get a se...
int volumeSamples
Monte-Carlo samples for the shared volume of an illegal pair; 0, the default, disables the estimate.
One pair of placed solids, and everything measured about it.
int deepPointsAInsideB
... of which deeper than depthTolerance
int sampledA
accepted (on-boundary) sample counts actually used
std::string deepestPointFrom
which solid's boundary the deepest point came from
std::array< double, 3 > deepestPoint
in the master frame
One solid's sampling report; a shape with a poor display mesh shows here as reduced coverage.