File indexing completed on 2025-11-05 08:55:19
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/data/test_case.hpp>
0010 #include <boost/test/detail/log_level.hpp>
0011 #include <boost/test/tools/context.hpp>
0012 #include <boost/test/tools/old/interface.hpp>
0013 #include <boost/test/unit_test.hpp>
0014 #include <boost/test/unit_test_log.hpp>
0015 #include <boost/test/unit_test_parameters.hpp>
0016 #include <boost/test/unit_test_suite.hpp>
0017
0018 #include "Acts/Definitions/Algebra.hpp"
0019 #include "Acts/Definitions/Units.hpp"
0020 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0021 #include "Acts/Geometry/CuboidVolumeStack.hpp"
0022 #include "Acts/Geometry/VolumeAttachmentStrategy.hpp"
0023 #include "Acts/Geometry/VolumeResizeStrategy.hpp"
0024 #include "Acts/Utilities/AxisDefinitions.hpp"
0025 #include "Acts/Utilities/Logger.hpp"
0026 #include "Acts/Utilities/ThrowAssert.hpp"
0027 #include "Acts/Utilities/Zip.hpp"
0028 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0029
0030 #include <cassert>
0031 #include <initializer_list>
0032 #include <stdexcept>
0033
0034 using namespace Acts;
0035 using namespace Acts::UnitLiterals;
0036
0037 namespace ActsTests {
0038
0039 auto logger = getDefaultLogger("UnitTests", Logging::VERBOSE);
0040
0041 struct Fixture {
0042 Logging::Level m_level;
0043 Fixture() {
0044 m_level = Logging::getFailureThreshold();
0045 Logging::setFailureThreshold(Logging::FATAL);
0046 }
0047
0048 ~Fixture() { Logging::setFailureThreshold(m_level); }
0049 };
0050
0051 BOOST_FIXTURE_TEST_SUITE(GeometrySuite, Fixture)
0052
0053 static const std::vector<VolumeAttachmentStrategy> strategies = {
0054 VolumeAttachmentStrategy::Gap,
0055 VolumeAttachmentStrategy::First,
0056 VolumeAttachmentStrategy::Second,
0057 VolumeAttachmentStrategy::Midpoint,
0058 };
0059
0060 static const std::vector<VolumeResizeStrategy> resizeStrategies = {
0061 VolumeResizeStrategy::Expand,
0062 VolumeResizeStrategy::Gap,
0063 };
0064
0065 BOOST_AUTO_TEST_SUITE(CuboidVolumeStackTest)
0066
0067 BOOST_DATA_TEST_CASE(BaselineLocal,
0068 (boost::unit_test::data::xrange(-135, 180, 45) *
0069 boost::unit_test::data::xrange(0, 2, 1) *
0070 boost::unit_test::data::make(0.8, 1.0, 1.2) *
0071 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0072 Vector3{20_mm, 0_mm, 0_mm},
0073 Vector3{0_mm, 20_mm, 0_mm},
0074 Vector3{20_mm, 20_mm, 0_mm},
0075 Vector3{0_mm, 0_mm, 20_mm}) *
0076 boost::unit_test::data::make(strategies) *
0077 boost::unit_test::data::make(AxisDirection::AxisX,
0078 AxisDirection::AxisY,
0079 AxisDirection::AxisZ)),
0080 angle, rotate, shift, offset, strategy, dir) {
0081 double halfDir = 400_mm;
0082
0083 auto [dirOrth1, dirOrth2] = CuboidVolumeStack::getOrthogonalAxes(dir);
0084
0085 auto dirIdx = CuboidVolumeStack::axisToIndex(dir);
0086 auto dirOrth1Idx = CuboidVolumeStack::axisToIndex(dirOrth1);
0087
0088 auto boundDir = CuboidVolumeBounds::boundsFromAxisDirection(dir);
0089 auto boundDirOrth1 = CuboidVolumeBounds::boundsFromAxisDirection(dirOrth1);
0090 auto boundDirOrth2 = CuboidVolumeBounds::boundsFromAxisDirection(dirOrth2);
0091
0092 auto bounds1 = std::make_shared<CuboidVolumeBounds>(
0093 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0094 {boundDir, halfDir},
0095 {boundDirOrth1, 100_mm},
0096 {boundDirOrth2, 400_mm}});
0097
0098 auto bounds2 = std::make_shared<CuboidVolumeBounds>(
0099 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0100 {boundDir, halfDir},
0101 {boundDirOrth1, 200_mm},
0102 {boundDirOrth2, 600_mm}});
0103
0104 auto bounds3 = std::make_shared<CuboidVolumeBounds>(
0105 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0106 {boundDir, halfDir},
0107 {boundDirOrth1, 300_mm},
0108 {boundDirOrth2, 500_mm}});
0109
0110 Transform3 base = AngleAxis3(angle * 1_degree, Vector3::Unit(dirOrth1Idx)) *
0111 Translation3(offset);
0112
0113 Translation3 translation1(Vector3::Unit(dirIdx) * (-2 * halfDir * shift));
0114 Transform3 transform1 = base * translation1;
0115 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
0116
0117 Transform3 transform2 = base;
0118 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
0119
0120 Translation3 translation3(Vector3::Unit(dirIdx) * (2 * halfDir * shift));
0121 Transform3 transform3 = base * translation3;
0122 auto vol3 = std::make_shared<Volume>(transform3, bounds3);
0123
0124 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
0125
0126 std::rotate(volumes.begin(), volumes.begin() + rotate, volumes.end());
0127
0128 auto origVolumes = volumes;
0129
0130 std::vector<CuboidVolumeBounds> originalBounds;
0131 std::transform(volumes.begin(), volumes.end(),
0132 std::back_inserter(originalBounds), [](const auto& vol) {
0133 const auto* res =
0134 dynamic_cast<CuboidVolumeBounds*>(&vol->volumeBounds());
0135 throw_assert(res != nullptr, "");
0136 return *res;
0137 });
0138
0139 if (shift < 1.0) {
0140 BOOST_CHECK_THROW(CuboidVolumeStack(volumes, dir, strategy,
0141 VolumeResizeStrategy::Gap, *logger),
0142 std::invalid_argument);
0143 return;
0144 }
0145 CuboidVolumeStack stack(volumes, dir, strategy, VolumeResizeStrategy::Gap,
0146 *logger);
0147
0148 auto stackBounds =
0149 dynamic_cast<const CuboidVolumeBounds*>(&stack.volumeBounds());
0150 BOOST_REQUIRE(stackBounds != nullptr);
0151
0152 BOOST_CHECK_CLOSE(stackBounds->get(boundDirOrth1), 300_mm, 1e-6);
0153 BOOST_CHECK_CLOSE(stackBounds->get(boundDirOrth2), 600_mm, 1e-6);
0154 BOOST_CHECK_CLOSE(stackBounds->get(boundDir), halfDir + 2 * halfDir * shift,
0155 1e-6);
0156 CHECK_CLOSE_OR_SMALL(stack.transform().matrix(), base.matrix(), 1e-10, 1e-12);
0157
0158
0159
0160 for (const auto& volume : volumes) {
0161 const auto* cuboidBounds =
0162 dynamic_cast<const CuboidVolumeBounds*>(&volume->volumeBounds());
0163 BOOST_REQUIRE(cuboidBounds != nullptr);
0164 BOOST_CHECK_CLOSE(cuboidBounds->get(boundDirOrth1), 300_mm, 1e-6);
0165 BOOST_CHECK_CLOSE(cuboidBounds->get(boundDirOrth2), 600_mm, 1e-6);
0166 }
0167
0168
0169 for (std::size_t i = 0; i < volumes.size() - 1; ++i) {
0170 const auto& a = volumes.at(i);
0171 const auto& b = volumes.at(i + 1);
0172
0173 BOOST_CHECK_LT((base.inverse() * a->center())[dirIdx],
0174 (base.inverse() * b->center())[dirIdx]);
0175 }
0176
0177 if (shift <= 1.0) {
0178
0179 BOOST_CHECK_EQUAL(volumes.size(), 3);
0180
0181
0182 BOOST_CHECK_EQUAL(vol1->transform().matrix(), transform1.matrix());
0183 BOOST_CHECK_EQUAL(vol2->transform().matrix(), transform2.matrix());
0184 BOOST_CHECK_EQUAL(vol3->transform().matrix(), transform3.matrix());
0185
0186 for (const auto& [volume, bounds] : zip(origVolumes, originalBounds)) {
0187 const auto* newBounds =
0188 dynamic_cast<const CuboidVolumeBounds*>(&volume->volumeBounds());
0189 BOOST_CHECK_CLOSE(newBounds->get(boundDir), bounds.get(boundDir), 1e-6);
0190 }
0191 } else {
0192 if (strategy == VolumeAttachmentStrategy::Gap) {
0193
0194 BOOST_CHECK_EQUAL(volumes.size(), 5);
0195 auto gap1 = volumes.at(1);
0196 auto gap2 = volumes.at(3);
0197
0198 BOOST_TEST_MESSAGE("Gap 1: " << gap1->transform().matrix());
0199 BOOST_TEST_MESSAGE("Gap 2: " << gap2->transform().matrix());
0200
0201 const auto* gapBounds1 =
0202 dynamic_cast<const CuboidVolumeBounds*>(&gap1->volumeBounds());
0203 const auto* gapBounds2 =
0204 dynamic_cast<const CuboidVolumeBounds*>(&gap2->volumeBounds());
0205
0206 double gapHlDir = (shift - 1.0) * halfDir;
0207
0208 BOOST_CHECK(std::abs(gapBounds1->get(boundDir) - gapHlDir) < 1e-12);
0209 BOOST_CHECK(std::abs(gapBounds2->get(boundDir) - gapHlDir) < 1e-12);
0210
0211 double gap1Dir = (-2 * halfDir * shift) + halfDir + gapHlDir;
0212 double gap2Dir = (2 * halfDir * shift) - halfDir - gapHlDir;
0213
0214 Translation3 gap1Translation(Vector3::Unit(dirIdx) * gap1Dir);
0215 Translation3 gap2Translation(Vector3::Unit(dirIdx) * gap2Dir);
0216
0217 Transform3 gap1Transform = base * gap1Translation;
0218 Transform3 gap2Transform = base * gap2Translation;
0219
0220 CHECK_CLOSE_OR_SMALL(gap1->transform().matrix(), gap1Transform.matrix(),
0221 1e-10, 1e-12);
0222 CHECK_CLOSE_OR_SMALL(gap2->transform().matrix(), gap2Transform.matrix(),
0223 1e-10, 1e-12);
0224
0225
0226 for (const auto& [volume, bounds] : zip(origVolumes, originalBounds)) {
0227 const auto* newBounds =
0228 dynamic_cast<const CuboidVolumeBounds*>(&volume->volumeBounds());
0229 BOOST_CHECK_CLOSE(newBounds->get(boundDir), bounds.get(boundDir), 1e-6);
0230 }
0231
0232
0233 BOOST_CHECK_EQUAL(vol1->transform().matrix(), transform1.matrix());
0234 BOOST_CHECK_EQUAL(vol2->transform().matrix(), transform2.matrix());
0235 BOOST_CHECK_EQUAL(vol3->transform().matrix(), transform3.matrix());
0236 } else if (strategy == VolumeAttachmentStrategy::First) {
0237
0238 BOOST_CHECK_EQUAL(volumes.size(), 3);
0239
0240 double wGap = (shift - 1.0) * halfDir * 2;
0241
0242
0243 auto newBounds1 =
0244 dynamic_cast<const CuboidVolumeBounds*>(&vol1->volumeBounds());
0245 BOOST_CHECK_CLOSE(newBounds1->get(boundDir), halfDir + wGap / 2.0, 1e-6);
0246 double pDir1 = -2 * halfDir * shift + wGap / 2.0;
0247 Translation3 expectedTranslation1(Vector3::Unit(dirIdx) * pDir1);
0248 Transform3 expectedTransform1 = base * expectedTranslation1;
0249 CHECK_CLOSE_OR_SMALL(vol1->transform().matrix(),
0250 expectedTransform1.matrix(), 1e-10, 1e-12);
0251
0252
0253 auto newBounds2 =
0254 dynamic_cast<const CuboidVolumeBounds*>(&vol2->volumeBounds());
0255 BOOST_CHECK_CLOSE(newBounds2->get(boundDir), halfDir + wGap / 2.0, 1e-6);
0256 double pDir2 = wGap / 2.0;
0257 Translation3 expectedTranslation2(Vector3::Unit(dirIdx) * pDir2);
0258 Transform3 expectedTransform2 = base * expectedTranslation2;
0259 CHECK_CLOSE_OR_SMALL(vol2->transform().matrix(),
0260 expectedTransform2.matrix(), 1e-10, 1e-12);
0261
0262
0263 auto newBounds3 =
0264 dynamic_cast<const CuboidVolumeBounds*>(&vol3->volumeBounds());
0265 BOOST_CHECK_CLOSE(newBounds3->get(boundDir), halfDir, 1e-6);
0266 double pDir3 = 2 * halfDir * shift;
0267 Translation3 expectedTranslation3(Vector3::Unit(dirIdx) * pDir3);
0268 Transform3 expectedTransform3 = base * expectedTranslation3;
0269 CHECK_CLOSE_OR_SMALL(vol3->transform().matrix(),
0270 expectedTransform3.matrix(), 1e-10, 1e-12);
0271 } else if (strategy == VolumeAttachmentStrategy::Second) {
0272
0273 BOOST_CHECK_EQUAL(volumes.size(), 3);
0274
0275 double wGap = (shift - 1.0) * halfDir * 2;
0276
0277
0278 auto newBounds1 =
0279 dynamic_cast<const CuboidVolumeBounds*>(&vol1->volumeBounds());
0280 BOOST_CHECK_CLOSE(newBounds1->get(boundDir), halfDir, 1e-6);
0281 double pDir1 = -2 * halfDir * shift;
0282 Translation3 expectedTranslation1(Vector3::Unit(dirIdx) * pDir1);
0283 Transform3 expectedTransform1 = base * expectedTranslation1;
0284 CHECK_CLOSE_OR_SMALL(vol1->transform().matrix(),
0285 expectedTransform1.matrix(), 1e-10, 1e-12);
0286
0287
0288 auto newBounds2 =
0289 dynamic_cast<const CuboidVolumeBounds*>(&vol2->volumeBounds());
0290 BOOST_CHECK_CLOSE(newBounds2->get(boundDir), halfDir + wGap / 2.0, 1e-6);
0291 double pDir2 = -wGap / 2.0;
0292 Translation3 expectedTranslation2(Vector3::Unit(dirIdx) * pDir2);
0293 Transform3 expectedTransform2 = base * expectedTranslation2;
0294 CHECK_CLOSE_OR_SMALL(vol2->transform().matrix(),
0295 expectedTransform2.matrix(), 1e-10, 1e-12);
0296
0297
0298 auto newBounds3 =
0299 dynamic_cast<const CuboidVolumeBounds*>(&vol3->volumeBounds());
0300 BOOST_CHECK_CLOSE(newBounds3->get(boundDir), halfDir + wGap / 2.0, 1e-6);
0301 double pDir3 = 2 * halfDir * shift - wGap / 2.0;
0302 Translation3 expectedTranslation3(Vector3::Unit(dirIdx) * pDir3);
0303 Transform3 expectedTransform3 = base * expectedTranslation3;
0304 CHECK_CLOSE_OR_SMALL(vol3->transform().matrix(),
0305 expectedTransform3.matrix(), 1e-10, 1e-12);
0306 } else if (strategy == VolumeAttachmentStrategy::Midpoint) {
0307
0308 BOOST_CHECK_EQUAL(volumes.size(), 3);
0309
0310 double wGap = (shift - 1.0) * halfDir * 2;
0311
0312
0313 auto newBounds1 =
0314 dynamic_cast<const CuboidVolumeBounds*>(&vol1->volumeBounds());
0315 BOOST_CHECK_CLOSE(newBounds1->get(boundDir), halfDir + wGap / 4.0, 1e-6);
0316 double pDir1 = -2 * halfDir * shift + wGap / 4.0;
0317 Translation3 expectedTranslation1(Vector3::Unit(dirIdx) * pDir1);
0318 Transform3 expectedTransform1 = base * expectedTranslation1;
0319 CHECK_CLOSE_OR_SMALL(vol1->transform().matrix(),
0320 expectedTransform1.matrix(), 1e-10, 1e-12);
0321
0322
0323 auto newBounds2 =
0324 dynamic_cast<const CuboidVolumeBounds*>(&vol2->volumeBounds());
0325 BOOST_CHECK_CLOSE(newBounds2->get(boundDir), halfDir + wGap / 2.0, 1e-6);
0326 CHECK_CLOSE_OR_SMALL(vol2->transform().matrix(), base.matrix(), 1e-10,
0327 1e-12);
0328
0329
0330 auto newBounds3 =
0331 dynamic_cast<const CuboidVolumeBounds*>(&vol3->volumeBounds());
0332 BOOST_CHECK_CLOSE(newBounds3->get(boundDir), halfDir + wGap / 4.0, 1e-6);
0333 double pDir3 = 2 * halfDir * shift - wGap / 4.0;
0334 Translation3 expectedTranslation3(Vector3::Unit(dirIdx) * pDir3);
0335 Transform3 expectedTransform3 = base * expectedTranslation3;
0336 CHECK_CLOSE_OR_SMALL(vol3->transform().matrix(),
0337 expectedTransform3.matrix(), 1e-10, 1e-12);
0338 }
0339 }
0340 }
0341
0342 BOOST_DATA_TEST_CASE(Asymmetric,
0343 boost::unit_test::data::make(AxisDirection::AxisX,
0344 AxisDirection::AxisY,
0345 AxisDirection::AxisZ),
0346 dir) {
0347 double halfDir1 = 200_mm;
0348 double pDir1 = -1100_mm;
0349 double halfDir2 = 600_mm;
0350 double pDir2 = -200_mm;
0351 double halfDir3 = 400_mm;
0352 double pDir3 = 850_mm;
0353
0354 auto [dirOrth1, dirOrth2] = CuboidVolumeStack::getOrthogonalAxes(dir);
0355
0356 auto dirIdx = CuboidVolumeStack::axisToIndex(dir);
0357
0358 auto boundDir = CuboidVolumeBounds::boundsFromAxisDirection(dir);
0359 auto boundDirOrth1 = CuboidVolumeBounds::boundsFromAxisDirection(dirOrth1);
0360 auto boundDirOrth2 = CuboidVolumeBounds::boundsFromAxisDirection(dirOrth2);
0361
0362 auto bounds1 = std::make_shared<CuboidVolumeBounds>(
0363 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0364 {boundDir, halfDir1},
0365 {boundDirOrth1, 100_mm},
0366 {boundDirOrth2, 400_mm}});
0367
0368 auto bounds2 = std::make_shared<CuboidVolumeBounds>(
0369 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0370 {boundDir, halfDir2},
0371 {boundDirOrth1, 200_mm},
0372 {boundDirOrth2, 600_mm}});
0373
0374 auto bounds3 = std::make_shared<CuboidVolumeBounds>(
0375 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0376 {boundDir, halfDir3},
0377 {boundDirOrth1, 300_mm},
0378 {boundDirOrth2, 500_mm}});
0379
0380 Translation3 translation1(Vector3::Unit(dirIdx) * pDir1);
0381 Transform3 transform1(translation1);
0382 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
0383
0384 Translation3 translation2(Vector3::Unit(dirIdx) * pDir2);
0385 Transform3 transform2(translation2);
0386 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
0387
0388 Translation3 translation3(Vector3::Unit(dirIdx) * pDir3);
0389 Transform3 transform3(translation3);
0390 auto vol3 = std::make_shared<Volume>(transform3, bounds3);
0391
0392 std::vector<Volume*> volumes = {vol2.get(), vol1.get(), vol3.get()};
0393
0394 CuboidVolumeStack stack(volumes, dir, VolumeAttachmentStrategy::Gap,
0395 VolumeResizeStrategy::Gap, *logger);
0396 BOOST_CHECK_EQUAL(volumes.size(), 5);
0397
0398 auto stackBounds =
0399 dynamic_cast<const CuboidVolumeBounds*>(&stack.volumeBounds());
0400 BOOST_REQUIRE(stackBounds != nullptr);
0401
0402 BOOST_CHECK_CLOSE(stackBounds->get(boundDirOrth1), 300_mm, 1e-6);
0403 BOOST_CHECK_CLOSE(stackBounds->get(boundDirOrth2), 600_mm, 1e-6);
0404 BOOST_CHECK_CLOSE(stackBounds->get(boundDir),
0405 (std::abs(pDir1 - halfDir1) + pDir3 + halfDir3) / 2.0,
0406 1e-6);
0407
0408 double midDir = (pDir1 - halfDir1 + pDir3 + halfDir3) / 2.0;
0409 Translation3 expectedTranslation(Vector3::Unit(dirIdx) * midDir);
0410 Transform3 expectedTransform = Transform3::Identity() * expectedTranslation;
0411 CHECK_CLOSE_OR_SMALL(stack.transform().matrix(), expectedTransform.matrix(),
0412 1e-10, 1e-12);
0413 }
0414
0415 BOOST_DATA_TEST_CASE(UpdateStack,
0416 (boost::unit_test::data::xrange(-135, 180, 45) *
0417 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0418 Vector3{20_mm, 0_mm, 0_mm},
0419 Vector3{0_mm, 20_mm, 0_mm},
0420 Vector3{20_mm, 20_mm, 0_mm},
0421 Vector3{0_mm, 0_mm, 20_mm}) *
0422 boost::unit_test::data::make(-100_mm, 0_mm, 100_mm) *
0423 boost::unit_test::data::make(resizeStrategies) *
0424 boost::unit_test::data::make(AxisDirection::AxisX,
0425 AxisDirection::AxisY,
0426 AxisDirection::AxisZ)),
0427 angle, offset, zshift, strategy, dir) {
0428 double halfDir = 400_mm;
0429
0430 auto [dirOrth1, dirOrth2] = CuboidVolumeStack::getOrthogonalAxes(dir);
0431
0432 auto dirIdx = CuboidVolumeStack::axisToIndex(dir);
0433 auto dirOrth1Idx = CuboidVolumeStack::axisToIndex(dirOrth1);
0434
0435 auto boundDir = CuboidVolumeBounds::boundsFromAxisDirection(dir);
0436 auto boundDirOrth1 = CuboidVolumeBounds::boundsFromAxisDirection(dirOrth1);
0437 auto boundDirOrth2 = CuboidVolumeBounds::boundsFromAxisDirection(dirOrth2);
0438
0439 auto bounds1 = std::make_shared<CuboidVolumeBounds>(
0440 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0441 {boundDir, halfDir},
0442 {boundDirOrth1, 100_mm},
0443 {boundDirOrth2, 600_mm}});
0444
0445 auto bounds2 = std::make_shared<CuboidVolumeBounds>(
0446 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0447 {boundDir, halfDir},
0448 {boundDirOrth1, 100_mm},
0449 {boundDirOrth2, 600_mm}});
0450
0451 auto bounds3 = std::make_shared<CuboidVolumeBounds>(
0452 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0453 {boundDir, halfDir},
0454 {boundDirOrth1, 100_mm},
0455 {boundDirOrth2, 600_mm}});
0456
0457 Vector3 shift = Vector3::Unit(dirIdx) * zshift;
0458 Transform3 base = AngleAxis3(angle * 1_degree, Vector3::Unit(dirOrth1Idx)) *
0459 Translation3(offset + shift);
0460
0461 Translation3 translation1(Vector3::Unit(dirIdx) * -2 * halfDir);
0462 Transform3 transform1 = base * translation1;
0463 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
0464
0465 Transform3 transform2 = base;
0466 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
0467
0468 Translation3 translation3(Vector3::Unit(dirIdx) * 2 * halfDir);
0469 Transform3 transform3 = base * translation3;
0470 auto vol3 = std::make_shared<Volume>(transform3, bounds3);
0471
0472 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
0473 std::vector<Volume*> originalVolumes = volumes;
0474
0475 std::vector<Transform3> originalTransforms = {transform1, transform2,
0476 transform3};
0477
0478 CuboidVolumeStack stack(volumes, dir,
0479 VolumeAttachmentStrategy::Gap,
0480
0481 strategy, *logger);
0482
0483 const auto* originalBounds =
0484 dynamic_cast<const CuboidVolumeBounds*>(&stack.volumeBounds());
0485
0486 auto assertOriginalBounds = [&]() {
0487 const auto* bounds =
0488 dynamic_cast<const CuboidVolumeBounds*>(&stack.volumeBounds());
0489 BOOST_REQUIRE(bounds != nullptr);
0490 BOOST_CHECK_EQUAL(bounds, originalBounds);
0491 BOOST_CHECK_CLOSE(bounds->get(boundDirOrth1), 100_mm, 1e-6);
0492 BOOST_CHECK_CLOSE(bounds->get(boundDirOrth2), 600_mm, 1e-6);
0493 BOOST_CHECK_CLOSE(bounds->get(boundDir), 3 * halfDir, 1e-6);
0494 };
0495
0496 assertOriginalBounds();
0497
0498 {
0499
0500 auto bounds = std::make_shared<CuboidVolumeBounds>(
0501 dynamic_cast<const CuboidVolumeBounds&>(stack.volumeBounds()));
0502 stack.update(bounds, std::nullopt, *logger);
0503 assertOriginalBounds();
0504 }
0505
0506 {
0507
0508 auto bounds = std::make_shared<CuboidVolumeBounds>(
0509 dynamic_cast<const CuboidVolumeBounds&>(stack.volumeBounds()));
0510 bounds->set(boundDirOrth1, 20_mm);
0511 BOOST_CHECK_THROW(stack.update(bounds, std::nullopt, *logger),
0512 std::invalid_argument);
0513 assertOriginalBounds();
0514 }
0515
0516 {
0517
0518 auto bounds = std::make_shared<CuboidVolumeBounds>(
0519 dynamic_cast<const CuboidVolumeBounds&>(stack.volumeBounds()));
0520 bounds->set(boundDirOrth2, 200_mm);
0521 BOOST_CHECK_THROW(stack.update(bounds, std::nullopt, *logger),
0522 std::invalid_argument);
0523 assertOriginalBounds();
0524 }
0525
0526 {
0527
0528 auto bounds = std::make_shared<CuboidVolumeBounds>(
0529 dynamic_cast<const CuboidVolumeBounds&>(stack.volumeBounds()));
0530 bounds->set(boundDir, 2 * halfDir);
0531 BOOST_CHECK_THROW(stack.update(bounds, std::nullopt, *logger),
0532 std::invalid_argument);
0533 assertOriginalBounds();
0534 }
0535
0536 {
0537
0538 auto bounds = std::make_shared<CuboidVolumeBounds>(
0539 dynamic_cast<const CuboidVolumeBounds&>(stack.volumeBounds()));
0540 bounds->set(boundDirOrth1, 700_mm);
0541 stack.update(bounds, std::nullopt, *logger);
0542 const auto* updatedBounds =
0543 dynamic_cast<const CuboidVolumeBounds*>(&stack.volumeBounds());
0544 BOOST_REQUIRE(updatedBounds != nullptr);
0545 BOOST_CHECK_CLOSE(updatedBounds->get(boundDirOrth1), 700_mm, 1e-6);
0546 BOOST_CHECK_CLOSE(updatedBounds->get(boundDirOrth2), 600_mm, 1e-6);
0547 BOOST_CHECK_CLOSE(updatedBounds->get(boundDir), 3 * halfDir, 1e-6);
0548
0549
0550 BOOST_CHECK_EQUAL(volumes.size(), 3);
0551
0552
0553 for (const auto& [volume, origTransform] :
0554 zip(volumes, originalTransforms)) {
0555 const auto* newBounds =
0556 dynamic_cast<const CuboidVolumeBounds*>(&volume->volumeBounds());
0557 BOOST_CHECK_CLOSE(newBounds->get(boundDirOrth1), 700_mm, 1e-6);
0558 BOOST_CHECK_CLOSE(newBounds->get(boundDirOrth2), 600_mm, 1e-6);
0559 BOOST_CHECK_CLOSE(newBounds->get(boundDir), halfDir, 1e-6);
0560
0561
0562 BOOST_CHECK_EQUAL(volume->transform().matrix(), origTransform.matrix());
0563 }
0564 }
0565 {
0566
0567 auto bounds = std::make_shared<CuboidVolumeBounds>(
0568 dynamic_cast<const CuboidVolumeBounds&>(stack.volumeBounds()));
0569 bounds->set(boundDirOrth2, 700_mm);
0570 stack.update(bounds, std::nullopt, *logger);
0571 const auto* updatedBounds =
0572 dynamic_cast<const CuboidVolumeBounds*>(&stack.volumeBounds());
0573 BOOST_REQUIRE(updatedBounds != nullptr);
0574 BOOST_CHECK_CLOSE(updatedBounds->get(boundDirOrth1), 700_mm, 1e-6);
0575 BOOST_CHECK_CLOSE(updatedBounds->get(boundDirOrth2), 700_mm, 1e-6);
0576 BOOST_CHECK_CLOSE(updatedBounds->get(boundDir), 3 * halfDir, 1e-6);
0577
0578
0579 BOOST_CHECK_EQUAL(volumes.size(), 3);
0580
0581
0582 for (const auto& [volume, origTransform] :
0583 zip(volumes, originalTransforms)) {
0584 const auto* newBounds =
0585 dynamic_cast<const CuboidVolumeBounds*>(&volume->volumeBounds());
0586 BOOST_CHECK_CLOSE(newBounds->get(boundDirOrth1), 700_mm, 1e-6);
0587 BOOST_CHECK_CLOSE(newBounds->get(boundDirOrth2), 700_mm, 1e-6);
0588 BOOST_CHECK_CLOSE(newBounds->get(boundDir), halfDir, 1e-6);
0589
0590
0591 BOOST_CHECK_EQUAL(volume->transform().matrix(), origTransform.matrix());
0592 }
0593 }
0594
0595 {
0596
0597 auto bounds = std::make_shared<CuboidVolumeBounds>(
0598 dynamic_cast<const CuboidVolumeBounds&>(stack.volumeBounds()));
0599 bounds->set(boundDir, 4 * halfDir);
0600 stack.update(bounds, std::nullopt, *logger);
0601 const auto* updatedBounds =
0602 dynamic_cast<const CuboidVolumeBounds*>(&stack.volumeBounds());
0603 BOOST_REQUIRE(updatedBounds != nullptr);
0604 BOOST_CHECK_CLOSE(updatedBounds->get(boundDir), 4 * halfDir, 1e-6);
0605 BOOST_CHECK_CLOSE(updatedBounds->get(boundDirOrth1), 700_mm, 1e-6);
0606 BOOST_CHECK_CLOSE(updatedBounds->get(boundDirOrth2), 700_mm, 1e-6);
0607
0608 if (strategy == VolumeResizeStrategy::Expand) {
0609
0610 BOOST_CHECK_EQUAL(volumes.size(), 3);
0611
0612
0613 auto newBounds1 =
0614 dynamic_cast<const CuboidVolumeBounds*>(&vol1->volumeBounds());
0615 BOOST_CHECK_CLOSE(newBounds1->get(boundDir), halfDir + halfDir / 2.0,
0616 1e-6);
0617 auto expectedTranslation1 =
0618 Translation3(Vector3::Unit(dirIdx) * (-2 * halfDir - halfDir / 2.0));
0619 Transform3 expectedTransform1 = base * expectedTranslation1;
0620 CHECK_CLOSE_OR_SMALL(vol1->transform().matrix(),
0621 expectedTransform1.matrix(), 1e-10, 1e-12);
0622
0623
0624 auto newBounds2 =
0625 dynamic_cast<const CuboidVolumeBounds*>(&vol2->volumeBounds());
0626 BOOST_CHECK_CLOSE(newBounds2->get(boundDir), halfDir, 1e-6);
0627 CHECK_CLOSE_OR_SMALL(vol2->transform().matrix(), transform2.matrix(),
0628 1e-10, 1e-12);
0629
0630
0631 auto newBounds3 =
0632 dynamic_cast<const CuboidVolumeBounds*>(&vol3->volumeBounds());
0633 BOOST_CHECK_CLOSE(newBounds3->get(boundDir), halfDir + halfDir / 2.0,
0634 1e-6);
0635 auto expectedTranslation3 =
0636 Translation3(Vector3::Unit(dirIdx) * (2 * halfDir + halfDir / 2.0));
0637 Transform3 expectedTransform3 = base * expectedTranslation3;
0638 CHECK_CLOSE_OR_SMALL(vol3->transform().matrix(),
0639 expectedTransform3.matrix(), 1e-10, 1e-12);
0640 } else if (strategy == VolumeResizeStrategy::Gap) {
0641
0642 BOOST_CHECK_EQUAL(volumes.size(), 5);
0643
0644 for (const auto& [volume, origTransform] :
0645 zip(originalVolumes, originalTransforms)) {
0646 const auto* newBounds =
0647 dynamic_cast<const CuboidVolumeBounds*>(&volume->volumeBounds());
0648 BOOST_CHECK_CLOSE(newBounds->get(boundDirOrth1), 700_mm, 1e-6);
0649 BOOST_CHECK_CLOSE(newBounds->get(boundDirOrth2), 700_mm, 1e-6);
0650 BOOST_CHECK_CLOSE(newBounds->get(boundDir), halfDir, 1e-6);
0651
0652 CHECK_CLOSE_OR_SMALL(volume->transform().matrix(),
0653 origTransform.matrix(), 1e-10, 1e-12);
0654 }
0655
0656 auto gap1 = volumes.front();
0657 auto gap2 = volumes.back();
0658
0659 const auto* gapBounds1 =
0660 dynamic_cast<const CuboidVolumeBounds*>(&gap1->volumeBounds());
0661 const auto* gapBounds2 =
0662 dynamic_cast<const CuboidVolumeBounds*>(&gap2->volumeBounds());
0663
0664 BOOST_CHECK_CLOSE(gapBounds1->get(boundDir), halfDir / 2.0, 1e-6);
0665 BOOST_CHECK_CLOSE(gapBounds2->get(boundDir), halfDir / 2.0, 1e-6);
0666 auto gap1Translation =
0667 Translation3(Vector3::Unit(dirIdx) * (-3 * halfDir - halfDir / 2.0));
0668 Transform3 gap1Transform = base * gap1Translation;
0669
0670 auto gap2Translation =
0671 Translation3(Vector3::Unit(dirIdx) * (3 * halfDir + halfDir / 2.0));
0672 Transform3 gap2Transform = base * gap2Translation;
0673 CHECK_CLOSE_OR_SMALL(gap1->transform().matrix(), gap1Transform.matrix(),
0674 1e-10, 1e-12);
0675 CHECK_CLOSE_OR_SMALL(gap2->transform().matrix(), gap2Transform.matrix(),
0676 1e-10, 1e-12);
0677 }
0678 }
0679 }
0680
0681 BOOST_DATA_TEST_CASE(
0682 UpdateStackOneSided,
0683 ((boost::unit_test::data::make(-1.0, 1.0) ^
0684 boost::unit_test::data::make(VolumeResizeStrategy::Gap,
0685 VolumeResizeStrategy::Expand)) *
0686 boost::unit_test::data::make(AxisDirection::AxisX, AxisDirection::AxisY,
0687 AxisDirection::AxisZ)),
0688 f, strategy, dir) {
0689 auto [dirOrth1, dirOrth2] = CuboidVolumeStack::getOrthogonalAxes(dir);
0690
0691 auto dirIdx = CuboidVolumeStack::axisToIndex(dir);
0692 auto dirOrth1Idx = CuboidVolumeStack::axisToIndex(dirOrth1);
0693 auto dirOrth2Idx = CuboidVolumeStack::axisToIndex(dirOrth2);
0694
0695 auto boundDir = CuboidVolumeBounds::boundsFromAxisDirection(dir);
0696 auto boundDirOrth1 = CuboidVolumeBounds::boundsFromAxisDirection(dirOrth1);
0697 auto boundDirOrth2 = CuboidVolumeBounds::boundsFromAxisDirection(dirOrth2);
0698
0699 auto bounds1 = std::make_shared<CuboidVolumeBounds>(
0700 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0701 {boundDir, 400_mm},
0702 {boundDirOrth1, 100_mm},
0703 {boundDirOrth2, 300_mm}});
0704
0705 auto bounds2 = std::make_shared<CuboidVolumeBounds>(
0706 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0707 {boundDir, 400_mm},
0708 {boundDirOrth1, 100_mm},
0709 {boundDirOrth2, 300_mm}});
0710
0711 auto trf = Transform3::Identity();
0712
0713 auto translation1 = Translation3(Vector3::Unit(dirIdx) * -500_mm);
0714 auto trf1 = trf * translation1;
0715 auto vol1 = std::make_shared<Volume>(trf1, bounds1);
0716
0717 auto translation2 = Translation3(Vector3::Unit(dirIdx) * 500_mm);
0718 auto trf2 = trf * translation2;
0719 auto vol2 = std::make_shared<Volume>(trf2, bounds2);
0720
0721 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
0722
0723 CuboidVolumeStack stack{volumes, dir, VolumeAttachmentStrategy::Gap, strategy,
0724 *logger};
0725 const auto* originalBounds =
0726 dynamic_cast<const CuboidVolumeBounds*>(&stack.volumeBounds());
0727
0728
0729 auto newBounds = std::make_shared<CuboidVolumeBounds>(
0730 dynamic_cast<const CuboidVolumeBounds&>(stack.volumeBounds()));
0731 newBounds->set(boundDir, 950_mm);
0732
0733 auto delta = Translation3(Vector3::Unit(dirIdx) * f * 50_mm);
0734 trf *= delta;
0735
0736
0737 auto checkUnchanged = [&]() {
0738 const auto* bounds =
0739 dynamic_cast<const CuboidVolumeBounds*>(&stack.volumeBounds());
0740 BOOST_REQUIRE(bounds != nullptr);
0741 BOOST_CHECK_EQUAL(*bounds, *originalBounds);
0742 };
0743
0744
0745 BOOST_CHECK_THROW(
0746 auto errDelta = Translation3(Vector3::Unit(dirIdx) * f * 20_mm);
0747 stack.update(newBounds, trf * errDelta, *logger), std::invalid_argument);
0748 checkUnchanged();
0749
0750
0751 BOOST_CHECK_THROW(
0752 auto errDelta = Translation3(Vector3::Unit(dirOrth1Idx) * 10_mm);
0753 stack.update(newBounds, trf * errDelta, *logger), std::invalid_argument);
0754 checkUnchanged();
0755
0756
0757 BOOST_CHECK_THROW(
0758 auto errDelta = Translation3(Vector3::Unit(dirOrth2Idx) * 10_mm);
0759 stack.update(newBounds, trf * errDelta, *logger), std::invalid_argument);
0760 checkUnchanged();
0761
0762
0763 BOOST_CHECK_THROW(
0764 stack.update(newBounds,
0765 trf * AngleAxis3{10_degree, Vector3::Unit(dirOrth1Idx)},
0766 *logger),
0767 std::invalid_argument);
0768 checkUnchanged();
0769
0770 stack.update(newBounds, trf, *logger);
0771
0772 CHECK_CLOSE_OR_SMALL(stack.transform().matrix(), trf.matrix(), 1e-10, 1e-12);
0773 const auto* bounds =
0774 dynamic_cast<const CuboidVolumeBounds*>(&stack.volumeBounds());
0775 BOOST_REQUIRE(bounds != nullptr);
0776 BOOST_CHECK_CLOSE(bounds->get(boundDir), 950_mm, 1e-6);
0777
0778
0779 for (const auto* vol : volumes) {
0780 const auto* volBounds =
0781 dynamic_cast<const CuboidVolumeBounds*>(&vol->volumeBounds());
0782 BOOST_REQUIRE(volBounds != nullptr);
0783 BOOST_CHECK_CLOSE(volBounds->get(boundDirOrth1), 100_mm, 1e-6);
0784 BOOST_CHECK_CLOSE(volBounds->get(boundDirOrth2), 300_mm, 1e-6);
0785 }
0786
0787 if (strategy == VolumeResizeStrategy::Expand) {
0788
0789 BOOST_CHECK_EQUAL(volumes.size(), 3);
0790 const Volume* vol = nullptr;
0791 if (f < 0.0) {
0792
0793 vol = volumes.front();
0794 } else {
0795
0796 vol = volumes.back();
0797 }
0798
0799 const auto* volBounds =
0800 dynamic_cast<const CuboidVolumeBounds*>(&vol->volumeBounds());
0801 BOOST_REQUIRE(volBounds != nullptr);
0802 BOOST_CHECK_CLOSE(volBounds->get(boundDir), 450_mm, 1e-6);
0803 BOOST_CHECK_EQUAL(vol->center()[dirIdx], f * 550_mm);
0804 } else if (strategy == VolumeResizeStrategy::Gap) {
0805
0806 BOOST_CHECK_EQUAL(volumes.size(), 4);
0807
0808 const Volume* gap = nullptr;
0809 if (f < 0.0) {
0810 gap = volumes.front();
0811 } else {
0812 gap = volumes.back();
0813 }
0814 const auto* gapBounds =
0815 dynamic_cast<const CuboidVolumeBounds*>(&gap->volumeBounds());
0816 BOOST_REQUIRE(gapBounds != nullptr);
0817
0818 BOOST_CHECK_CLOSE(gapBounds->get(boundDir), 50_mm, 1e-6);
0819 BOOST_CHECK_EQUAL(gap->center()[dirIdx], f * 950_mm);
0820 }
0821 }
0822
0823
0824
0825
0826
0827
0828
0829
0830
0831
0832
0833
0834
0835
0836
0837
0838
0839
0840
0841
0842
0843
0844
0845
0846
0847
0848
0849
0850
0851 BOOST_DATA_TEST_CASE(ResizeGapMultiple,
0852 boost::unit_test::data::make(AxisDirection::AxisX,
0853 AxisDirection::AxisY,
0854 AxisDirection::AxisZ),
0855 dir) {
0856 auto [dirOrth1, dirOrth2] = CuboidVolumeStack::getOrthogonalAxes(dir);
0857
0858 auto dirIdx = CuboidVolumeStack::axisToIndex(dir);
0859
0860 auto boundDir = CuboidVolumeBounds::boundsFromAxisDirection(dir);
0861 auto boundDirOrth1 = CuboidVolumeBounds::boundsFromAxisDirection(dirOrth1);
0862 auto boundDirOrth2 = CuboidVolumeBounds::boundsFromAxisDirection(dirOrth2);
0863
0864 auto bounds = std::make_shared<CuboidVolumeBounds>(
0865 std::initializer_list<std::pair<CuboidVolumeBounds::BoundValues, double>>{
0866 {boundDir, 100}, {boundDirOrth1, 70}, {boundDirOrth2, 100}});
0867 Transform3 trf = Transform3::Identity();
0868 Volume vol{trf, bounds};
0869
0870 BOOST_TEST_CONTEXT("Positive") {
0871 std::vector<Volume*> volumes = {&vol};
0872 CuboidVolumeStack stack(volumes, dir, VolumeAttachmentStrategy::Gap,
0873 VolumeResizeStrategy::Gap, *logger);
0874
0875 BOOST_CHECK_EQUAL(volumes.size(), 1);
0876 BOOST_CHECK(stack.gaps().empty());
0877
0878 auto newBounds1 = std::make_shared<CuboidVolumeBounds>(
0879 std::initializer_list<
0880 std::pair<CuboidVolumeBounds::BoundValues, double>>{
0881 {boundDir, 200}, {boundDirOrth1, 70}, {boundDirOrth2, 100}});
0882 stack.update(newBounds1, trf * Translation3{Vector3::Unit(dirIdx) * 100},
0883 *logger);
0884 BOOST_CHECK_EQUAL(volumes.size(), 2);
0885 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
0886
0887 BOOST_CHECK_EQUAL(stack.gaps().front()->center()[dirIdx], 200.0);
0888 const auto* updatedBounds = dynamic_cast<const CuboidVolumeBounds*>(
0889 &stack.gaps().front()->volumeBounds());
0890 BOOST_REQUIRE_NE(updatedBounds, nullptr);
0891 BOOST_CHECK_CLOSE(updatedBounds->get(boundDir), 100.0, 1e-6);
0892
0893 auto newBounds2 = std::make_shared<CuboidVolumeBounds>(
0894 std::initializer_list<
0895 std::pair<CuboidVolumeBounds::BoundValues, double>>{
0896 {boundDir, 300}, {boundDirOrth1, 70}, {boundDirOrth2, 100}});
0897 stack.update(newBounds2, trf * Translation3{Vector3::Unit(dirIdx) * 200},
0898 *logger);
0899
0900 BOOST_CHECK_EQUAL(volumes.size(), 2);
0901
0902 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
0903
0904 BOOST_CHECK_EQUAL(stack.gaps().front()->center()[dirIdx], 300.0);
0905 updatedBounds = dynamic_cast<const CuboidVolumeBounds*>(
0906 &stack.gaps().front()->volumeBounds());
0907 BOOST_REQUIRE_NE(updatedBounds, nullptr);
0908 BOOST_CHECK_CLOSE(updatedBounds->get(boundDir), 200.0, 1e-6);
0909 }
0910
0911 BOOST_TEST_CONTEXT("Negative") {
0912 std::vector<Volume*> volumes = {&vol};
0913 CuboidVolumeStack stack(volumes, dir, VolumeAttachmentStrategy::Gap,
0914 VolumeResizeStrategy::Gap, *logger);
0915
0916 BOOST_CHECK_EQUAL(volumes.size(), 1);
0917 BOOST_CHECK(stack.gaps().empty());
0918
0919 auto newBounds1 = std::make_shared<CuboidVolumeBounds>(
0920 std::initializer_list<
0921 std::pair<CuboidVolumeBounds::BoundValues, double>>{
0922 {boundDir, 200}, {boundDirOrth1, 70}, {boundDirOrth2, 100}});
0923 stack.update(newBounds1, trf * Translation3{Vector3::Unit(dirIdx) * -100},
0924 *logger);
0925 BOOST_CHECK_EQUAL(volumes.size(), 2);
0926 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
0927
0928 BOOST_CHECK_EQUAL(stack.gaps().front()->center()[dirIdx], -200.0);
0929 const auto* updatedBounds = dynamic_cast<const CuboidVolumeBounds*>(
0930 &stack.gaps().front()->volumeBounds());
0931 BOOST_REQUIRE_NE(updatedBounds, nullptr);
0932 BOOST_CHECK_CLOSE(updatedBounds->get(boundDir), 100.0, 1e-6);
0933
0934 auto newBounds2 = std::make_shared<CuboidVolumeBounds>(
0935 std::initializer_list<
0936 std::pair<CuboidVolumeBounds::BoundValues, double>>{
0937 {boundDir, 300}, {boundDirOrth1, 70}, {boundDirOrth2, 100}});
0938 stack.update(newBounds2, trf * Translation3{Vector3::Unit(dirIdx) * -200},
0939 *logger);
0940
0941 BOOST_CHECK_EQUAL(volumes.size(), 2);
0942
0943 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
0944
0945 BOOST_CHECK_EQUAL(stack.gaps().front()->center()[dirIdx], -300.0);
0946 updatedBounds = dynamic_cast<const CuboidVolumeBounds*>(
0947 &stack.gaps().front()->volumeBounds());
0948 BOOST_REQUIRE_NE(updatedBounds, nullptr);
0949 BOOST_CHECK_CLOSE(updatedBounds->get(boundDir), 200.0, 1e-6);
0950 }
0951 }
0952
0953 BOOST_DATA_TEST_CASE(InvalidDirection, boost::unit_test::data::make(strategies),
0954 strategy) {
0955 std::vector<Volume*> volumes;
0956 auto vol1 = std::make_shared<Volume>(
0957 Transform3::Identity(),
0958 std::make_shared<CuboidVolumeBounds>(100_mm, 400_mm, 400_mm));
0959 volumes.push_back(vol1.get());
0960
0961
0962 BOOST_CHECK_THROW(CuboidVolumeStack(volumes, AxisDirection::AxisR, strategy),
0963 std::invalid_argument);
0964
0965 auto vol2 = std::make_shared<Volume>(
0966 Transform3::Identity(),
0967 std::make_shared<CuboidVolumeBounds>(100_mm, 400_mm, 400_mm));
0968 volumes.push_back(vol2.get());
0969
0970 BOOST_CHECK_THROW(CuboidVolumeStack(volumes, AxisDirection::AxisR, strategy),
0971 std::invalid_argument);
0972 }
0973
0974 BOOST_DATA_TEST_CASE(InvalidInput,
0975 (boost::unit_test::data::make(strategies) *
0976 boost::unit_test::data::make(AxisDirection::AxisX,
0977 AxisDirection::AxisY,
0978 AxisDirection::AxisZ)),
0979 strategy, direction) {
0980 BOOST_TEST_CONTEXT("Empty Volume") {
0981 std::vector<Volume*> volumes;
0982 BOOST_CHECK_THROW(CuboidVolumeStack(volumes, direction, strategy),
0983 std::invalid_argument);
0984 }
0985
0986 BOOST_TEST_CONTEXT("Volumes rotated relative to each other") {
0987
0988
0989 for (const Vector3 axis : {Vector3::UnitX(), Vector3::UnitY()}) {
0990 std::vector<Volume*> volumes;
0991 auto vol1 = std::make_shared<Volume>(
0992 Transform3{Translation3{Vector3{0_mm, 0_mm, -500_mm}}},
0993 std::make_shared<CuboidVolumeBounds>(100_mm, 400_mm, 400_mm));
0994 volumes.push_back(vol1.get());
0995
0996 BOOST_TEST_MESSAGE("Axis: " << axis);
0997 auto vol2 = std::make_shared<Volume>(
0998 Transform3{Translation3{Vector3{0_mm, 0_mm, 500_mm}} *
0999 AngleAxis3(1_degree, axis)},
1000 std::make_shared<CuboidVolumeBounds>(100_mm, 400_mm, 400_mm));
1001 volumes.push_back(vol2.get());
1002
1003 BOOST_CHECK_THROW(CuboidVolumeStack(volumes, direction, strategy,
1004 VolumeResizeStrategy::Gap, *logger),
1005 std::invalid_argument);
1006 }
1007 }
1008
1009 BOOST_TEST_CONTEXT(
1010 "Volumes shifted in the orthogonal plane relative to each other") {
1011 for (const Vector3& shift :
1012 {Vector3{5_mm, 0, 0}, Vector3{0, -5_mm, 0}, Vector3{2_mm, -2_mm, 0}}) {
1013 std::vector<Volume*> volumes;
1014 auto vol1 = std::make_shared<Volume>(
1015 Transform3{Translation3{Vector3{0_mm, 0_mm, -500_mm}}},
1016 std::make_shared<CuboidVolumeBounds>(100_mm, 400_mm, 400_mm));
1017 volumes.push_back(vol1.get());
1018
1019 auto vol2 = std::make_shared<Volume>(
1020 Transform3{Translation3{Vector3{0_mm, 0_mm, 500_mm} + shift}},
1021 std::make_shared<CuboidVolumeBounds>(100_mm, 400_mm, 400_mm));
1022 volumes.push_back(vol2.get());
1023
1024 BOOST_CHECK_THROW(CuboidVolumeStack(volumes, direction, strategy,
1025 VolumeResizeStrategy::Gap, *logger),
1026 std::invalid_argument);
1027 }
1028 }
1029 }
1030
1031 BOOST_DATA_TEST_CASE(JoinCuboidVolumeSingle,
1032 (boost::unit_test::data::make(AxisDirection::AxisX,
1033 AxisDirection::AxisY,
1034 AxisDirection::AxisZ) *
1035 boost::unit_test::data::make(strategies)),
1036 direction, strategy) {
1037 auto vol = std::make_shared<Volume>(
1038 Transform3::Identity() * Translation3{14_mm, 24_mm, 0_mm} *
1039 AngleAxis3(73_degree, Vector3::UnitX()),
1040 std::make_shared<CuboidVolumeBounds>(100_mm, 400_mm, 400_mm));
1041
1042 std::vector<Volume*> volumes{vol.get()};
1043
1044 CuboidVolumeStack stack(volumes, direction, strategy,
1045 VolumeResizeStrategy::Gap, *logger);
1046
1047
1048
1049 BOOST_CHECK_EQUAL(volumes.size(), 1);
1050 BOOST_CHECK_EQUAL(volumes.at(0), vol.get());
1051 BOOST_CHECK_EQUAL(vol->transform().matrix(), stack.transform().matrix());
1052 BOOST_CHECK_EQUAL(vol->volumeBounds(), stack.volumeBounds());
1053 }
1054
1055 BOOST_AUTO_TEST_SUITE_END()
1056 BOOST_AUTO_TEST_SUITE_END()
1057
1058 }