Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:12:56

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Geometry/GenericCuboidVolumeBounds.hpp"
0013 #include "Acts/Geometry/Volume.hpp"
0014 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0015 #include "Acts/Utilities/BoundingBox.hpp"
0016 #include "Acts/Utilities/Frustum.hpp"
0017 #include "Acts/Utilities/Ray.hpp"
0018 #include "Acts/Visualization/IVisualization3D.hpp"
0019 #include "Acts/Visualization/PlyVisualization3D.hpp"
0020 
0021 #include <algorithm>
0022 #include <array>
0023 #include <cmath>
0024 #include <cstdio>
0025 #include <filesystem>
0026 #include <fstream>
0027 #include <iostream>
0028 #include <memory>
0029 #include <numbers>
0030 #include <set>
0031 #include <sstream>
0032 #include <string>
0033 #include <utility>
0034 #include <vector>
0035 
0036 namespace Acts::Test {
0037 
0038 struct Object {};
0039 
0040 using ObjectBBox = Acts::AxisAlignedBoundingBox<Object, double, 3>;
0041 
0042 using Vector2F = Eigen::Matrix<double, 2, 1>;
0043 using Vector3F = Eigen::Matrix<double, 3, 1>;
0044 using AngleAxis3F = Eigen::AngleAxis<double>;
0045 
0046 std::filesystem::path tmp_path = []() {
0047   auto tmpPath = std::filesystem::temp_directory_path() / "acts_unit_tests";
0048   std::filesystem::create_directory(tmpPath);
0049   std::cout << "Writing test output to: " << tmpPath << std::endl;
0050   return tmpPath;
0051 }();
0052 
0053 std::ofstream tmp(const std::string& path) {
0054   return std::ofstream{(tmp_path / path).string()};
0055 }
0056 
0057 BOOST_AUTO_TEST_CASE(box_construction) {
0058   BOOST_TEST_CONTEXT("2D") {
0059     Object o;
0060     using Box = Acts::AxisAlignedBoundingBox<Object, double, 2>;
0061     Box bb(&o, {-1, -1}, {2, 2});
0062 
0063     typename Box::transform_type rot;
0064     rot = Eigen::Rotation2D<double>(std::numbers::pi / 7.);
0065     Box bb_rot = bb.transformed(rot);
0066 
0067     CHECK_CLOSE_ABS(bb_rot.min(), Vector2F(-1.76874, -1.33485), 1e-4);
0068     CHECK_CLOSE_ABS(bb_rot.max(), Vector2F(2.23582, 2.66971), 1e-4);
0069   }
0070 
0071   BOOST_TEST_CONTEXT("3D") {
0072     Object o;
0073     using Box = Acts::AxisAlignedBoundingBox<Object, double, 3>;
0074     Box bb(&o, {-1, -1, -1}, {2, 2, 2});
0075 
0076     typename Box::transform_type rot;
0077     rot = AngleAxis3F(std::numbers::pi / 3., Vector3F::UnitZ());
0078     Box bb_rot = bb.transformed(rot);
0079 
0080     CHECK_CLOSE_ABS(bb_rot.min(), Vector3F(-2.23205, -1.36603, -1), 1e-4);
0081     CHECK_CLOSE_ABS(bb_rot.max(), Vector3F(1.86603, 2.73205, 2), 1e-4);
0082 
0083     rot *= AngleAxis3F(std::numbers::pi / 5., Vector3F(1, 1, 0).normalized());
0084     Box bb_rot2 = bb.transformed(rot);
0085 
0086     CHECK_CLOSE_ABS(bb_rot2.min(), Vector3F(-2.40848, -1.51816, -2.0559), 1e-4);
0087     CHECK_CLOSE_ABS(bb_rot2.max(), Vector3F(2.61021, 3.03631, 2.86491), 1e-4);
0088   }
0089 }
0090 
0091 BOOST_AUTO_TEST_CASE(intersect_points) {
0092   using VertexType = ObjectBBox::VertexType;
0093 
0094   Object o;
0095   ObjectBBox bb(&o, {0, 0, 0}, {1, 1, 1});
0096   VertexType p;
0097 
0098   p = {0.5, 0.5, 0.5};
0099   BOOST_CHECK(bb.intersect(p));
0100   p = {0.25, 0.25, 0.25};
0101   BOOST_CHECK(bb.intersect(p));
0102   p = {0.75, 0.75, 0.75};
0103   BOOST_CHECK(bb.intersect(p));
0104 
0105   // lower bound is inclusive
0106   p = {0, 0, 0};
0107   BOOST_CHECK(bb.intersect(p));
0108   // upper bound is exclusive
0109   p = {1.0, 1.0, 1.0};
0110   BOOST_CHECK(!bb.intersect(p));
0111 
0112   // some outsides
0113   p = {2, 0, 0};
0114   BOOST_CHECK(!bb.intersect(p));
0115   p = {0, 2, 0};
0116   BOOST_CHECK(!bb.intersect(p));
0117   p = {0, 0, 2};
0118   BOOST_CHECK(!bb.intersect(p));
0119   p = {2, 2, 0};
0120   BOOST_CHECK(!bb.intersect(p));
0121   p = {2, 0, 2};
0122   BOOST_CHECK(!bb.intersect(p));
0123   p = {2, 2, 2};
0124   BOOST_CHECK(!bb.intersect(p));
0125 
0126   p = {-1, 0, 0};
0127   BOOST_CHECK(!bb.intersect(p));
0128   p = {0, -1, 0};
0129   BOOST_CHECK(!bb.intersect(p));
0130   p = {0, 0, -1};
0131   BOOST_CHECK(!bb.intersect(p));
0132   p = {-1, -1, 0};
0133   BOOST_CHECK(!bb.intersect(p));
0134   p = {-1, 0, -1};
0135   BOOST_CHECK(!bb.intersect(p));
0136   p = {-1, -1, -1};
0137   BOOST_CHECK(!bb.intersect(p));
0138 }
0139 
0140 BOOST_AUTO_TEST_CASE(intersect_rays) {
0141   BOOST_TEST_CONTEXT("2D") {
0142     using Box = AxisAlignedBoundingBox<Object, double, 2>;
0143 
0144     Object o;
0145     Box bb(&o, {-1, -1}, {1, 1});
0146 
0147     // ray in positive x direction
0148 
0149     Ray<double, 2> ray({-2, 0}, {1, 0});
0150     BOOST_CHECK(bb.intersect(ray));
0151 
0152     ray = {{-2, 2}, {1, 0}};
0153     BOOST_CHECK(!bb.intersect(ray));
0154 
0155     ray = {{-2, -2}, {1, 0}};
0156     BOOST_CHECK(!bb.intersect(ray));
0157 
0158     // upper bound is exclusive - temporarily removed, fails macOS ci
0159     // ray = {{-2, 1}, {1, 0}};
0160     // BOOST_CHECK(!bb.intersect(ray));
0161 
0162     // lower bound is inclusive
0163     ray = {{-2, -1}, {1, 0}};
0164     BOOST_CHECK(bb.intersect(ray));
0165 
0166     // ray faces away from box
0167     ray = {{2, 0}, {1, 0}};
0168     BOOST_CHECK(!bb.intersect(ray));
0169 
0170     // ray in negative x direction
0171 
0172     ray = {{2, 0}, {-1, 0}};
0173     BOOST_CHECK(bb.intersect(ray));
0174 
0175     ray = {{2, 2}, {-1, 0}};
0176     BOOST_CHECK(!bb.intersect(ray));
0177 
0178     ray = {{2, -2}, {-1, 0}};
0179     BOOST_CHECK(!bb.intersect(ray));
0180 
0181     // upper bound is exclusive - temporarily removed, fails macOS ci
0182     // ray = {{2, 1}, {-1, 0}};
0183     // BOOST_CHECK(!bb.intersect(ray));
0184 
0185     // lower bound is inclusive
0186     ray = {{2, -1}, {-1, 0}};
0187     BOOST_CHECK(bb.intersect(ray));
0188 
0189     // ray in positive y direction
0190 
0191     ray = {{0, -2}, {0, 1}};
0192     BOOST_CHECK(bb.intersect(ray));
0193 
0194     ray = {{2, -2}, {0, 1}};
0195     BOOST_CHECK(!bb.intersect(ray));
0196 
0197     ray = {{-2, -2}, {0, 1}};
0198     BOOST_CHECK(!bb.intersect(ray));
0199 
0200     // upper bound is exclusive
0201     ray = {{1, -2}, {0, 1}};
0202     BOOST_CHECK(!bb.intersect(ray));
0203 
0204     // lower bound is not inclusive, due to Eigen's NaN handling.
0205     ray = {{-1, -2}, {0, 1}};
0206     BOOST_CHECK(!bb.intersect(ray));
0207 
0208     // other direction
0209     ray = {{0, -2}, {0, -1}};
0210     BOOST_CHECK(!bb.intersect(ray));
0211 
0212     // ray in positive y direction
0213 
0214     ray = {{0, 2}, {0, -1}};
0215     BOOST_CHECK(bb.intersect(ray));
0216 
0217     ray = {{2, 2}, {0, -1}};
0218     BOOST_CHECK(!bb.intersect(ray));
0219 
0220     ray = {{-2, 2}, {0, -1}};
0221     BOOST_CHECK(!bb.intersect(ray));
0222 
0223     // upper bound is exclusive
0224     ray = {{1, 2}, {0, -1}};
0225     BOOST_CHECK(!bb.intersect(ray));
0226 
0227     // lower bound is not inclusive, due to Eigen's NaN handling.
0228     ray = {{-1, 2}, {0, -1}};
0229     BOOST_CHECK(!bb.intersect(ray));
0230 
0231     // other direction
0232     ray = {{0, 2}, {0, 1}};
0233     BOOST_CHECK(!bb.intersect(ray));
0234 
0235     // some off axis rays
0236 
0237     ray = {{-2, 0}, {0.5, 0.25}};
0238     BOOST_CHECK(bb.intersect(ray));
0239 
0240     ray = {{-2, 0}, {0.5, 0.4}};
0241     BOOST_CHECK(bb.intersect(ray));
0242 
0243     ray = {{-2, 0}, {0.5, 0.6}};
0244     BOOST_CHECK(!bb.intersect(ray));
0245 
0246     ray = {{-2, 0}, {0.5, 0.1}};
0247     BOOST_CHECK(bb.intersect(ray));
0248 
0249     ray = {{-2, 0}, {0.5, -0.4}};
0250     BOOST_CHECK(bb.intersect(ray));
0251 
0252     ray = {{-2, 0}, {0.5, -0.6}};
0253     BOOST_CHECK(!bb.intersect(ray));
0254 
0255     ray = {{-2, 0}, {0.1, 0.5}};
0256     BOOST_CHECK(!bb.intersect(ray));
0257 
0258     // starting point inside
0259     ray = {{0, 0}, {-1, 0}};
0260     BOOST_CHECK(bb.intersect(ray));
0261     ray = {{0, 0}, {1, 0}};
0262     BOOST_CHECK(bb.intersect(ray));
0263     ray = {{0, 0}, {0, -1}};
0264     BOOST_CHECK(bb.intersect(ray));
0265     ray = {{0, 0}, {0, 1}};
0266     BOOST_CHECK(bb.intersect(ray));
0267   }
0268 
0269   BOOST_TEST_CONTEXT("3D visualize") {
0270     Object o;
0271 
0272     // let's make sure it also works in 3d
0273     ObjectBBox bb3(&o, {-1, -1, -1}, {1, 1, 1});
0274     Ray<double, 3> ray3({0, 0, -2}, {0, 0, 1});
0275     BOOST_CHECK(bb3.intersect(ray3));
0276 
0277     PlyVisualization3D<double> ply;
0278 
0279     ray3.draw(ply);
0280     auto os = tmp("ray3d.ply");
0281     os << ply << std::flush;
0282     os.close();
0283   }
0284 
0285   BOOST_TEST_CONTEXT("3D") {
0286     using VertexType3 = ObjectBBox::VertexType;
0287     Object o;
0288 
0289     // let's make sure it also works in 3d
0290     ObjectBBox bb3(&o, {-1, -1, -1}, {1, 1, 1});
0291     Ray<double, 3> ray3({0, 0, -2}, {0, 0, 1});
0292     BOOST_CHECK(bb3.intersect(ray3));
0293 
0294     // facing away from box
0295     ray3 = {{0, 0, -2}, {0, 0, -1}};
0296     BOOST_CHECK(!bb3.intersect(ray3));
0297 
0298     ray3 = {{0, 2, -2}, {0, 0, 1}};
0299     BOOST_CHECK(!bb3.intersect(ray3));
0300 
0301     ray3 = {{0, -2, -2}, {0, 0, 1}};
0302     BOOST_CHECK(!bb3.intersect(ray3));
0303 
0304     // right on slab - temporarily removed, fails macOS ci
0305     // ray3 = {{0, 1, -2}, {0, 0, 1}};
0306     // BOOST_CHECK(!bb3.intersect(ray3));
0307 
0308     // right on slab
0309     ray3 = {{0, -1, -2}, {0, 0, 1}};
0310     BOOST_CHECK(bb3.intersect(ray3));
0311 
0312     // right on slab
0313     ray3 = {{-1, 0, -2}, {0, 0, 1}};
0314     BOOST_CHECK(!bb3.intersect(ray3));
0315 
0316     // right on slab
0317     ray3 = {{1, 0, -2}, {0, 0, 1}};
0318     BOOST_CHECK(!bb3.intersect(ray3));
0319 
0320     ray3 = {{-0.95, 0, -2}, {0, 0, 1}};
0321     BOOST_CHECK(bb3.intersect(ray3));
0322 
0323     // some off-axis rays
0324     ObjectBBox::VertexType p(0, 0, -2);
0325 
0326     ray3 = {p, VertexType3(1, 1, 1) - p};
0327     BOOST_CHECK(bb3.intersect(ray3));
0328 
0329     ray3 = {p, VertexType3(-1, 1, 1) - p};
0330     BOOST_CHECK(bb3.intersect(ray3));
0331 
0332     ray3 = {p, VertexType3(-1, -1, 1) - p};
0333     BOOST_CHECK(bb3.intersect(ray3));
0334 
0335     ray3 = {p, VertexType3(1, -1, 1) - p};
0336     BOOST_CHECK(bb3.intersect(ray3));
0337 
0338     ray3 = {p, VertexType3(1.1, 0, -1) - p};
0339     BOOST_CHECK(!bb3.intersect(ray3));
0340 
0341     ray3 = {p, VertexType3(-1.1, 0, -1) - p};
0342     BOOST_CHECK(!bb3.intersect(ray3));
0343 
0344     ray3 = {p, VertexType3(0, 1.1, -1) - p};
0345     BOOST_CHECK(!bb3.intersect(ray3));
0346 
0347     ray3 = {p, VertexType3(0, -1.1, -1) - p};
0348     BOOST_CHECK(!bb3.intersect(ray3));
0349 
0350     ray3 = {p, VertexType3(0.9, 0, -1) - p};
0351     BOOST_CHECK(bb3.intersect(ray3));
0352 
0353     ray3 = {p, VertexType3(-0.9, 0, -1) - p};
0354     BOOST_CHECK(bb3.intersect(ray3));
0355 
0356     ray3 = {p, VertexType3(0, 0.9, -1) - p};
0357     BOOST_CHECK(bb3.intersect(ray3));
0358 
0359     ray3 = {p, VertexType3(0, -0.9, -1) - p};
0360     BOOST_CHECK(bb3.intersect(ray3));
0361 
0362     ray3 = {{0, 0, 0}, {1, 0, 0}};
0363     BOOST_CHECK(bb3.intersect(ray3));
0364     ray3 = {{0, 0, 0}, {0, 1, 0}};
0365     BOOST_CHECK(bb3.intersect(ray3));
0366     ray3 = {{0, 0, 0}, {0, 0, 1}};
0367     BOOST_CHECK(bb3.intersect(ray3));
0368 
0369     ray3 = {{0, 0, 0}, {-1, 0, 0}};
0370     BOOST_CHECK(bb3.intersect(ray3));
0371     ray3 = {{0, 0, 0}, {0, -1, 0}};
0372     BOOST_CHECK(bb3.intersect(ray3));
0373     ray3 = {{0, 0, 0}, {0, 0, -1}};
0374     BOOST_CHECK(bb3.intersect(ray3));
0375   }
0376 }  // namespace Test
0377 
0378 BOOST_AUTO_TEST_CASE(ray_obb_intersect) {
0379   using Ray = Ray<double, 3>;
0380 
0381   std::array<Vector3, 8> vertices;
0382   vertices = {{{0, 0, 0},
0383                {2, 0, 0.4},
0384                {2, 1, 0.4},
0385                {0, 1, 0},
0386                {0, 0, 1},
0387                {1.8, 0, 1},
0388                {1.8, 1, 1},
0389                {0, 1, 1}}};
0390   auto cubo = std::make_shared<GenericCuboidVolumeBounds>(vertices);
0391   auto trf = Transform3(
0392       Translation3(Vector3(0, 8, -5)) *
0393       AngleAxis3(std::numbers::pi / 3., Vector3(1, -3, 9).normalized()));
0394 
0395   Volume vol(trf, cubo);
0396 
0397   PlyVisualization3D<double> ply;
0398 
0399   Transform3 trl = Transform3::Identity();
0400   trl.translation() = trf.translation();
0401 
0402   cubo->draw(ply);
0403 
0404   auto obb = vol.orientedBoundingBox();
0405   obb.draw(ply, {200, 0, 0});
0406 
0407   ply.clear();
0408 
0409   Vector3 origin(10, -20, 6);
0410   Vector3 centroid(0., 0., 0.);
0411 
0412   for (const auto& vtx_ : vertices) {
0413     Vector3 vtx = trf * vtx_;
0414     centroid += vtx;
0415   }
0416 
0417   // approximately the centroid
0418   centroid *= 0.125;
0419 
0420   // shoot rays to the corner points of the cuboid
0421   for (const auto& vtx_ : vertices) {
0422     Vector3 vtx = trf * vtx_;
0423 
0424     // this ray goes straight to the actual vertex, this should definitely
0425     // intersect the OBB
0426     Ray ray(origin, (vtx - origin).normalized());
0427     ray = ray.transformed(trf.inverse());
0428     BOOST_CHECK(obb.intersect(ray));
0429     ray.draw(ply, (vtx - origin).norm());
0430 
0431     // now shift the target point away from the centroid this should definitely
0432     // NOT intersect the OBB
0433     vtx += (vtx - centroid);
0434     ray = Ray(origin, (vtx - origin).normalized());
0435     ray = ray.transformed(trf.inverse());
0436     BOOST_CHECK(!obb.intersect(ray));
0437     ray.draw(ply, (vtx - origin).norm());
0438   }
0439 }
0440 
0441 BOOST_AUTO_TEST_CASE(frustum_intersect) {
0442   BOOST_TEST_CONTEXT("2D") {
0443     auto make_svg = [](const std::string& fname, std::size_t width,
0444                        std::size_t height) {
0445       auto os = tmp(fname);
0446       os << "<?xml version=\"1.0\" standalone=\"no\"?>\n";
0447       os << "<svg width=\"" << width << "\" height=\"" << height
0448          << "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n";
0449       return os;
0450     };
0451 
0452     using Frustum2 = Frustum<double, 2, 2>;
0453 
0454     std::ofstream os;
0455 
0456     const std::size_t svgWidth = 1000;
0457     const std::size_t svgHeight = svgWidth;
0458     const std::size_t nSteps = 10;
0459 
0460     // Visualise the parameters
0461     const double min = -20;
0462     const double max = 20;
0463     os = make_svg("frust2d_parameters.svg", svgWidth, svgHeight);
0464 
0465     const double step = (max - min) / nSteps;
0466     for (std::size_t i = 0; i <= nSteps; i++) {
0467       for (std::size_t j = 0; j <= nSteps; j++) {
0468         Vector2F dir(1, 0);
0469         Vector2F origin(min + step * i, min + step * j);
0470         origin.x() *= 1.10;
0471         Eigen::Rotation2D<double> rot(2 * std::numbers::pi / nSteps * i);
0472         double angle = std::numbers::pi / 2. / nSteps * j;
0473         Frustum2 fr(origin, rot * dir, angle);
0474         fr.svg(os, svgWidth, svgHeight, 2);
0475       }
0476     }
0477 
0478     os << "</svg>";
0479     os.close();
0480 
0481     const double unit = 20.;
0482 
0483     using Box = AxisAlignedBoundingBox<Object, double, 2>;
0484     Object o;
0485     Box::Size size(Eigen::Matrix<double, 2, 1>(2, 2));
0486 
0487     const double minX = -20.;
0488     const double minY = -20.;
0489     const double maxX = 20.;
0490     const double maxY = 20.;
0491     const double stepX = (maxX - minX) / nSteps;
0492     const double stepY = (maxY - minY) / nSteps;
0493 
0494     std::set<std::size_t> idxsAct;
0495 
0496     // clang-format off
0497     std::vector<std::pair<Frustum2, std::set<std::size_t>>> frExp;
0498     frExp = {
0499         {Frustum2({0, 0}, {1, 0}, std::numbers::pi / 2.),
0500          {60,  70,  71,  72,  80,  81,  82,  83,  84,  90,  91,  92,
0501           93,  94,  95,  96,  100, 101, 102, 103, 104, 105, 106, 107,
0502           108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120}
0503         },
0504         {Frustum2({0, 0}, {1, 0}, 0.5 * std::numbers::pi / 2.),
0505          {60,  71,  81,  82,  83,  92,  93,  94, 102,
0506           103, 104, 105, 106, 113, 114, 115, 116, 117}
0507         },
0508         {Frustum2({0, 0}, {1, 0}, 0.2 * std::numbers::pi / 2.),
0509          {60, 71, 82, 93, 104, 114, 115, 116}
0510         },
0511         {Frustum2({0, 0}, {1, 0},  3 * std::numbers::pi / 4.),
0512          {60, 68, 69, 70, 71, 72, 73, 74, 77, 78, 79, 80, 81, 82, 83,
0513           84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
0514           99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
0515           112, 113, 114, 115, 116, 117, 118, 119, 120}
0516         },
0517         {Frustum2({0, 0}, {0, 1}, 0.5 * std::numbers::pi / 2.),
0518          {42, 43, 51, 52, 53, 54, 60, 61, 62, 63, 64, 65, 73, 74, 75, 76, 86, 87}
0519         },
0520         {Frustum2({0, 0}, {-1, 0}, 0.5 * std::numbers::pi / 2.),
0521          {3, 4, 5, 6, 7, 14, 15, 16, 17, 18, 26, 27, 28, 37, 38, 39, 49, 60}
0522         },
0523         {Frustum2({0, 0}, {0, -1}, 0.5 * std::numbers::pi / 2.),
0524          {33, 34, 44, 45, 46, 47, 55, 56, 57, 58, 59, 60, 66, 67, 68, 69, 77, 78}
0525         },
0526         {Frustum2({0, 0}, {1, 1}, 0.5 * std::numbers::pi / 2.),
0527          {60, 72, 73, 74, 83, 84, 85, 86, 87, 94, 95, 96, 97, 98, 106, 107,
0528           108, 109, 117, 118, 119, 120}
0529         },
0530         {Frustum2({0, 0}, {-1, 1}, 0.5 * std::numbers::pi / 2.),
0531          {7, 8, 9, 10, 18, 19, 20, 21, 28, 29, 30, 31, 32, 39, 40, 41, 42,
0532           43, 50, 51, 52, 60}
0533         },
0534         {Frustum2({0, 0}, {-1, -1}, 0.5 * std::numbers::pi / 2.),
0535          {0, 1, 2, 3, 11, 12, 13, 14, 22, 23, 24, 25, 26, 33, 34, 35, 36,
0536           37, 46, 47, 48, 60}
0537         },
0538         {Frustum2({0, 0}, {1, -1}, 0.5 * std::numbers::pi / 2.),
0539          {60, 68, 69, 70, 77, 78, 79, 80, 81, 88, 89, 90, 91, 92, 99, 100,
0540           101, 102, 110, 111, 112, 113}
0541         },
0542         {Frustum2({1, 1}, {1, -1}, std::numbers::pi / 2.),
0543          {55, 56, 57, 58, 59, 60, 66, 67, 68, 69, 70, 71, 77, 78, 79, 80,
0544           81, 82, 88, 89, 90, 91, 92, 93, 99, 100, 101, 102, 103, 104, 110, 111, 112, 113, 114, 115}
0545         },
0546         {Frustum2({-1, -1}, {1, -1}, std::numbers::pi / 2.),
0547          {55, 56, 57, 58, 59, 60, 66, 67, 68, 69, 70, 71, 77, 78, 79, 80,
0548           81, 82, 88, 89, 90, 91, 92, 93, 99, 100, 101, 102, 103, 104, 110, 111, 112, 113, 114, 115}
0549         },
0550         {Frustum2({10, -10}, {1, 1}, 0.5 * std::numbers::pi / 2.),
0551          {91, 92, 102, 103, 104, 105, 114, 115, 116, 117, 118, 119}
0552         },
0553         {Frustum2({-10.3, 12.8}, {0.3, -1}, 0.5 * std::numbers::pi / 2.),
0554          {22, 23, 24, 25, 26, 27, 28, 33, 34, 35, 36, 37, 38, 39, 40, 41,
0555           44, 45, 46, 47, 48, 49, 50, 55, 56, 57, 58, 59, 60, 66, 67, 68,
0556           69, 70, 77, 78, 79, 80, 88, 89, 99}
0557         },
0558         {Frustum2({17.2, 19.45}, {-1, -0.1}, 0.5 * std::numbers::pi / 2.),
0559          {5, 6, 7, 8, 9, 10, 17, 18, 19, 20, 21, 28, 29, 30, 31, 32, 40,
0560           41, 42, 43, 51, 52, 53, 54, 63, 64, 65, 74, 75, 76, 86, 87, 97,
0561           98, 109}
0562         },
0563     };
0564     // clang-format on
0565 
0566     for (std::size_t l = 0; l < frExp.size(); l++) {
0567       const Frustum2& fr = frExp.at(l).first;
0568       const std::set<std::size_t>& idxsExp = frExp.at(l).second;
0569       std::stringstream ss;
0570       ss << "frust2d_test_" << l << ".svg";
0571       os = make_svg(ss.str(), svgWidth, svgHeight);
0572 
0573       idxsAct.clear();
0574 
0575       std::vector<Box> boxes;
0576       boxes.reserve((nSteps + 1) * (nSteps + 1));
0577       for (std::size_t i = 0; i <= nSteps; i++) {
0578         for (std::size_t j = 0; j <= nSteps; j++) {
0579           boxes.emplace_back(
0580               &o,
0581               Eigen::Matrix<double, 2, 1>{minX + i * stepX, minY + j * stepY},
0582               size);
0583           std::stringstream st;
0584           st << boxes.size() - 1;
0585 
0586           std::string color = "red";
0587           if (boxes.back().intersect(fr)) {
0588             color = "green";
0589             idxsAct.insert(boxes.size() - 1);
0590           }
0591 
0592           boxes.back().svg(os, svgWidth, svgHeight, unit, st.str(), color);
0593         }
0594       }
0595 
0596       BOOST_CHECK(idxsAct == idxsExp);
0597 
0598       fr.svg(os, svgWidth, svgHeight, maxX, unit);
0599       os << "</svg>";
0600 
0601       os.close();
0602     }
0603   }
0604 
0605   PlyVisualization3D<double> helper;
0606   BOOST_TEST_CONTEXT("3D - 3 Sides") {
0607     using Frustum3 = Frustum<double, 3, 3>;
0608     std::ofstream os;
0609     std::size_t n = 10;
0610     const std::size_t nSteps = 5;
0611     double min = -10;
0612     double max = 10;
0613     double step = (max - min) / nSteps;
0614 
0615     // Visualise the parameters
0616     auto make = [&](double angle, const Vector3F& origin,
0617                     std::ofstream& osTmp) {
0618       helper.clear();
0619       double far = 1.;
0620       Frustum3 fr(origin, {0, 0, 1}, angle);
0621       fr.draw(helper, far);
0622       fr = Frustum3(origin, {0, 0, -1}, angle);
0623       fr.draw(helper, far);
0624       fr = Frustum3(origin, {1, 0, 0}, angle);
0625       fr.draw(helper, far);
0626       fr = Frustum3(origin, {-1, 0, 0}, angle);
0627       fr.draw(helper, far);
0628 
0629       fr = Frustum3(origin, {0, 1, 0}, angle);
0630       fr.draw(helper, far);
0631       fr = Frustum3(origin, {0, -1, 0}, angle);
0632       fr.draw(helper, far);
0633 
0634       osTmp << helper << std::flush;
0635 
0636       helper.clear();
0637     };
0638 
0639     os = std::ofstream("frust3d_dir.ply");
0640     for (std::size_t i = 0; i <= nSteps; i++) {
0641       for (std::size_t j = 0; j <= nSteps; j++) {
0642         for (std::size_t k = 0; k <= nSteps; k++) {
0643           Vector3F origin(min + i * step, min + j * step, min + k * step);
0644           make(std::numbers::pi / 4., origin, os);
0645         }
0646       }
0647     }
0648     os.close();
0649 
0650     os = tmp("frust3D_angle.ply");
0651     helper.clear();
0652     for (std::size_t i = 0; i <= n; i++) {
0653       Vector3F origin(i * 4, 0, 0);
0654       AngleAxis3F rot(std::numbers::pi / n * i, Vector3F::UnitY());
0655       double angle = (std::numbers::pi / 2.) / n * (1 + i);
0656       Vector3F dir(1, 0, 0);
0657       Frustum3 fr(origin, rot * dir, angle);
0658       fr.draw(helper, 2);
0659     }
0660 
0661     os << helper << std::flush;
0662     os.close();
0663 
0664     std::set<std::size_t> idxsAct;
0665 
0666     std::vector<std::pair<Frustum3, std::set<std::size_t>>> frExp;
0667     frExp = {
0668         {Frustum3({0, 0, 0}, {1, 0, 0}, std::numbers::pi / 2.),
0669          {
0670              665,  763,  774,  775,  785,  786,  787,  788,  796,  797,  807,
0671              872,  873,  883,  884,  885,  886,  894,  895,  896,  897,  898,
0672              905,  906,  907,  908,  909,  910,  911,  916,  917,  918,  919,
0673              920,  927,  928,  929,  930,  938,  939,  970,  971,  981,  982,
0674              983,  992,  993,  994,  995,  996,  1003, 1004, 1005, 1006, 1007,
0675              1008, 1009, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1025,
0676              1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1036, 1037, 1038,
0677              1039, 1040, 1041, 1042, 1043, 1047, 1048, 1049, 1050, 1051, 1052,
0678              1053, 1058, 1059, 1060, 1061, 1062, 1069, 1070, 1071, 1080, 1081,
0679              1090, 1091, 1092, 1093, 1094, 1101, 1102, 1103, 1104, 1105, 1106,
0680              1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1123, 1124, 1125,
0681              1126, 1127, 1128, 1129, 1130, 1131, 1132, 1134, 1135, 1136, 1137,
0682              1138, 1139, 1140, 1141, 1142, 1143, 1145, 1146, 1147, 1148, 1149,
0683              1150, 1151, 1152, 1153, 1154, 1156, 1157, 1158, 1159, 1160, 1161,
0684              1162, 1163, 1164, 1165, 1167, 1168, 1169, 1170, 1171, 1172, 1173,
0685              1174, 1175, 1176, 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185,
0686              1189, 1190, 1191, 1192, 1193, 1194, 1200, 1201, 1202, 1203, 1204,
0687              1210, 1211, 1212, 1213, 1214, 1215, 1216, 1217, 1221, 1222, 1223,
0688              1224, 1225, 1226, 1227, 1228, 1229, 1232, 1233, 1234, 1235, 1236,
0689              1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247,
0690              1248, 1249, 1250, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258,
0691              1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, 1267, 1268, 1269,
0692              1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280,
0693              1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291,
0694              1292, 1293, 1294, 1295, 1296, 1297, 1298, 1299, 1300, 1301, 1302,
0695              1303, 1304, 1305, 1306, 1307, 1308, 1309, 1310, 1311, 1312, 1313,
0696              1314, 1315, 1316, 1317, 1320, 1321, 1322, 1323, 1324, 1325, 1326,
0697              1327,
0698          }},
0699         {Frustum3({0, 0, 0}, {0, 1, 0}, std::numbers::pi / 2.),
0700          {93,   102,  103,  104,  105,  106,  112,  113,  114,  115,  116,
0701           117,  118,  203,  213,  214,  215,  223,  224,  225,  226,  227,
0702           233,  234,  235,  236,  237,  238,  239,  324,  333,  334,  335,
0703           336,  337,  343,  344,  345,  346,  347,  348,  349,  353,  354,
0704           355,  356,  357,  358,  359,  360,  361,  434,  444,  445,  446,
0705           454,  455,  456,  457,  458,  464,  465,  466,  467,  468,  469,
0706           470,  473,  474,  475,  476,  477,  478,  479,  480,  481,  482,
0707           483,  555,  564,  565,  566,  567,  568,  574,  575,  576,  577,
0708           578,  579,  580,  584,  585,  586,  587,  588,  589,  590,  591,
0709           592,  594,  595,  596,  597,  598,  599,  600,  601,  602,  603,
0710           604,  665,  675,  676,  677,  685,  686,  687,  688,  689,  695,
0711           696,  697,  698,  699,  700,  701,  704,  705,  706,  707,  708,
0712           709,  710,  711,  712,  713,  714,  715,  716,  717,  718,  719,
0713           720,  721,  722,  723,  724,  725,  795,  796,  797,  798,  799,
0714           805,  806,  807,  808,  809,  810,  811,  815,  816,  817,  818,
0715           819,  820,  821,  822,  823,  825,  826,  827,  828,  829,  830,
0716           831,  832,  833,  834,  835,  836,  837,  838,  839,  840,  841,
0717           842,  843,  844,  845,  846,  926,  927,  928,  929,  930,  931,
0718           932,  935,  936,  937,  938,  939,  940,  941,  942,  943,  944,
0719           945,  946,  947,  948,  949,  950,  951,  952,  953,  954,  955,
0720           956,  957,  958,  959,  960,  961,  962,  963,  964,  965,  966,
0721           967,  1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065,
0722           1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076,
0723           1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087,
0724           1088, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197,
0725           1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208,
0726           1209, 1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329,
0727           1330}},
0728         {Frustum3({0, 0, 0}, {0, 0, 1}, std::numbers::pi / 2.),
0729          {32,   42,   43,   53,   54,   63,   64,   65,   75,   76,   86,
0730           87,   98,   153,  163,  164,  173,  174,  175,  183,  184,  185,
0731           186,  195,  196,  197,  207,  208,  219,  263,  273,  274,  283,
0732           284,  285,  294,  295,  296,  304,  305,  306,  307,  316,  317,
0733           318,  327,  328,  329,  339,  340,  351,  373,  384,  394,  395,
0734           404,  405,  406,  414,  415,  416,  417,  424,  425,  426,  427,
0735           428,  436,  437,  438,  439,  448,  449,  450,  460,  461,  472,
0736           483,  494,  504,  505,  514,  515,  516,  524,  525,  526,  527,
0737           535,  536,  537,  538,  545,  546,  547,  548,  549,  557,  558,
0738           559,  560,  568,  569,  570,  571,  580,  581,  582,  592,  593,
0739           604,  614,  615,  625,  626,  635,  636,  637,  645,  646,  647,
0740           648,  655,  656,  657,  658,  659,  665,  666,  667,  668,  669,
0741           670,  677,  678,  679,  680,  681,  689,  690,  691,  692,  701,
0742           702,  703,  713,  714,  724,  725,  735,  736,  745,  746,  747,
0743           755,  756,  757,  758,  765,  766,  767,  768,  769,  776,  777,
0744           778,  779,  780,  787,  788,  789,  790,  791,  798,  799,  800,
0745           801,  802,  809,  810,  811,  812,  813,  821,  822,  823,  824,
0746           833,  834,  835,  845,  846,  855,  856,  857,  866,  867,  868,
0747           876,  877,  878,  879,  887,  888,  889,  890,  898,  899,  900,
0748           901,  909,  910,  911,  912,  920,  921,  922,  923,  931,  932,
0749           933,  934,  942,  943,  944,  945,  954,  955,  956,  965,  966,
0750           967,  976,  977,  978,  987,  988,  989,  998,  999,  1000, 1009,
0751           1010, 1011, 1020, 1021, 1022, 1031, 1032, 1033, 1042, 1043, 1044,
0752           1053, 1054, 1055, 1064, 1065, 1066, 1075, 1076, 1077, 1086, 1087,
0753           1088, 1098, 1099, 1109, 1110, 1120, 1121, 1131, 1132, 1142, 1143,
0754           1153, 1154, 1164, 1165, 1175, 1176, 1186, 1187, 1197, 1198, 1208,
0755           1209, 1220, 1231, 1242, 1253, 1264, 1275, 1286, 1297, 1308, 1319,
0756           1330}},
0757         {Frustum3({0, 0, 0}, {0, 0, 1}, std::numbers::pi / 4.),
0758          {186, 305, 306, 307, 416, 417, 425, 426, 427, 428, 438, 439,
0759           527, 536, 537, 538, 545, 546, 547, 548, 549, 558, 559, 560,
0760           571, 647, 648, 656, 657, 658, 659, 665, 666, 667, 668, 669,
0761           670, 678, 679, 680, 681, 691, 692, 758, 767, 768, 769, 777,
0762           778, 779, 780, 788, 789, 790, 791, 799, 800, 801, 802, 811,
0763           812, 813, 824, 879, 890, 901, 912, 923, 934, 945}},
0764         {Frustum3({0, 0, 0}, {0, 0, 1}, std::numbers::pi / 8.),
0765          {427, 428, 546, 547, 548, 549, 658, 659, 665, 666, 667, 668, 669, 670,
0766           680, 681, 780, 791, 802}},
0767         {Frustum3({0, 0, 0}, {0, 0, 1}, std::numbers::pi * 3. / 4.),
0768          {8,    9,    10,   19,   20,   21,   29,   30,   31,   32,   40,
0769           41,   42,   43,   51,   52,   53,   54,   61,   62,   63,   64,
0770           65,   73,   74,   75,   76,   84,   85,   86,   87,   95,   96,
0771           97,   98,   107,  108,  109,  118,  119,  120,  129,  130,  131,
0772           140,  141,  142,  150,  151,  152,  153,  161,  162,  163,  164,
0773           171,  172,  173,  174,  175,  182,  183,  184,  185,  186,  193,
0774           194,  195,  196,  197,  205,  206,  207,  208,  216,  217,  218,
0775           219,  228,  229,  230,  239,  240,  241,  250,  251,  252,  260,
0776           261,  262,  263,  271,  272,  273,  274,  282,  283,  284,  285,
0777           292,  293,  294,  295,  296,  303,  304,  305,  306,  307,  314,
0778           315,  316,  317,  318,  326,  327,  328,  329,  337,  338,  339,
0779           340,  348,  349,  350,  351,  360,  361,  362,  370,  371,  372,
0780           373,  381,  382,  383,  384,  392,  393,  394,  395,  402,  403,
0781           404,  405,  406,  413,  414,  415,  416,  417,  424,  425,  426,
0782           427,  428,  435,  436,  437,  438,  439,  446,  447,  448,  449,
0783           450,  458,  459,  460,  461,  469,  470,  471,  472,  480,  481,
0784           482,  483,  491,  492,  493,  494,  502,  503,  504,  505,  513,
0785           514,  515,  516,  523,  524,  525,  526,  527,  534,  535,  536,
0786           537,  538,  544,  545,  546,  547,  548,  549,  556,  557,  558,
0787           559,  560,  567,  568,  569,  570,  571,  579,  580,  581,  582,
0788           590,  591,  592,  593,  601,  602,  603,  604,  612,  613,  614,
0789           615,  623,  624,  625,  626,  633,  634,  635,  636,  637,  644,
0790           645,  646,  647,  648,  655,  656,  657,  658,  659,  665,  666,
0791           667,  668,  669,  670,  677,  678,  679,  680,  681,  688,  689,
0792           690,  691,  692,  699,  700,  701,  702,  703,  711,  712,  713,
0793           714,  722,  723,  724,  725,  733,  734,  735,  736,  743,  744,
0794           745,  746,  747,  754,  755,  756,  757,  758,  765,  766,  767,
0795           768,  769,  776,  777,  778,  779,  780,  787,  788,  789,  790,
0796           791,  798,  799,  800,  801,  802,  809,  810,  811,  812,  813,
0797           820,  821,  822,  823,  824,  831,  832,  833,  834,  835,  843,
0798           844,  845,  846,  854,  855,  856,  857,  864,  865,  866,  867,
0799           868,  875,  876,  877,  878,  879,  886,  887,  888,  889,  890,
0800           897,  898,  899,  900,  901,  908,  909,  910,  911,  912,  919,
0801           920,  921,  922,  923,  930,  931,  932,  933,  934,  941,  942,
0802           943,  944,  945,  952,  953,  954,  955,  956,  964,  965,  966,
0803           967,  975,  976,  977,  978,  986,  987,  988,  989,  997,  998,
0804           999,  1000, 1008, 1009, 1010, 1011, 1019, 1020, 1021, 1022, 1030,
0805           1031, 1032, 1033, 1041, 1042, 1043, 1044, 1052, 1053, 1054, 1055,
0806           1063, 1064, 1065, 1066, 1074, 1075, 1076, 1077, 1085, 1086, 1087,
0807           1088, 1096, 1097, 1098, 1099, 1107, 1108, 1109, 1110, 1118, 1119,
0808           1120, 1121, 1129, 1130, 1131, 1132, 1140, 1141, 1142, 1143, 1151,
0809           1152, 1153, 1154, 1162, 1163, 1164, 1165, 1173, 1174, 1175, 1176,
0810           1184, 1185, 1186, 1187, 1195, 1196, 1197, 1198, 1206, 1207, 1208,
0811           1209, 1217, 1218, 1219, 1220, 1228, 1229, 1230, 1231, 1239, 1240,
0812           1241, 1242, 1250, 1251, 1252, 1253, 1261, 1262, 1263, 1264, 1272,
0813           1273, 1274, 1275, 1283, 1284, 1285, 1286, 1294, 1295, 1296, 1297,
0814           1305, 1306, 1307, 1308, 1316, 1317, 1318, 1319, 1327, 1328, 1329,
0815           1330}},
0816         {Frustum3({1.3, -5.9, 3.5}, {0.2, 0.4, 1}, std::numbers::pi / 3.),
0817          {318,  426,  427,  428,  438,  439,  450,  538,  546,  547,  548,
0818           549,  558,  559,  560,  570,  571,  582,  655,  656,  657,  658,
0819           659,  667,  668,  669,  670,  678,  679,  680,  681,  690,  691,
0820           692,  702,  703,  714,  768,  769,  777,  778,  779,  780,  787,
0821           788,  789,  790,  791,  799,  800,  801,  802,  810,  811,  812,
0822           813,  822,  823,  824,  834,  835,  846,  888,  889,  890,  899,
0823           900,  901,  910,  911,  912,  920,  921,  922,  923,  931,  932,
0824           933,  934,  942,  943,  944,  945,  954,  955,  956,  966,  967,
0825           1000, 1010, 1011, 1021, 1022, 1032, 1033, 1042, 1043, 1044, 1053,
0826           1054, 1055, 1064, 1065, 1066, 1074, 1075, 1076, 1077, 1086, 1087,
0827           1088, 1143, 1154, 1165, 1175, 1176, 1186, 1187, 1197, 1198, 1207,
0828           1208, 1209, 1308, 1319, 1330}}};
0829 
0830     for (std::size_t l = 0; l < frExp.size(); l++) {
0831       const Frustum3& fr = frExp.at(l).first;
0832       const std::set<std::size_t>& idxsExp = frExp.at(l).second;
0833       std::stringstream ss;
0834       ss << "frust3d-3s_test_" << l << ".ply";
0835 
0836       os = tmp(ss.str());
0837 
0838       helper.clear();
0839 
0840       idxsAct.clear();
0841 
0842       fr.draw(helper, 50);
0843 
0844       n = 10;
0845       min = -33;
0846       max = 33;
0847       step = (max - min) / n;
0848 
0849       Object o;
0850       using Box = AxisAlignedBoundingBox<Object, double, 3>;
0851       Box::Size size(Eigen::Matrix<double, 3, 1>(2, 2, 2));
0852 
0853       std::size_t idx = 0;
0854 
0855       for (std::size_t i = 0; i <= n; i++) {
0856         for (std::size_t j = 0; j <= n; j++) {
0857           for (std::size_t k = 0; k <= n; k++) {
0858             Eigen::Matrix<double, 3, 1> pos(min + i * step, min + j * step,
0859                                             min + k * step);
0860             Box bb(&o, pos, size);
0861 
0862             Color color = {255, 0, 0};
0863 
0864             if (bb.intersect(fr)) {
0865               color = {0, 255, 0};
0866               idxsAct.insert(idx);
0867             }
0868 
0869             bb.draw(helper, color);
0870             idx++;
0871           }
0872         }
0873       }
0874 
0875       os << helper << std::flush;
0876       os.close();
0877 
0878       BOOST_CHECK(idxsAct == idxsExp);
0879     }
0880   }
0881 
0882   BOOST_TEST_CONTEXT("3D - 4 Sides") {
0883     using Frustum34 = Frustum<double, 3, 4>;
0884     const std::size_t n = 10;
0885     double min = -10;
0886     double max = 10;
0887     const std::size_t s = 5;
0888     double step = (max - min) / s;
0889     std::ofstream os;
0890 
0891     // Visualise the parameters
0892     helper.clear();
0893     os = tmp("frust3d-4s_dir.ply");
0894 
0895     const double constAngle = std::numbers::pi / 4.;
0896     for (std::size_t i = 0; i <= s; i++) {
0897       for (std::size_t j = 0; j <= s; j++) {
0898         for (std::size_t k = 0; k <= s; k++) {
0899           Vector3F origin(min + i * step, min + j * step, min + k * step);
0900           Vector3F dir(1, 0, 0);
0901 
0902           double piOverS = std::numbers::pi / s;
0903           AngleAxis3F rot;
0904           rot = AngleAxis3F(piOverS * i, Vector3F::UnitX()) *
0905                 AngleAxis3F(piOverS * j, Vector3F::UnitY()) *
0906                 AngleAxis3F(piOverS * k, Vector3F::UnitZ());
0907 
0908           Frustum34 fr(origin, rot * dir, constAngle);
0909           fr.draw(helper, 1);
0910         }
0911       }
0912     }
0913 
0914     os << helper << std::flush;
0915     os.close();
0916 
0917     os = tmp("frust3d-4s_angle.ply");
0918     helper.clear();
0919 
0920     for (std::size_t i = 0; i <= n; i++) {
0921       Vector3F origin(i * 4, 0, 0);
0922       AngleAxis3F rot(std::numbers::pi / n * i, Vector3F::UnitY());
0923       const double angle = (std::numbers::pi / 2.) / n * (1 + i);
0924       Vector3F dir(1, 0, 0);
0925       Frustum34 fr(origin, rot * dir, angle);
0926       fr.draw(helper, 2);
0927     }
0928 
0929     os << helper << std::flush;
0930     os.close();
0931 
0932     std::set<std::size_t> idxsAct;
0933 
0934     std::vector<std::pair<Frustum34, std::set<std::size_t>>> frExp;
0935     frExp = {
0936         {Frustum34({0, 0, 0}, {1, 0, 0}, std::numbers::pi / 2.),
0937          {665,  774,  775,  776,  785,  786,  787,  796,  797,  798,  883,
0938           884,  885,  886,  887,  894,  895,  896,  897,  898,  905,  906,
0939           907,  908,  909,  916,  917,  918,  919,  920,  927,  928,  929,
0940           930,  931,  992,  993,  994,  995,  996,  997,  998,  1003, 1004,
0941           1005, 1006, 1007, 1008, 1009, 1014, 1015, 1016, 1017, 1018, 1019,
0942           1020, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1036, 1037, 1038,
0943           1039, 1040, 1041, 1042, 1047, 1048, 1049, 1050, 1051, 1052, 1053,
0944           1058, 1059, 1060, 1061, 1062, 1063, 1064, 1101, 1102, 1103, 1104,
0945           1105, 1106, 1107, 1108, 1109, 1112, 1113, 1114, 1115, 1116, 1117,
0946           1118, 1119, 1120, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130,
0947           1131, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1145,
0948           1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1156, 1157, 1158,
0949           1159, 1160, 1161, 1162, 1163, 1164, 1167, 1168, 1169, 1170, 1171,
0950           1172, 1173, 1174, 1175, 1178, 1179, 1180, 1181, 1182, 1183, 1184,
0951           1185, 1186, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197,
0952           1210, 1211, 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220,
0953           1221, 1222, 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231,
0954           1232, 1233, 1234, 1235, 1236, 1237, 1238, 1239, 1240, 1241, 1242,
0955           1243, 1244, 1245, 1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253,
0956           1254, 1255, 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264,
0957           1265, 1266, 1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275,
0958           1276, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1285, 1286,
0959           1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297,
0960           1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308,
0961           1309, 1310, 1311, 1312, 1313, 1314, 1315, 1316, 1317, 1318, 1319,
0962           1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1330}},
0963         {Frustum34({0, 0, 0}, {0, 1, 0}, std::numbers::pi / 2.),
0964          {110,  111,  112,  113,  114,  115,  116,  117,  118,  119,  120,
0965           221,  222,  223,  224,  225,  226,  227,  228,  229,  231,  232,
0966           233,  234,  235,  236,  237,  238,  239,  240,  241,  332,  333,
0967           334,  335,  336,  337,  338,  342,  343,  344,  345,  346,  347,
0968           348,  349,  350,  352,  353,  354,  355,  356,  357,  358,  359,
0969           360,  361,  362,  443,  444,  445,  446,  447,  453,  454,  455,
0970           456,  457,  458,  459,  463,  464,  465,  466,  467,  468,  469,
0971           470,  471,  473,  474,  475,  476,  477,  478,  479,  480,  481,
0972           482,  483,  554,  555,  556,  564,  565,  566,  567,  568,  574,
0973           575,  576,  577,  578,  579,  580,  584,  585,  586,  587,  588,
0974           589,  590,  591,  592,  594,  595,  596,  597,  598,  599,  600,
0975           601,  602,  603,  604,  665,  675,  676,  677,  685,  686,  687,
0976           688,  689,  695,  696,  697,  698,  699,  700,  701,  705,  706,
0977           707,  708,  709,  710,  711,  712,  713,  715,  716,  717,  718,
0978           719,  720,  721,  722,  723,  724,  725,  796,  797,  798,  806,
0979           807,  808,  809,  810,  816,  817,  818,  819,  820,  821,  822,
0980           826,  827,  828,  829,  830,  831,  832,  833,  834,  836,  837,
0981           838,  839,  840,  841,  842,  843,  844,  845,  846,  927,  928,
0982           929,  930,  931,  937,  938,  939,  940,  941,  942,  943,  947,
0983           948,  949,  950,  951,  952,  953,  954,  955,  957,  958,  959,
0984           960,  961,  962,  963,  964,  965,  966,  967,  1058, 1059, 1060,
0985           1061, 1062, 1063, 1064, 1068, 1069, 1070, 1071, 1072, 1073, 1074,
0986           1075, 1076, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086,
0987           1087, 1088, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197,
0988           1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209,
0989           1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1330}},
0990         {Frustum34({0, 0, 0}, {0, 0, 1}, std::numbers::pi / 2.),
0991          {10,   21,   32,   43,   54,   65,   76,   87,   98,   109,  120,
0992           131,  141,  142,  152,  153,  163,  164,  174,  175,  185,  186,
0993           196,  197,  207,  208,  218,  219,  229,  230,  241,  252,  262,
0994           263,  272,  273,  274,  283,  284,  285,  294,  295,  296,  305,
0995           306,  307,  316,  317,  318,  327,  328,  329,  338,  339,  340,
0996           350,  351,  362,  373,  383,  384,  393,  394,  395,  403,  404,
0997           405,  406,  414,  415,  416,  417,  425,  426,  427,  428,  436,
0998           437,  438,  439,  447,  448,  449,  450,  459,  460,  461,  471,
0999           472,  483,  494,  504,  505,  514,  515,  516,  524,  525,  526,
1000           527,  534,  535,  536,  537,  538,  545,  546,  547,  548,  549,
1001           556,  557,  558,  559,  560,  568,  569,  570,  571,  580,  581,
1002           582,  592,  593,  604,  615,  625,  626,  635,  636,  637,  645,
1003           646,  647,  648,  655,  656,  657,  658,  659,  665,  666,  667,
1004           668,  669,  670,  677,  678,  679,  680,  681,  689,  690,  691,
1005           692,  701,  702,  703,  713,  714,  725,  736,  746,  747,  756,
1006           757,  758,  766,  767,  768,  769,  776,  777,  778,  779,  780,
1007           787,  788,  789,  790,  791,  798,  799,  800,  801,  802,  810,
1008           811,  812,  813,  822,  823,  824,  834,  835,  846,  857,  867,
1009           868,  877,  878,  879,  887,  888,  889,  890,  898,  899,  900,
1010           901,  909,  910,  911,  912,  920,  921,  922,  923,  931,  932,
1011           933,  934,  943,  944,  945,  955,  956,  967,  978,  988,  989,
1012           998,  999,  1000, 1009, 1010, 1011, 1020, 1021, 1022, 1031, 1032,
1013           1033, 1042, 1043, 1044, 1053, 1054, 1055, 1064, 1065, 1066, 1076,
1014           1077, 1088, 1099, 1109, 1110, 1120, 1121, 1131, 1132, 1142, 1143,
1015           1153, 1154, 1164, 1165, 1175, 1176, 1186, 1187, 1197, 1198, 1209,
1016           1220, 1231, 1242, 1253, 1264, 1275, 1286, 1297, 1308, 1319, 1330}},
1017         {Frustum34({0, 0, 0}, {0, 0, 1}, std::numbers::pi / 4.),
1018          {406, 417, 428, 439, 450, 527, 535, 536, 537, 538, 546, 547, 548, 549,
1019           557, 558, 559, 560, 571, 648, 656, 657, 658, 659, 665, 666, 667, 668,
1020           669, 670, 678, 679, 680, 681, 692, 769, 777, 778, 779, 780, 788, 789,
1021           790, 791, 799, 800, 801, 802, 813, 890, 901, 912, 923, 934}},
1022         {Frustum34({0, 0, 0}, {0, 0, 1}, std::numbers::pi / 8.),
1023          {538, 549, 560, 659, 665, 666, 667, 668, 669, 670, 681, 780, 791,
1024           802}},
1025         {Frustum34({0, 0, 0}, {0, 0, 1}, std::numbers::pi * 3. / 4.),
1026          {7,    8,    9,    10,   18,   19,   20,   21,   29,   30,   31,
1027           32,   40,   41,   42,   43,   51,   52,   53,   54,   62,   63,
1028           64,   65,   73,   74,   75,   76,   84,   85,   86,   87,   95,
1029           96,   97,   98,   106,  107,  108,  109,  117,  118,  119,  120,
1030           128,  129,  130,  131,  139,  140,  141,  142,  150,  151,  152,
1031           153,  161,  162,  163,  164,  172,  173,  174,  175,  183,  184,
1032           185,  186,  194,  195,  196,  197,  205,  206,  207,  208,  216,
1033           217,  218,  219,  227,  228,  229,  230,  238,  239,  240,  241,
1034           249,  250,  251,  252,  260,  261,  262,  263,  271,  272,  273,
1035           274,  282,  283,  284,  285,  293,  294,  295,  296,  304,  305,
1036           306,  307,  315,  316,  317,  318,  326,  327,  328,  329,  337,
1037           338,  339,  340,  348,  349,  350,  351,  359,  360,  361,  362,
1038           370,  371,  372,  373,  381,  382,  383,  384,  392,  393,  394,
1039           395,  402,  403,  404,  405,  406,  413,  414,  415,  416,  417,
1040           424,  425,  426,  427,  428,  435,  436,  437,  438,  439,  446,
1041           447,  448,  449,  450,  458,  459,  460,  461,  469,  470,  471,
1042           472,  480,  481,  482,  483,  491,  492,  493,  494,  502,  503,
1043           504,  505,  513,  514,  515,  516,  523,  524,  525,  526,  527,
1044           534,  535,  536,  537,  538,  545,  546,  547,  548,  549,  556,
1045           557,  558,  559,  560,  567,  568,  569,  570,  571,  579,  580,
1046           581,  582,  590,  591,  592,  593,  601,  602,  603,  604,  612,
1047           613,  614,  615,  623,  624,  625,  626,  634,  635,  636,  637,
1048           644,  645,  646,  647,  648,  655,  656,  657,  658,  659,  665,
1049           666,  667,  668,  669,  670,  677,  678,  679,  680,  681,  688,
1050           689,  690,  691,  692,  700,  701,  702,  703,  711,  712,  713,
1051           714,  722,  723,  724,  725,  733,  734,  735,  736,  744,  745,
1052           746,  747,  755,  756,  757,  758,  765,  766,  767,  768,  769,
1053           776,  777,  778,  779,  780,  787,  788,  789,  790,  791,  798,
1054           799,  800,  801,  802,  809,  810,  811,  812,  813,  821,  822,
1055           823,  824,  832,  833,  834,  835,  843,  844,  845,  846,  854,
1056           855,  856,  857,  865,  866,  867,  868,  876,  877,  878,  879,
1057           886,  887,  888,  889,  890,  897,  898,  899,  900,  901,  908,
1058           909,  910,  911,  912,  919,  920,  921,  922,  923,  930,  931,
1059           932,  933,  934,  942,  943,  944,  945,  953,  954,  955,  956,
1060           964,  965,  966,  967,  975,  976,  977,  978,  986,  987,  988,
1061           989,  997,  998,  999,  1000, 1008, 1009, 1010, 1011, 1019, 1020,
1062           1021, 1022, 1030, 1031, 1032, 1033, 1041, 1042, 1043, 1044, 1052,
1063           1053, 1054, 1055, 1063, 1064, 1065, 1066, 1074, 1075, 1076, 1077,
1064           1085, 1086, 1087, 1088, 1096, 1097, 1098, 1099, 1107, 1108, 1109,
1065           1110, 1118, 1119, 1120, 1121, 1129, 1130, 1131, 1132, 1140, 1141,
1066           1142, 1143, 1151, 1152, 1153, 1154, 1162, 1163, 1164, 1165, 1173,
1067           1174, 1175, 1176, 1184, 1185, 1186, 1187, 1195, 1196, 1197, 1198,
1068           1206, 1207, 1208, 1209, 1217, 1218, 1219, 1220, 1228, 1229, 1230,
1069           1231, 1239, 1240, 1241, 1242, 1250, 1251, 1252, 1253, 1261, 1262,
1070           1263, 1264, 1272, 1273, 1274, 1275, 1283, 1284, 1285, 1286, 1294,
1071           1295, 1296, 1297, 1305, 1306, 1307, 1308, 1316, 1317, 1318, 1319,
1072           1327, 1328, 1329, 1330}},
1073         {Frustum34({1.3, -5.9, 3.5}, {0.2, 0.4, 1}, std::numbers::pi / 3.),
1074          {461,  472,  537,  538,  548,  549,  558,  559,  560,  569,  570,
1075           571,  581,  582,  593,  655,  656,  657,  658,  659,  666,  667,
1076           668,  669,  670,  678,  679,  680,  681,  690,  691,  692,  702,
1077           703,  714,  777,  778,  779,  780,  787,  788,  789,  790,  791,
1078           799,  800,  801,  802,  811,  812,  813,  823,  824,  835,  846,
1079           899,  900,  901,  910,  911,  912,  920,  921,  922,  923,  932,
1080           933,  934,  944,  945,  955,  956,  967,  1021, 1022, 1032, 1033,
1081           1042, 1043, 1044, 1053, 1054, 1055, 1064, 1065, 1066, 1076, 1077,
1082           1088, 1143, 1154, 1165, 1175, 1176, 1186, 1187, 1197, 1198, 1209,
1083           1308, 1319, 1330}}};
1084 
1085     min = -33;
1086     max = 33;
1087     step = (max - min) / n;
1088 
1089     for (std::size_t l = 0; l < frExp.size(); l++) {
1090       const Frustum34& fr = frExp.at(l).first;
1091       const std::set<std::size_t>& idxsExp = frExp.at(l).second;
1092       std::stringstream ss;
1093       ss << "frust3d-4s_test_" << l << ".ply";
1094 
1095       os = tmp(ss.str());
1096 
1097       helper.clear();
1098 
1099       idxsAct.clear();
1100 
1101       fr.draw(helper, 50);
1102 
1103       Object o;
1104       using Box = AxisAlignedBoundingBox<Object, double, 3>;
1105       Box::Size size(Eigen::Matrix<double, 3, 1>(2, 2, 2));
1106 
1107       std::size_t idx = 0;
1108       for (std::size_t i = 0; i <= n; i++) {
1109         for (std::size_t j = 0; j <= n; j++) {
1110           for (std::size_t k = 0; k <= n; k++) {
1111             Eigen::Matrix<double, 3, 1> pos(min + i * step, min + j * step,
1112                                             min + k * step);
1113             Box bb(&o, pos, size);
1114 
1115             Color color = {255, 0, 0};
1116 
1117             if (bb.intersect(fr)) {
1118               color = {0, 255, 0};
1119               idxsAct.insert(idx);
1120             }
1121 
1122             bb.draw(helper, color);
1123             idx++;
1124           }
1125         }
1126       }
1127 
1128       os << helper << std::flush;
1129       os.close();
1130 
1131       BOOST_CHECK(idxsAct == idxsExp);
1132     }
1133   }
1134 
1135   BOOST_TEST_CONTEXT("3D - 5 Sides") {
1136     using Frustum = Frustum<double, 3, 5>;
1137     using Box = AxisAlignedBoundingBox<Object, double, 3>;
1138     Box::Size size(Acts::Vector3(2, 2, 2));
1139 
1140     Object o;
1141 
1142     PlyVisualization3D<double> ply;
1143 
1144     Frustum fr({0, 0, 0}, {0, 0, 1}, std::numbers::pi / 8.);
1145     fr.draw(ply, 10);
1146 
1147     Box bb(&o, {0, 0, 10}, size);
1148     bb.draw(ply);
1149 
1150     BOOST_CHECK(bb.intersect(fr));
1151 
1152     auto os = tmp("frust3d-5s.ply");
1153     os << ply << std::flush;
1154     os.close();
1155   }
1156 
1157   BOOST_TEST_CONTEXT("3D - 10 Sides") {
1158     using Frustum = Frustum<double, 3, 10>;
1159     using Box = AxisAlignedBoundingBox<Object, double, 3>;
1160     Box::Size size(Acts::Vector3(2, 2, 2));
1161 
1162     Object o;
1163 
1164     PlyVisualization3D<double> ply;
1165 
1166     Acts::Vector3 pos = {-12.4205, 29.3578, 44.6207};
1167     Acts::Vector3 dir = {-0.656862, 0.48138, 0.58035};
1168     Frustum fr(pos, dir, 0.972419);
1169     fr.draw(ply, 10);
1170 
1171     Box bb(&o, pos + dir * 10, size);
1172     bb.draw(ply);
1173 
1174     BOOST_CHECK(bb.intersect(fr));
1175 
1176     auto os = tmp("frust3d-10s.ply");
1177     os << ply << std::flush;
1178     os.close();
1179   }
1180 
1181   BOOST_TEST_CONTEXT("3D - 4 Sides - Big box") {
1182     using Frustum = Frustum<double, 3, 4>;
1183     using Box = AxisAlignedBoundingBox<Object, double, 3>;
1184 
1185     Object o;
1186 
1187     PlyVisualization3D<double> ply;
1188 
1189     Acts::Vector3 pos = {0, 0, 0};
1190     Acts::Vector3 dir = {0, 0, 1};
1191     Frustum fr(pos, dir, 0.972419);
1192     fr.draw(ply, 10);
1193 
1194     Box::Size size(Acts::Vector3(100, 100, 2));
1195     Box bb(&o, pos + dir * 7, size);
1196     bb.draw(ply);
1197 
1198     BOOST_CHECK(bb.intersect(fr));
1199 
1200     auto os = tmp("frust3d-4s-bigbox.ply");
1201     os << ply << std::flush;
1202     os.close();
1203   }
1204 }
1205 
1206 BOOST_AUTO_TEST_CASE(ostream_operator) {
1207   Object o;
1208   using Box = Acts::AxisAlignedBoundingBox<Object, double, 2>;
1209   Box bb(&o, {-1, -1}, {2, 2});
1210 
1211   std::stringstream ss;
1212   ss << bb;
1213 
1214   BOOST_CHECK(ss.str() == "AABB(ctr=(0.5, 0.5) vmin=(-1, -1) vmax=(2, 2))");
1215 }
1216 
1217 }  // namespace Acts::Test