Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:25:16

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