14#define BOOST_TEST_MODULE Test O2FlatCSG class
15#define BOOST_TEST_MAIN
16#define BOOST_TEST_DYN_LINK
17#include <boost/test/unit_test.hpp>
43 explicit Rng(
unsigned long long seed) : mState(seed) {}
44 double uniform(
double low,
double high)
46 mState = mState * 6364136223846793005ULL + 1442695040888963407ULL;
47 const double unit =
static_cast<double>((mState >> 11) & ((1ULL << 53) - 1)) /
static_cast<double>(1ULL << 53);
48 return low + unit * (high - low);
52 unsigned long long mState;
56void planeQuadric(
const double n[3],
const double p[3],
double coeff[10])
61 coeff[6] = 0.5 *
n[0];
62 coeff[7] = 0.5 *
n[1];
63 coeff[8] = 0.5 *
n[2];
64 coeff[9] = -(
n[0] * p[0] +
n[1] * p[1] +
n[2] * p[2]);
68void zCylinderQuadric(
double r,
double coeff[10])
70 const double values[10] = {1., 0., 0., 1., 0., 0., 0., 0., 0., -
r *
r};
81void tiltedCylinderQuadric(
double r,
double coeff[10])
83 const double s = 1. / std::sqrt(3.);
84 const double d[3] = {
s,
s,
s};
87 for (
int column = 0; column < 3; ++column) {
88 a[
row][column] = (
row == column ? 1. : 0.) -
d[
row] *
d[column];
104void addBoxCell(O2FlatCSG& solid,
double dx,
double dy,
double dz)
106 const double half[3] = {dx, dy, dz};
107 const int first = solid.GetNhalfspaces();
108 for (
int axis = 0; axis < 3; ++axis) {
109 for (
int sense = -1; sense <= 1; sense += 2) {
110 double normal[3] = {0., 0., 0.};
111 double through[3] = {0., 0., 0.};
112 normal[axis] =
static_cast<double>(sense);
113 through[axis] = sense *
half[axis];
115 planeQuadric(normal, through, coeff);
116 solid.AddQuadric(1., coeff);
119 solid.AddCell(
first, 6, 8. * dx * dy * dz);
125 O2FlatCSG solid(
"box");
126 addBoxCell(solid, 3., 4., 5.);
131 Rng rng(20260824ULL);
133 for (
int trial = 0; trial < 20000; ++trial) {
134 const double point[3] = {rng.uniform(-6., 6.), rng.uniform(-7., 7.), rng.uniform(-8., 8.)};
136 if (std::abs(std::abs(point[0]) - 3.) < 1.e-9 || std::abs(std::abs(point[1]) - 4.) < 1.e-9 ||
137 std::abs(std::abs(point[2]) - 5.) < 1.e-9) {
140 BOOST_REQUIRE_EQUAL(solid.Contains_Loop(point),
reference.Contains(point));
141 BOOST_REQUIRE_EQUAL(solid.Contains(point), solid.Contains_Loop(point));
144 BOOST_CHECK_GT(scored, 19000);
151 O2FlatCSG solid(
"tube");
153 zCylinderQuadric(5., coeff);
154 solid.AddQuadric(1., coeff);
155 zCylinderQuadric(2., coeff);
156 solid.AddQuadric(-1., coeff);
157 const double up[3] = {0., 0., 1.};
158 const double down[3] = {0., 0., -1.};
159 const double top[3] = {0., 0., 7.};
160 const double bottom[3] = {0., 0., -7.};
161 planeQuadric(up,
top, coeff);
162 solid.AddQuadric(1., coeff);
163 planeQuadric(down,
bottom, coeff);
164 solid.AddQuadric(1., coeff);
165 solid.AddCell(0, 4, TMath::Pi() * (25. - 4.) * 14.);
169 for (
int trial = 0; trial < 20000; ++trial) {
170 const double point[3] = {rng.uniform(-6., 6.), rng.uniform(-6., 6.), rng.uniform(-8., 8.)};
171 const double radius = std::hypot(point[0], point[1]);
172 if (std::abs(
radius - 2.) < 1.e-9 || std::abs(
radius - 5.) < 1.e-9 ||
173 std::abs(std::abs(point[2]) - 7.) < 1.e-9) {
176 BOOST_TEST_CONTEXT(
"point = (" << point[0] <<
", " << point[1] <<
", " << point[2] <<
")")
178 BOOST_REQUIRE_EQUAL(solid.Contains_Loop(point),
reference.Contains(point));
185 O2FlatCSG solid(
"two_boxes");
186 addBoxCell(solid, 1., 1., 1.);
188 const int first = solid.GetNhalfspaces();
189 const double centre = 10.;
190 for (
int axis = 0; axis < 3; ++axis) {
191 for (
int sense = -1; sense <= 1; sense += 2) {
192 double normal[3] = {0., 0., 0.};
193 double through[3] = {centre, 0., 0.};
194 normal[axis] =
static_cast<double>(sense);
195 through[axis] += (axis == 0 ? sense * 1. : 0.);
197 through[axis] = sense * 1.;
200 planeQuadric(normal, through, coeff);
201 solid.AddQuadric(1., coeff);
204 solid.AddCell(
first, 6, 8.);
206 const double inFirst[3] = {0., 0., 0.};
207 const double inSecond[3] = {10., 0., 0.};
208 const double between[3] = {5., 0., 0.};
216 O2FlatCSG solid(
"box_dist");
217 addBoxCell(solid, 3., 4., 5.);
221 for (
int trial = 0; trial < 20000; ++trial) {
222 double point[3] = {rng.uniform(-12., 12.), rng.uniform(-12., 12.), rng.uniform(-12., 12.)};
227 dir[
index] = rng.uniform(-1., 1.);
229 norm = std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
230 }
while (norm < 1.e-3);
234 const bool inside =
reference.Contains(point);
235 if (inside !=
static_cast<bool>(solid.Contains_Loop(point))) {
238 const double mine = inside ? solid.DistFromInside_Loop(point, dir, TGeoShape::Big())
239 : solid.DistFromOutside_Loop(point, dir, TGeoShape::Big());
240 const double theirs = inside ?
reference.DistFromInside(point, dir, 3, TGeoShape::Big(),
nullptr)
241 :
reference.DistFromOutside(point, dir, 3, TGeoShape::Big(),
nullptr);
242 if (theirs >= TGeoShape::Big()) {
243 BOOST_REQUIRE_GE(mine, TGeoShape::Big());
245 BOOST_REQUIRE_SMALL(mine - theirs, 1.e-9);
254 O2FlatCSG solid(
"tube_dist");
256 zCylinderQuadric(5., coeff);
257 solid.AddQuadric(1., coeff);
258 zCylinderQuadric(2., coeff);
259 solid.AddQuadric(-1., coeff);
260 const double up[3] = {0., 0., 1.};
261 const double down[3] = {0., 0., -1.};
262 const double top[3] = {0., 0., 7.};
263 const double bottom[3] = {0., 0., -7.};
264 planeQuadric(up,
top, coeff);
265 solid.AddQuadric(1., coeff);
266 planeQuadric(down,
bottom, coeff);
267 solid.AddQuadric(1., coeff);
268 solid.AddCell(0, 4, 0.);
273 const double origin[3] = {-9., 0., 0.};
274 const double dir[3] = {1., 0., 0.};
275 BOOST_CHECK_SMALL(solid.DistFromOutside_Loop(
origin, dir, TGeoShape::Big()) - 4., 1.e-12);
277 const double inWall[3] = {-4., 0., 0.};
278 BOOST_CHECK_SMALL(solid.DistFromInside_Loop(inWall, dir, TGeoShape::Big()) - 2., 1.e-12);
280 const double inBore[3] = {0., 0., 0.};
282 BOOST_CHECK_SMALL(solid.DistFromOutside_Loop(inBore, dir, TGeoShape::Big()) - 2., 1.e-12);
285 for (
int trial = 0; trial < 20000; ++trial) {
286 double point[3] = {rng.uniform(-9., 9.), rng.uniform(-9., 9.), rng.uniform(-10., 10.)};
291 direction[
index] = rng.uniform(-1., 1.);
293 norm = std::sqrt(direction[0] * direction[0] + direction[1] * direction[1] + direction[2] * direction[2]);
294 }
while (norm < 1.e-3);
296 direction[
index] /= norm;
298 const bool inside =
reference.Contains(point);
299 if (inside !=
static_cast<bool>(solid.Contains_Loop(point))) {
302 const double mine = inside ? solid.DistFromInside_Loop(point, direction, TGeoShape::Big())
303 : solid.DistFromOutside_Loop(point, direction, TGeoShape::Big());
304 const double theirs = inside ?
reference.DistFromInside(point, direction, 3, TGeoShape::Big(),
nullptr)
305 :
reference.DistFromOutside(point, direction, 3, TGeoShape::Big(),
nullptr);
306 if (theirs >= TGeoShape::Big()) {
307 BOOST_REQUIRE_GE(mine, TGeoShape::Big());
309 BOOST_REQUIRE_SMALL(mine - theirs, 1.e-8);
318 O2FlatCSG solid(
"touching");
319 addBoxCell(solid, 1., 1., 1.);
320 const int first = solid.GetNhalfspaces();
321 const double planes[6][2][3] = {{{1., 0., 0.}, {3., 0., 0.}},
322 {{-1., 0., 0.}, {1., 0., 0.}},
323 {{0., 1., 0.}, {0., 1., 0.}},
324 {{0., -1., 0.}, {0., -1., 0.}},
325 {{0., 0., 1.}, {0., 0., 1.}},
326 {{0., 0., -1.}, {0., 0., -1.}}};
327 for (
const auto& plane : planes) {
329 planeQuadric(plane[0], plane[1], coeff);
330 solid.AddQuadric(1., coeff);
332 solid.AddCell(
first, 6, 8.);
334 const double origin[3] = {0., 0., 0.};
335 const double dir[3] = {1., 0., 0.};
336 BOOST_CHECK_SMALL(solid.DistFromInside_Loop(
origin, dir, TGeoShape::Big()) - 3., 1.e-12);
344 O2FlatCSG solid(
"tangent_ray");
346 zCylinderQuadric(5., coeff);
347 solid.AddQuadric(1., coeff);
348 const auto& cylinder = solid.GetHalfspace(0);
350 const double origin[3] = {5., 0., 0.};
351 const double dir[3] = {0., 1., 0.};
353 const int found = O2FlatCSG::HalfspaceRoots(cylinder,
origin, dir, roots);
355 BOOST_REQUIRE_EQUAL(found, 1);
357 BOOST_CHECK_SMALL(roots[0], 1.e-12);
361 const double hit[3] = {
origin[0] + roots[0] * dir[0],
origin[1] + roots[0] * dir[1],
362 origin[2] + roots[0] * dir[2]};
363 BOOST_CHECK_SMALL(O2FlatCSG::EvalHalfspace(cylinder, hit), 1.e-9);
369 O2FlatCSG solid(
"torus");
370 const double centre[3] = {0., 0., 0.};
371 const double axis[3] = {0., 0., 1.};
372 solid.AddTorus(1., centre, axis, 10., 3.);
373 solid.AddCell(0, 1, 2. * TMath::Pi() * TMath::Pi() * 10. * 9.);
377 int scoredPoints = 0;
378 for (
int trial = 0; trial < 20000; ++trial) {
379 const double point[3] = {rng.uniform(-15., 15.), rng.uniform(-15., 15.), rng.uniform(-5., 5.)};
380 const double radial = std::hypot(point[0], point[1]);
381 const double distance = std::hypot(radial - 10., point[2]) - 3.;
385 BOOST_REQUIRE_EQUAL(solid.Contains_Loop(point),
reference.Contains(point));
388 BOOST_CHECK_GT(scoredPoints, 19000);
390 for (
int trial = 0; trial < 20000; ++trial) {
391 double point[3] = {rng.uniform(-20., 20.), rng.uniform(-20., 20.), rng.uniform(-8., 8.)};
396 dir[
index] = rng.uniform(-1., 1.);
398 norm = std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
399 }
while (norm < 1.e-3);
403 const bool inside =
reference.Contains(point);
404 if (inside !=
static_cast<bool>(solid.Contains_Loop(point))) {
407 const double mine = inside ? solid.DistFromInside_Loop(point, dir, TGeoShape::Big())
408 : solid.DistFromOutside_Loop(point, dir, TGeoShape::Big());
409 const double theirs = inside ?
reference.DistFromInside(point, dir, 3, TGeoShape::Big(),
nullptr)
410 :
reference.DistFromOutside(point, dir, 3, TGeoShape::Big(),
nullptr);
411 if (theirs >= TGeoShape::Big()) {
412 BOOST_REQUIRE_GE(mine, TGeoShape::Big());
415 BOOST_REQUIRE_SMALL(mine - theirs, 1.e-6);
423 const double axis[3] = {0., 1. / std::sqrt(2.), 1. / std::sqrt(2.)};
424 const double centre[3] = {1., 2., 3.};
425 O2FlatCSG solid(
"tilted_torus");
426 solid.AddTorus(1., centre, axis, 8., 2.);
427 solid.AddCell(0, 1, 0.);
430 for (
int trial = 0; trial < 20000; ++trial) {
431 const double point[3] = {rng.uniform(-14., 16.), rng.uniform(-13., 17.), rng.uniform(-12., 18.)};
433 const double offset[3] = {point[0] - centre[0], point[1] - centre[1], point[2] - centre[2]};
439 const double rho = std::sqrt(radialVec[0] * radialVec[0] + radialVec[1] * radialVec[1] +
440 radialVec[2] * radialVec[2]);
441 const double signedDistance = std::hypot(rho - 8., along) - 2.;
442 if (std::abs(signedDistance) < 1.e-9) {
445 BOOST_REQUIRE_EQUAL(
static_cast<bool>(solid.Contains_Loop(point)), signedDistance < 0.);
453 const double s = 1. / std::sqrt(2.);
455 auto rotateToWorld = [
s](
const double local[3],
double world[3]) {
457 world[1] =
s * local[1] +
s * local[2];
458 world[2] = -
s * local[1] +
s * local[2];
462 for (
int trial = 0; trial < 20000; ++trial) {
463 double localPoint[3] = {rng.uniform(-20., 20.), rng.uniform(-20., 20.), rng.uniform(-8., 8.)};
468 localDir[
index] = rng.uniform(-1., 1.);
470 norm = std::sqrt(localDir[0] * localDir[0] + localDir[1] * localDir[1] + localDir[2] * localDir[2]);
471 }
while (norm < 1.e-3);
473 localDir[
index] /= norm;
475 double worldPoint[3];
477 rotateToWorld(localPoint, worldPoint);
478 rotateToWorld(localDir, worldDir);
483 const bool inside =
reference.Contains(localPoint);
484 if (inside !=
static_cast<bool>(solid.Contains_Loop(worldPoint))) {
487 const double mine = inside ? solid.DistFromInside_Loop(worldPoint, worldDir, TGeoShape::Big())
488 : solid.DistFromOutside_Loop(worldPoint, worldDir, TGeoShape::Big());
489 const double theirs = inside ?
reference.DistFromInside(localPoint, localDir, 3, TGeoShape::Big(),
nullptr)
490 :
reference.DistFromOutside(localPoint, localDir, 3, TGeoShape::Big(),
nullptr);
491 if (theirs >= TGeoShape::Big()) {
492 BOOST_REQUIRE_GE(mine, TGeoShape::Big());
494 BOOST_REQUIRE_SMALL(mine - theirs, 1.e-6);
502 O2FlatCSG solid(
"range");
504 zCylinderQuadric(5., coeff);
505 const int cylinder = solid.AddQuadric(1., coeff);
506 const double normal[3] = {0., 0., 1.};
507 const double through[3] = {0., 0., 2.};
508 planeQuadric(normal, through, coeff);
509 const int plane = solid.AddQuadric(-1., coeff);
510 const double centre[3] = {1., 0., 0.};
511 const double axis[3] = {0., 0., 1.};
512 const int torus = solid.AddTorus(1., centre, axis, 7., 2.);
513 tiltedCylinderQuadric(5., coeff);
514 const int tilted = solid.AddQuadric(1., coeff);
517 const std::vector<int> halfspaces = {cylinder, plane, torus, tilted};
519 auto checkBox = [&](
const double* lo,
const double* hi) {
520 for (
int which : halfspaces) {
523 O2FlatCSG::HalfspaceRange(solid.GetHalfspace(which), lo, hi, rangeLo, rangeHi);
524 BOOST_REQUIRE_LE(rangeLo, rangeHi);
526 auto checkPoint = [&](
const double point[3]) {
527 const double value = O2FlatCSG::EvalHalfspace(solid.GetHalfspace(which), point);
528 BOOST_REQUIRE_GE(
value, rangeLo - 1.e-9);
529 BOOST_REQUIRE_LE(
value, rangeHi + 1.e-9);
535 for (
int cx : {0, 1}) {
536 for (
int cy : {0, 1}) {
537 for (
int cz : {0, 1}) {
538 const double corner[3] = {cx ? hi[0] : lo[0], cy ? hi[1] : lo[1], cz ? hi[2] : lo[2]};
543 const double mid[3] = {0.5 * (lo[0] + hi[0]), 0.5 * (lo[1] + hi[1]), 0.5 * (lo[2] + hi[2])};
544 for (
int faceAxis = 0; faceAxis < 3; ++faceAxis) {
545 for (
int side : {0, 1}) {
546 double face[3] = {mid[0], mid[1], mid[2]};
547 face[faceAxis] =
side ? hi[faceAxis] : lo[faceAxis];
551 for (
int edgeAxis = 0; edgeAxis < 3; ++edgeAxis) {
552 const int other1 = (edgeAxis + 1) % 3;
553 const int other2 = (edgeAxis + 2) % 3;
554 for (
int s1 : {0, 1}) {
555 for (
int s2 : {0, 1}) {
557 edge[edgeAxis] = mid[edgeAxis];
558 edge[other1] =
s1 ? hi[other1] : lo[other1];
559 edge[other2] = s2 ? hi[other2] : lo[other2];
566 for (
int sample = 0; sample < 200; ++sample) {
567 const double point[3] = {rng.uniform(lo[0], hi[0]), rng.uniform(lo[1], hi[1]),
568 rng.uniform(lo[2], hi[2])};
574 for (
int trial = 0; trial < 3000; ++trial) {
578 const double a = rng.uniform(-12., 12.);
579 const double b =
a + rng.uniform(0.01, 6.);
588 const double extreme[4][3][2] = {
589 {{-0.005, 0.005}, {-12., 12.}, {-0.5, 0.5}},
590 {{-12., 12.}, {-0.005, 0.005}, {3., 27.}},
591 {{2., 2.01}, {-1., 1.}, {-12., 12.}},
592 {{-24., 0.}, {5., 5.01}, {-3., 3.}},
594 for (
const auto&
box : extreme) {
595 const double lo[3] = {
box[0][0],
box[1][0],
box[2][0]};
596 const double hi[3] = {
box[0][1],
box[1][1],
box[2][1]};
604 O2FlatCSG solid(
"boxes");
606 zCylinderQuadric(5., coeff);
607 solid.AddQuadric(1., coeff);
608 zCylinderQuadric(2., coeff);
609 solid.AddQuadric(-1., coeff);
610 const double up[3] = {0., 0., 1.};
611 const double down[3] = {0., 0., -1.};
612 const double top[3] = {0., 0., 7.};
613 const double bottom[3] = {0., 0., -7.};
614 planeQuadric(up,
top, coeff);
615 solid.AddQuadric(1., coeff);
616 planeQuadric(down,
bottom, coeff);
617 solid.AddQuadric(1., coeff);
618 solid.AddCell(0, 4, 0.);
619 const double lo[3] = {-5., -5., -7.};
620 const double hi[3] = {5., 5., 7.};
621 solid.SetCellBBox(0, lo, hi);
624 BOOST_CHECK_GT(solid.GetNboxes(), 1);
627 int insideSamples = 0;
628 for (
int trial = 0; trial < 50000; ++trial) {
629 const double point[3] = {rng.uniform(-6., 6.), rng.uniform(-6., 6.), rng.uniform(-8., 8.)};
630 if (!solid.Contains_Loop(point)) {
635 bool covered =
false;
637 const auto&
box = solid.GetBox(
index);
638 covered = point[0] >=
box.min[0] && point[0] <=
box.max[0] && point[1] >=
box.min[1] &&
639 point[1] <=
box.max[1] && point[2] >=
box.min[2] && point[2] <=
box.max[2];
641 BOOST_REQUIRE(covered);
643 BOOST_CHECK_GT(insideSamples, 5000);
647 const auto&
box = solid.GetBox(
index);
648 for (
int sample = 0; sample < 200; ++sample) {
649 const double point[3] = {rng.uniform(
box.min[0],
box.max[0]),
650 rng.uniform(
box.min[1],
box.max[1]),
651 rng.uniform(
box.min[2],
box.max[2])};
652 bool byActive =
true;
653 for (
int slot = 0; slot <
box.nActive && byActive; ++slot) {
654 byActive = O2FlatCSG::EvalHalfspace(
655 solid.GetHalfspace(solid.GetActive(
box.firstActive + slot)), point) <= 0.;
657 BOOST_REQUIRE_EQUAL(byActive, solid.CellContains(
box.cell, point));
664 O2FlatCSG solid(
"solid_boxes");
665 addBoxCell(solid, 4., 4., 4.);
666 const double lo[3] = {-4., -4., -4.};
667 const double hi[3] = {4., 4., 4.};
668 solid.SetCellBBox(0, lo, hi);
673 solid.SetSplitDepth(6);
674 solid.SetMinBoxFraction(0.01);
679 if (solid.GetBox(
index).nActive == 0) {
683 BOOST_CHECK_GT(solidBoxes, 0);
690 O2FlatCSG solid(
"missing_bbox");
691 addBoxCell(solid, 1., 1., 1.);
692 const int first = solid.GetNhalfspaces();
693 const double centre[3] = {10., 0., 0.};
694 for (
int axis = 0; axis < 3; ++axis) {
695 for (
int sense = -1; sense <= 1; sense += 2) {
696 double normal[3] = {0., 0., 0.};
697 double through[3] = {centre[0], centre[1], centre[2]};
698 normal[axis] =
static_cast<double>(sense);
699 through[axis] += sense * 1.;
701 planeQuadric(normal, through, coeff);
702 solid.AddQuadric(1., coeff);
705 solid.AddCell(
first, 6, 8.);
707 const double lo[3] = {-1., -1., -1.};
708 const double hi[3] = {1., 1., 1.};
709 solid.SetCellBBox(0, lo, hi);
722 O2FlatCSG solid(
"inverted_bbox");
723 addBoxCell(solid, 1., 1., 1.);
724 const double lo[3] = {-1., -1., -1.};
725 const double hi[3] = {1., 1., 1.};
726 solid.SetCellBBox(0, hi, lo);
739 O2FlatCSG solid(
"nan_bbox");
740 addBoxCell(solid, 1., 1., 1.);
741 const double nan = std::numeric_limits<double>::quiet_NaN();
742 const double lo[3] = {-1., -1., -1.};
743 const double hi[3] = {1., nan, 1.};
744 solid.SetCellBBox(0, lo, hi);
764void buildBracket(O2FlatCSG& solid,
double planeScale = 1.)
767 const auto scaledPlane = [&](
const double* normal,
const double* through) {
768 planeQuadric(normal, through, coeff);
770 coeff[
index] *= planeScale;
774 const double arm[6][2][3] = {{{1., 0., 0.}, {10., 0., 0.}},
775 {{-1., 0., 0.}, {-10., 0., 0.}},
776 {{0., 1., 0.}, {0., 1., 0.}},
777 {{0., -1., 0.}, {0., -1., 0.}},
778 {{0., 0., 1.}, {0., 0., 1.}},
779 {{0., 0., -1.}, {0., 0., -1.}}};
780 int first = solid.GetNhalfspaces();
781 for (
const auto& plane : arm) {
782 scaledPlane(plane[0], plane[1]);
783 solid.AddQuadric(1., coeff);
785 solid.AddCell(
first, 6, 8. * 10. * 1. * 1.);
786 const double armLo[3] = {-10., -1., -1.};
787 const double armHi[3] = {10., 1., 1.};
788 solid.SetCellBBox(0, armLo, armHi);
791 const double upright[6][2][3] = {{{1., 0., 0.}, {10., 0., 0.}},
792 {{-1., 0., 0.}, {8., 0., 0.}},
793 {{0., 1., 0.}, {0., 1., 0.}},
794 {{0., -1., 0.}, {0., -1., 0.}},
795 {{0., 0., 1.}, {0., 0., 12.}},
796 {{0., 0., -1.}, {0., 0., 1.}}};
797 first = solid.GetNhalfspaces();
798 for (
const auto& plane : upright) {
799 scaledPlane(plane[0], plane[1]);
800 solid.AddQuadric(1., coeff);
802 solid.AddCell(
first, 6, 2. * 2. * 11.);
803 const double uprightLo[3] = {8., -1., 1.};
804 const double uprightHi[3] = {10., 1., 12.};
805 solid.SetCellBBox(1, uprightLo, uprightHi);
809 first = solid.GetNhalfspaces();
810 const double centreShift = -8.;
812 const double outer[10] = {1., 0., 0., 1., 0., 0., -centreShift, 0., 0.,
813 centreShift * centreShift - 9.};
814 solid.AddQuadric(1., outer);
815 const double inner[10] = {1., 0., 0., 1., 0., 0., -centreShift, 0., 0.,
816 centreShift * centreShift - 1.};
817 solid.AddQuadric(-1., inner);
818 const double washer[2][2][3] = {{{0., 0., 1.}, {0., 0., 3.}}, {{0., 0., -1.}, {0., 0., 1.}}};
819 for (
const auto& plane : washer) {
820 scaledPlane(plane[0], plane[1]);
821 solid.AddQuadric(1., coeff);
823 solid.AddCell(
first, 4, TMath::Pi() * (9. - 1.) * 2.);
824 const double washerLo[3] = {-11., -3., 1.};
825 const double washerHi[3] = {-5., 3., 3.};
826 solid.SetCellBBox(2, washerLo, washerHi);
832 O2FlatCSG solid(
"bracket");
835 BOOST_CHECK_GT(solid.GetNboxes(), 3);
836 BOOST_CHECK_GT(solid.GetBVHMemory(), 0u);
839 for (
int trial = 0; trial < 200000; ++trial) {
840 const double point[3] = {rng.uniform(-13., 13.), rng.uniform(-5., 5.), rng.uniform(-3., 14.)};
841 BOOST_REQUIRE_EQUAL(solid.Contains(point), solid.Contains_Loop(point));
847 O2FlatCSG solid(
"bracket_points");
850 constexpr int kPoints = 4000;
851 std::vector<double> points(3 * kPoints, 0.);
852 BOOST_REQUIRE(solid.GetPointsOnSegments(kPoints, points.data()));
853 const double zAxis[3] = {0., 0., 1.};
855 const double* point = &points[3 *
index];
856 double normal[3] = {0., 0., 0.};
857 solid.ComputeNormal(point, zAxis, normal);
860 for (
int axis = 0; axis < 3; ++axis) {
861 below[axis] = point[axis] - 1.e-6 * normal[axis];
862 above[axis] = point[axis] + 1.e-6 * normal[axis];
864 BOOST_TEST_CONTEXT(
"point = (" << point[0] <<
", " << point[1] <<
", " << point[2] <<
")")
866 BOOST_CHECK_NE(solid.Contains(below), solid.Contains(above));
873 O2FlatCSG solid(
"bracket_dist");
878 for (
int trial = 0; trial < 200000; ++trial) {
879 double point[3] = {rng.uniform(-16., 16.), rng.uniform(-8., 8.), rng.uniform(-6., 17.)};
884 dir[
index] = rng.uniform(-1., 1.);
886 norm = std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
887 }
while (norm < 1.e-3);
891 if (solid.Contains_Loop(point)) {
892 BOOST_REQUIRE_EQUAL(solid.DistFromInside(point, dir, 3, TGeoShape::Big(),
nullptr),
893 solid.DistFromInside_Loop(point, dir, TGeoShape::Big()));
895 BOOST_REQUIRE_EQUAL(solid.DistFromOutside(point, dir, 3, TGeoShape::Big(),
nullptr),
896 solid.DistFromOutside_Loop(point, dir, TGeoShape::Big()));
905 O2FlatCSG solid(
"bracket_long");
908 const double dir[3] = {1., 0., 0.};
911 const double origin[3] = {-20., 0., 0.};
912 BOOST_CHECK_SMALL(solid.DistFromOutside(
origin, dir, 3, TGeoShape::Big(),
nullptr) - 10., 1.e-12);
917 const double inArm[3] = {0., 0., 0.};
920 BOOST_CHECK_SMALL(solid.DistFromInside(inArm, dir, 3, TGeoShape::Big(),
nullptr) - 10., 1.e-12);
928 const double grazing[3] = {-8.75 - 2.e-11, 1. + 1.e-11, 0.5};
929 const double slant = 1. / std::sqrt(2.);
930 const double slantDir[3] = {slant, -slant, 0.};
931 const double entered = solid.DistFromOutside(grazing, slantDir, 3, TGeoShape::Big(),
nullptr);
932 BOOST_CHECK_EQUAL(entered, solid.DistFromOutside_Loop(grazing, slantDir, TGeoShape::Big()));
935 BOOST_CHECK_GT(entered, 0.);
936 BOOST_CHECK_LT(entered, TGeoShape::Tolerance());
937 BOOST_CHECK_LT(entered, 2.e-11 * std::sqrt(2.));
953 O2FlatCSG solid(
"bracket_scaled");
954 buildBracket(solid, 3.);
956 BOOST_REQUIRE(solid.IsClosed());
960 for (
int trial = 0; trial < 200000; ++trial) {
961 double point[3] = {rng.uniform(-16., 16.), rng.uniform(-8., 8.), rng.uniform(-6., 17.)};
966 dir[
index] = rng.uniform(-1., 1.);
968 norm = std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
969 }
while (norm < 1.e-3);
973 const bool inside = solid.Contains_Loop(point);
975 BOOST_REQUIRE_EQUAL(solid.Contains(point), inside);
976 const double accelerated =
977 inside ? solid.DistFromInside(point, dir, 3, TGeoShape::Big(),
nullptr)
978 : solid.DistFromOutside(point, dir, 3, TGeoShape::Big(),
nullptr);
979 const double twin = inside ? solid.DistFromInside_Loop(point, dir, TGeoShape::Big())
980 : solid.DistFromOutside_Loop(point, dir, TGeoShape::Big());
981 const double slack = std::abs(accelerated - twin);
982 worst = std::max(worst, slack);
986 BOOST_REQUIRE_LE(slack, 1.e-13 * std::max(1., std::abs(twin)));
988 BOOST_TEST_MESSAGE(
"largest accelerated-vs-twin gap under a x3 plane rescale: " << worst);
993 BOOST_CHECK_LE(worst, 1.e-12);
1001 O2FlatCSG solid(
"bracket_unclosed");
1002 buildBracket(solid);
1004 const double extra[6][2][3] = {{{1., 0., 0.}, {14., 0., 0.}},
1005 {{-1., 0., 0.}, {12., 0., 0.}},
1006 {{0., 1., 0.}, {0., 1., 0.}},
1007 {{0., -1., 0.}, {0., -1., 0.}},
1008 {{0., 0., 1.}, {0., 0., 1.}},
1009 {{0., 0., -1.}, {0., 0., -1.}}};
1011 const int first = solid.GetNhalfspaces();
1012 for (
const auto& plane : extra) {
1013 planeQuadric(plane[0], plane[1], coeff);
1014 solid.AddQuadric(1., coeff);
1016 solid.AddCell(
first, 6, 2. * 2. * 2.);
1019 BOOST_REQUIRE(!solid.IsClosed());
1024 const double inArm[3] = {0., 0., 0.};
1025 const double inUpright[3] = {9., 0., 6.};
1026 const double inWasher[3] = {-10.5, 0., 2.};
1027 const double inExtra[3] = {13., 0., 0.};
1029 const double inBore[3] = {-8., 0., 2.};
1030 const double outside[3] = {0., 0., 20.};
1039 for (
int trial = 0; trial < 20000; ++trial) {
1040 const double point[3] = {rng.uniform(-16., 16.), rng.uniform(-5., 5.), rng.uniform(-3., 14.)};
1041 BOOST_REQUIRE_EQUAL(solid.Contains(point), solid.Contains_Loop(point));
1046 dir[
index] = rng.uniform(-1., 1.);
1048 norm = std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
1049 }
while (norm < 1.e-3);
1053 const bool inside = solid.Contains_Loop(point);
1055 BOOST_REQUIRE_EQUAL(solid.DistFromInside(point, dir, 3, TGeoShape::Big(),
nullptr),
1056 solid.DistFromInside_Loop(point, dir, TGeoShape::Big()));
1058 BOOST_REQUIRE_EQUAL(solid.DistFromOutside(point, dir, 3, TGeoShape::Big(),
nullptr),
1059 solid.DistFromOutside_Loop(point, dir, TGeoShape::Big()));
1063 BOOST_REQUIRE_EQUAL(solid.Safety(point, inside), solid.Safety_Loop(point, inside));
1069 O2FlatCSG solid(
"bracket_safety");
1070 buildBracket(solid);
1074 for (
int trial = 0; trial < 50000; ++trial) {
1075 double point[3] = {rng.uniform(-16., 16.), rng.uniform(-8., 8.), rng.uniform(-6., 17.)};
1076 const bool inside = solid.Contains_Loop(point);
1077 const double safety = solid.Safety(point, inside);
1078 BOOST_REQUIRE_GE(safety, 0.);
1079 BOOST_REQUIRE_EQUAL(safety, solid.Safety_Loop(point, inside));
1082 for (
int probe = 0; probe < 40; ++probe) {
1087 dir[
index] = rng.uniform(-1., 1.);
1089 norm = std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
1090 }
while (norm < 1.e-3);
1091 const double reach = safety * rng.uniform(0., 0.999) / norm;
1092 const double near[3] = {point[0] + reach * dir[0], point[1] + reach * dir[1],
1093 point[2] + reach * dir[2]};
1094 BOOST_REQUIRE_EQUAL(
static_cast<bool>(solid.Contains_Loop(near)), inside);
1110 O2FlatCSG solid(
"bracket_safety_deep");
1111 buildBracket(solid);
1112 solid.SetSplitDepth(14);
1115 solid.SetMinBoxFraction(0.002);
1118 bool sawPositiveInsideSafety =
false;
1119 Rng rng(11235813ULL);
1120 for (
int trial = 0; trial < 50000; ++trial) {
1121 double point[3] = {rng.uniform(-16., 16.), rng.uniform(-8., 8.), rng.uniform(-6., 17.)};
1122 const bool inside = solid.Contains_Loop(point);
1123 const double safety = solid.Safety(point, inside);
1124 BOOST_REQUIRE_GE(safety, 0.);
1125 BOOST_REQUIRE_EQUAL(safety, solid.Safety_Loop(point, inside));
1126 if (inside && safety > 0.) {
1127 sawPositiveInsideSafety =
true;
1131 for (
int probe = 0; probe < 40; ++probe) {
1136 dir[
index] = rng.uniform(-1., 1.);
1138 norm = std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
1139 }
while (norm < 1.e-3);
1140 const double reach = safety * rng.uniform(0., 0.999) / norm;
1141 const double near[3] = {point[0] + reach * dir[0], point[1] + reach * dir[1],
1142 point[2] + reach * dir[2]};
1143 BOOST_REQUIRE_EQUAL(
static_cast<bool>(solid.Contains_Loop(near)), inside);
1146 BOOST_REQUIRE(sawPositiveInsideSafety);
1151 O2FlatCSG solid(
"bracket_capacity");
1152 buildBracket(solid);
1154 const double expected = 8. * 10. * 1. * 1. + 2. * 2. * 11. + TMath::Pi() * (9. - 1.) * 2.;
1155 BOOST_CHECK_SMALL(solid.Capacity() -
expected, 1.e-12);
1164 O2FlatCSG solid(
"bracket_bbox");
1165 buildBracket(solid);
1170 for (
int trial = 0; trial < 50000; ++trial) {
1171 const double point[3] = {rng.uniform(-13., 13.), rng.uniform(-5., 5.), rng.uniform(-3., 14.)};
1172 if (solid.Contains_Loop(point)) {
1173 BOOST_REQUIRE(solid.TGeoBBox::Contains(point));
1180 const double cellLo[3][3] = {{-10., -1., -1.}, {8., -1., 1.}, {-11., -3., 1.}};
1181 const double cellHi[3][3] = {{10., 1., 1.}, {10., 1., 12.}, {-5., 3., 3.}};
1182 double unionLo[3] = {cellLo[0][0], cellLo[0][1], cellLo[0][2]};
1183 double unionHi[3] = {cellHi[0][0], cellHi[0][1], cellHi[0][2]};
1184 for (
int cell = 1; cell < 3; ++cell) {
1190 const double*
origin = solid.GetOrigin();
1192 const double dHalf =
index == 0 ? solid.GetDX() : (
index == 1 ? solid.GetDY() : solid.GetDZ());
1200 O2FlatCSG solid(
"box_normal");
1201 addBoxCell(solid, 3., 4., 5.);
1202 const double lo[3] = {-3., -4., -5.};
1203 const double hi[3] = {3., 4., 5.};
1204 solid.SetCellBBox(0, lo, hi);
1207 const double onFace[3] = {3., 1., 1.};
1208 const double dir[3] = {1., 0., 0.};
1209 double normal[3] = {0., 0., 0.};
1210 solid.ComputeNormal(onFace, dir, normal);
1211 BOOST_CHECK_SMALL(normal[0] - 1., 1.e-12);
1212 BOOST_CHECK_SMALL(normal[1], 1.e-12);
1213 BOOST_CHECK_SMALL(normal[2], 1.e-12);
1229 O2FlatCSG solid(
"scale_invariance");
1231 zCylinderQuadric(100., coeff);
1232 solid.AddQuadric(1., coeff);
1233 solid.AddCell(0, 1, 0.);
1235 const double planeNormal[3] = {0., 0., 1.};
1236 const double planeThrough[3] = {0., 0., 0.05};
1237 planeQuadric(planeNormal, planeThrough, coeff);
1238 const int planeFirst = solid.GetNhalfspaces();
1239 solid.AddQuadric(1., coeff);
1240 solid.AddCell(planeFirst, 1, 0.);
1242 BOOST_REQUIRE(!solid.IsClosed());
1244 const double point[3] = {99.9995, 0., 0.};
1245 const double dir[3] = {1., 0., 0.};
1246 double normal[3] = {0., 0., 0.};
1247 solid.ComputeNormal(point, dir, normal);
1248 BOOST_CHECK_SMALL(normal[0] - 1., 1.e-9);
1249 BOOST_CHECK_SMALL(normal[1], 1.e-9);
1250 BOOST_CHECK_SMALL(normal[2], 1.e-9);
1264 O2FlatCSG solid(
"flat_cell");
1266 const double planes[6][2][3] = {
1267 {{1., 0., 0.}, {50., 0., 0.}}, {{-1., 0., 0.}, {-50., 0., 0.}}, {{0., 1., 0.}, {0., 50., 0.}}, {{0., -1., 0.}, {0., -50., 0.}}, {{0., 0., 1.}, {0., 0., 0.}}, {{0., 0., -1.}, {0., 0., 0.}}};
1268 for (
const auto& plane : planes) {
1269 planeQuadric(plane[0], plane[1], coeff);
1270 solid.AddQuadric(1., coeff);
1272 solid.AddCell(0, 6, 0.);
1273 const double lo[3] = {-50., -50., 0.};
1274 const double hi[3] = {50., 50., 0.};
1275 solid.SetCellBBox(0, lo, hi);
1278 BOOST_REQUIRE(solid.IsClosed());
1281 BOOST_CHECK_LT(solid.GetNboxes(), 600);
1286 O2FlatCSG original(
"bracket_io");
1287 buildBracket(original);
1288 original.CloseShape();
1290 const std::string
path =
"testFlatCSG_roundtrip.bin";
1293 O2FlatCSG loaded(
"bracket_io_loaded");
1295 loaded.CloseShape();
1303 for (
int trial = 0; trial < 100000; ++trial) {
1304 const double point[3] = {rng.uniform(-13., 13.), rng.uniform(-5., 5.), rng.uniform(-3., 14.)};
1305 BOOST_REQUIRE_EQUAL(loaded.Contains(point), original.Contains(point));
1307 std::filesystem::remove(
path);
1316 const std::string
path =
"testFlatCSG_unclosed.bin";
1318 O2FlatCSG neverClosed(
"bracket_never_closed");
1319 buildBracket(neverClosed);
1320 BOOST_REQUIRE(!neverClosed.IsClosed());
1324 O2FlatCSG refused(
"bracket_refused_close");
1326 const double plane[2][3] = {{1., 0., 0.}, {0., 0., 0.}};
1327 planeQuadric(plane[0], plane[1], coeff);
1328 refused.AddQuadric(1., coeff);
1329 refused.AddCell(0, 1, 0.);
1330 refused.CloseShape();
1331 BOOST_REQUIRE(!refused.IsClosed());
1338 O2FlatCSG original(
"bracket_trunc");
1339 buildBracket(original);
1340 original.CloseShape();
1341 const std::string
path =
"testFlatCSG_truncated.bin";
1343 std::filesystem::resize_file(
path, std::filesystem::file_size(
path) - 17);
1345 O2FlatCSG loaded(
"bracket_trunc_loaded");
1347 std::filesystem::remove(
path);
1352 O2FlatCSG original(
"bracket_root");
1353 buildBracket(original);
1354 original.CloseShape();
1356 const std::string
path =
"testFlatCSG_shape.root";
1358 TFile
file(
path.c_str(),
"RECREATE");
1359 file.WriteObject(&original,
"shape");
1361 O2FlatCSG* restored =
nullptr;
1364 file.GetObject(
"shape", restored);
1366 BOOST_REQUIRE(restored !=
nullptr);
1367 restored->CloseShape();
1370 for (
int trial = 0; trial < 100000; ++trial) {
1371 const double point[3] = {rng.uniform(-13., 13.), rng.uniform(-5., 5.), rng.uniform(-3., 14.)};
1372 BOOST_REQUIRE_EQUAL(restored->Contains(point), original.Contains(point));
1374 std::filesystem::remove(
path);
1380void addBoxAsCell(O2FlatCSG& solid,
const double* lo,
const double* hi)
1382 const int first = solid.GetNhalfspaces();
1384 for (
int axis = 0; axis < 3; ++axis) {
1385 for (
int sense = -1; sense <= 1; sense += 2) {
1386 double normal[3] = {0., 0., 0.};
1387 double through[3] = {0., 0., 0.};
1388 normal[axis] =
static_cast<double>(sense);
1389 through[axis] = sense > 0 ? hi[axis] : lo[axis];
1390 planeQuadric(normal, through, coeff);
1391 solid.AddQuadric(1., coeff);
1394 const int cell = solid.AddCell(
first, 6, (hi[0] - lo[0]) * (hi[1] - lo[1]) * (hi[2] - lo[2]));
1395 solid.SetCellBBox(cell, lo, hi);
1401void buildStaggeredChain(O2FlatCSG& solid)
1403 const double nearLo[3] = {0., -1., -1.};
1404 const double nearHi[3] = {1., 20., 1.};
1405 const double farLo[3] = {5., -1., -1.};
1406 const double farHi[3] = {6., 20., 1.};
1407 const double middleLo[3] = {1., -1., -1.};
1408 const double middleHi[3] = {5., 1., 1.};
1409 addBoxAsCell(solid, nearLo, nearHi);
1410 addBoxAsCell(solid, farLo, farHi);
1411 addBoxAsCell(solid, middleLo, middleHi);
1418 O2FlatCSG solid(
"staggered");
1419 buildStaggeredChain(solid);
1423 const double point[3] = {0.5, 0., 0.};
1424 const double dir[3] = {1., 0., 0.};
1425 O2FlatCSG::ResetUnprunedRetryCounter();
1426 const double distance = solid.DistFromInside(point, dir, 3, TGeoShape::Big(),
nullptr);
1428 BOOST_CHECK_CLOSE(
distance, 5.5, 1.e-9);
1429 BOOST_CHECK_GT(O2FlatCSG::GetUnprunedRetryCount(), 0);
1434 O2FlatCSG solid(
"staggered_random");
1435 buildStaggeredChain(solid);
1439 for (
int trial = 0; trial < 100000; ++trial) {
1440 double point[3] = {rng.uniform(-1., 7.), rng.uniform(-2., 21.), rng.uniform(-2., 2.)};
1441 if (!solid.Contains_Loop(point)) {
1448 dir[
index] = rng.uniform(-1., 1.);
1450 norm = std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
1451 }
while (norm < 1.e-3);
1456 BOOST_REQUIRE_EQUAL(solid.DistFromInside(point, dir, 3, TGeoShape::Big(),
nullptr),
1457 solid.DistFromInside_Loop(point, dir, TGeoShape::Big()));
1459 BOOST_REQUIRE_EQUAL(solid.DistFromInside(point, dir, 3, 2.,
nullptr),
1460 solid.DistFromInside_Loop(point, dir, 2.));
1462 BOOST_CHECK_GT(inside, 1000);
header::DataOrigin origin
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate increase X h configStandalone Camera window renderer OpenGL display display_camera Orient the camera such that the y axis is always upwards Full Screen MultiSample Anti Downsampling Anti Enable Vertical Sync Target frame or if the backend cannot limit to the refresh Use OpenGL indirect draws to reduce number of draw calls Display h configStandalone Vulkan display display_vulkan Have uniform buffers in host accessible device memory h configStandalone Internal display display_internal Number of updates the Qt GUI may send ahead of the last one the display has before it waits for the display Opacity of the console background Number of console lines the history is shortened to this when it reaches twice the size Time in seconds an info message fades out after setInfoTime h OpenGL display display Font size Do not use Freetype for font Renderer for event Frontend to use for event ROOT macros used as track filter Console script file
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error s
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events d
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
GLdouble GLdouble GLdouble GLdouble top
GLenum GLuint GLint GLenum face
GLboolean GLboolean GLboolean b
GLsizei GLsizei GLfloat distance
GLsizei const GLfloat * value
GLenum GLsizei GLsizei GLint * values
GLsizei const GLchar *const * path
GLboolean GLboolean GLboolean GLboolean a
GLsizei const GLint * box
bool LoadFlatCSG(const std::string &file, O2FlatCSG &solid)
Load a flat-CSG sidecar (flatcsg_*.bin, version 1) into solid; call CloseShape() after....
bool WriteFlatCSG(const std::string &file, const O2FlatCSG &solid)
std::map< std::string, ID > expected
BOOST_AUTO_TEST_CASE(box_from_six_planes_contains_like_TGeoBBox)
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())