Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-11 07:50:01

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 #pragma once
0010 
0011 #include "Acts/Utilities/BoundingBox.hpp"
0012 
0013 template <typename entity_t, typename value_t, std::size_t DIM>
0014 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::AxisAlignedBoundingBox(
0015     const entity_t* entity, const VertexType& vmin, const VertexType& vmax)
0016     : m_entity(entity),
0017       m_vmin(vmin),
0018       m_vmax(vmax),
0019       m_center((vmin + vmax) / 2.),
0020       m_width(vmax - vmin),
0021       m_iwidth(1 / m_width) {}
0022 
0023 template <typename entity_t, typename value_t, std::size_t DIM>
0024 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::AxisAlignedBoundingBox(
0025     const entity_t* entity, const VertexType& center, const Size& size)
0026     : m_entity(entity),
0027       m_vmin(center - size.get() * 0.5),
0028       m_vmax(center + size.get() * 0.5),
0029       m_center(center),
0030       m_width(size.get()),
0031       m_iwidth(1 / m_width) {}
0032 
0033 template <typename entity_t, typename value_t, std::size_t DIM>
0034 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::AxisAlignedBoundingBox(
0035     const std::vector<self_t*>& boxes, vertex_array_type envelope)
0036     : m_entity(nullptr) {
0037   assert(boxes.size() > 1);
0038 
0039   for (std::size_t i = 0; i < boxes.size(); i++) {
0040     if (i < boxes.size() - 1) {
0041       // set next on i to i+1
0042       boxes[i]->setSkip(boxes[i + 1]);
0043     } else {
0044       // make sure last is set to nullptr, this marks end
0045       // boxes[i]->m_next = nullptr;
0046       boxes[i]->setSkip(nullptr);
0047     }
0048   }
0049 
0050   m_left_child = boxes.front();
0051   m_right_child = boxes.back();
0052   m_skip = nullptr;
0053 
0054   std::tie(m_vmin, m_vmax) = wrap(boxes, envelope);
0055 
0056   m_center = (m_vmin + m_vmax) / 2.;
0057   m_width = m_vmax - m_vmin;
0058   m_iwidth = 1 / m_width;
0059 }
0060 
0061 template <typename entity_t, typename value_t, std::size_t DIM>
0062 std::pair<
0063     typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType,
0064     typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType>
0065 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::wrap(
0066     const std::vector<const self_t*>& boxes, vertex_array_type envelope) {
0067   assert(boxes.size() > 1);
0068   // figure out extent of boxes
0069   // use array for Eigen coefficient wise min/max
0070   vertex_array_type vmax(
0071       vertex_array_type::Constant(std::numeric_limits<value_type>::lowest()));
0072   vertex_array_type vmin(
0073       vertex_array_type::Constant(std::numeric_limits<value_type>::max()));
0074 
0075   for (std::size_t i = 0; i < boxes.size(); i++) {
0076     vmin = vmin.min(boxes[i]->min().array());
0077     vmax = vmax.max(boxes[i]->max().array());
0078   }
0079 
0080   vmax += envelope;
0081   vmin -= envelope;
0082 
0083   return {vmin, vmax};
0084 }
0085 
0086 template <typename entity_t, typename value_t, std::size_t DIM>
0087 std::pair<
0088     typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType,
0089     typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType>
0090 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::wrap(
0091     const std::vector<self_t*>& boxes, vertex_array_type envelope) {
0092   assert(boxes.size() > 1);
0093   std::vector<const self_t*> box_ptrs;
0094   box_ptrs.reserve(boxes.size());
0095   std::transform(boxes.begin(), boxes.end(), std::back_inserter(box_ptrs),
0096                  [](const auto* box) { return box; });
0097   return wrap(box_ptrs, envelope);
0098 }
0099 
0100 template <typename entity_t, typename value_t, std::size_t DIM>
0101 std::pair<
0102     typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType,
0103     typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType>
0104 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::wrap(
0105     const std::vector<self_t>& boxes, vertex_array_type envelope) {
0106   assert(boxes.size() > 1);
0107   std::vector<const self_t*> box_ptrs;
0108   box_ptrs.reserve(boxes.size());
0109   std::transform(boxes.begin(), boxes.end(), std::back_inserter(box_ptrs),
0110                  [](auto& box) { return &box; });
0111   return wrap(box_ptrs, envelope);
0112 }
0113 
0114 template <typename entity_t, typename value_t, std::size_t DIM>
0115 bool Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::intersect(
0116     const VertexType& point) const {
0117   vertex_array_type t = (point - m_vmin).array() * m_iwidth;
0118   return t.minCoeff() >= 0 && t.maxCoeff() < 1;
0119 }
0120 
0121 template <typename entity_t, typename value_t, std::size_t DIM>
0122 bool Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::intersect(
0123     const Ray<value_type, DIM>& ray) const {
0124   const VertexType& origin = ray.origin();
0125   const vertex_array_type& idir = ray.idir();
0126 
0127   // Calculate the intersect distances with the min and max planes along the ray
0128   // direction, from the ray origin. See Ch VII.5 Fig.1 in [1].
0129   // This is done in all dimensions at the same time:
0130   vertex_array_type t0s = (m_vmin - origin).array() * idir;
0131   vertex_array_type t1s = (m_vmax - origin).array() * idir;
0132 
0133   // Calculate the component wise min/max between the t0s and t1s
0134   // this is non-compliant with IEEE-754-2008, NaN gets propagated through
0135   // https://eigen.tuxfamily.org/bz/show_bug.cgi?id=564
0136   // this means that rays parallel to boundaries might not be considered
0137   // to intersect.
0138   vertex_array_type tsmaller = t0s.min(t1s);
0139   vertex_array_type tbigger = t0s.max(t1s);
0140 
0141   // extract largest and smallest component of the component wise extrema
0142   value_type tmin = tsmaller.maxCoeff();
0143   value_type tmax = tbigger.minCoeff();
0144 
0145   // If tmin is smaller than tmax and tmax is positive, then the box is in
0146   // positive ray direction, and the ray intersects the box.
0147   return tmin < tmax && tmax > 0.0;
0148 }
0149 
0150 template <typename entity_t, typename value_t, std::size_t DIM>
0151 template <std::size_t sides>
0152 bool Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::intersect(
0153     const Frustum<value_type, DIM, sides>& fr) const {
0154   const auto& normals = fr.normals();
0155   // Transform vmin and vmax into the coordinate system, at which the frustum is
0156   // located at the coordinate origin.
0157   const vertex_array_type fr_vmin = m_vmin - fr.origin();
0158   const vertex_array_type fr_vmax = m_vmax - fr.origin();
0159 
0160   // For each plane, find the p-vertex, which is the vertex that is at the
0161   // furthest distance from the plane *along* its normal direction.
0162   // See Fig. 2 in [2].
0163   VertexType p_vtx;
0164   // for loop, we could eliminate this, probably,
0165   // but sides+1 is known at compile time, so the compiler
0166   // will most likely unroll the loop
0167   for (std::size_t i = 0; i < sides + 1; i++) {
0168     const VertexType& normal = normals[i];
0169 
0170     // for AABBs, take the component from the min vertex, if the normal
0171     // component is negative, else take the component from the max vertex.
0172     p_vtx = (normal.array() < 0).template cast<value_type>() * fr_vmin +
0173             (normal.array() >= 0).template cast<value_type>() * fr_vmax;
0174 
0175     // Check if the p-vertex is at positive or negative direction along the
0176     // If the p vertex is along negative normal direction *once*, the box is
0177     // outside the frustum, and we can terminate early.
0178     if (p_vtx.dot(normal) < 0) {
0179       // p vertex is outside on this plane, box must be outside
0180       return false;
0181     }
0182   }
0183 
0184   // If we get here, no p-vertex was outside, so box intersects or is
0185   // contained. We don't care, so report 'intersect'
0186   return true;
0187 }
0188 
0189 template <typename entity_t, typename value_t, std::size_t DIM>
0190 void Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::setSkip(
0191     self_t* skip) {
0192   // set next on this
0193   m_skip = skip;
0194   // find last child and set its skip
0195   if (m_right_child != nullptr) {
0196     m_right_child->setSkip(skip);
0197   }
0198 }
0199 
0200 template <typename entity_t, typename value_t, std::size_t DIM>
0201 const Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>*
0202 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::getLeftChild() const {
0203   return m_left_child;
0204 }
0205 
0206 template <typename entity_t, typename value_t, std::size_t DIM>
0207 const Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>*
0208 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::getSkip() const {
0209   return m_skip;
0210 }
0211 
0212 template <typename entity_t, typename value_t, std::size_t DIM>
0213 bool Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::hasEntity() const {
0214   return m_entity != nullptr;
0215 }
0216 
0217 template <typename entity_t, typename value_t, std::size_t DIM>
0218 const entity_t* Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::entity()
0219     const {
0220   return m_entity;
0221 }
0222 
0223 template <typename entity_t, typename value_t, std::size_t DIM>
0224 void Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::setEntity(
0225     const entity_t* entity) {
0226   m_entity = entity;
0227 }
0228 
0229 template <typename entity_t, typename value_t, std::size_t DIM>
0230 const typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType&
0231 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::center() const {
0232   return m_center;
0233 }
0234 
0235 template <typename entity_t, typename value_t, std::size_t DIM>
0236 const typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType&
0237 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::min() const {
0238   return m_vmin;
0239 }
0240 
0241 template <typename entity_t, typename value_t, std::size_t DIM>
0242 const typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType&
0243 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::max() const {
0244   return m_vmax;
0245 }
0246 
0247 template <typename entity_t, typename value_t, std::size_t DIM>
0248 std::ostream& Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::toStream(
0249     std::ostream& os) const {
0250   os << "AABB(ctr=(";
0251 
0252   for (std::size_t i = 0; i < DIM; i++) {
0253     if (i > 0) {
0254       os << ", ";
0255     }
0256     os << m_center[i];
0257   }
0258 
0259   os << ") vmin=(";
0260   for (std::size_t i = 0; i < DIM; i++) {
0261     if (i > 0) {
0262       os << ", ";
0263     }
0264     os << m_vmin[i];
0265   }
0266 
0267   os << ") vmax=(";
0268 
0269   for (std::size_t i = 0; i < DIM; i++) {
0270     if (i > 0) {
0271       os << ", ";
0272     }
0273     os << m_vmax[i];
0274   }
0275 
0276   os << "))";
0277 
0278   return os;
0279 }
0280 
0281 template <typename entity_t, typename value_t, std::size_t DIM>
0282 std::pair<
0283     typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType,
0284     typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType>
0285 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::transformVertices(
0286     const transform_type& trf) const
0287   requires(DIM == 3)
0288 {
0289   // we need to enumerate all the vertices, transform,
0290   // and then recalculate min and max
0291 
0292   std::array<VertexType, 8> vertices({{
0293       {m_vmin.x(), m_vmin.y(), m_vmin.z()},
0294       {m_vmin.x(), m_vmax.y(), m_vmin.z()},
0295       {m_vmax.x(), m_vmax.y(), m_vmin.z()},
0296       {m_vmax.x(), m_vmin.y(), m_vmin.z()},
0297       {m_vmin.x(), m_vmin.y(), m_vmax.z()},
0298       {m_vmin.x(), m_vmax.y(), m_vmax.z()},
0299       {m_vmax.x(), m_vmax.y(), m_vmax.z()},
0300       {m_vmax.x(), m_vmin.y(), m_vmax.z()},
0301   }});
0302 
0303   VertexType vmin = trf * vertices[0];
0304   VertexType vmax = trf * vertices[0];
0305 
0306   for (std::size_t i = 1; i < 8; i++) {
0307     const VertexType vtx = trf * vertices[i];
0308     vmin = vmin.cwiseMin(vtx);
0309     vmax = vmax.cwiseMax(vtx);
0310   }
0311 
0312   return {vmin, vmax};
0313 }
0314 
0315 template <typename entity_t, typename value_t, std::size_t DIM>
0316 std::pair<
0317     typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType,
0318     typename Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::VertexType>
0319 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::transformVertices(
0320     const transform_type& trf) const
0321   requires(DIM == 2)
0322 {
0323   // we need to enumerate all the vertices, transform,
0324   // and then recalculate min and max
0325 
0326   std::array<VertexType, 4> vertices({{{m_vmin.x(), m_vmin.y()},
0327                                        {m_vmin.x(), m_vmax.y()},
0328                                        {m_vmax.x(), m_vmax.y()},
0329                                        {m_vmax.x(), m_vmin.y()}}});
0330 
0331   VertexType vmin = trf * vertices[0];
0332   VertexType vmax = trf * vertices[0];
0333 
0334   for (std::size_t i = 1; i < 4; i++) {
0335     const VertexType vtx = trf * vertices[i];
0336     vmin = vmin.cwiseMin(vtx);
0337     vmax = vmax.cwiseMax(vtx);
0338   }
0339 
0340   return {vmin, vmax};
0341 }
0342 
0343 template <typename entity_t, typename value_t, std::size_t DIM>
0344 void Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::transform(
0345     const transform_type& trf) {
0346   std::tie(m_vmin, m_vmax) = transformVertices(trf);
0347 }
0348 
0349 template <typename entity_t, typename value_t, std::size_t DIM>
0350 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>
0351 Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::transformed(
0352     const transform_type& trf) const {
0353   const auto [vmin, vmax] = transformVertices(trf);
0354   return self_t(m_entity, vmin, vmax);
0355 }
0356 
0357 template <typename entity_t, typename value_t, std::size_t DIM>
0358 void Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::draw(
0359     IVisualization3D& helper, Color color, const transform_type& trf) const
0360   requires(DIM == 3)
0361 {
0362   static_assert(DIM == 3, "PLY output only supported in 3D");
0363 
0364   const VertexType& vmin = m_vmin;
0365   const VertexType& vmax = m_vmax;
0366 
0367   auto write = [&](const VertexType& a, const VertexType& b,
0368                    const VertexType& c, const VertexType& d) {
0369     helper.face(std::vector<VertexType>({trf * a, trf * b, trf * c, trf * d}),
0370                 color);
0371   };
0372 
0373   write({vmin.x(), vmin.y(), vmin.z()}, {vmin.x(), vmax.y(), vmin.z()},
0374         {vmin.x(), vmax.y(), vmax.z()}, {vmin.x(), vmin.y(), vmax.z()});
0375 
0376   write({vmax.x(), vmin.y(), vmin.z()}, {vmax.x(), vmax.y(), vmin.z()},
0377         {vmax.x(), vmax.y(), vmax.z()}, {vmax.x(), vmin.y(), vmax.z()});
0378 
0379   write({vmin.x(), vmin.y(), vmin.z()}, {vmax.x(), vmin.y(), vmin.z()},
0380         {vmax.x(), vmin.y(), vmax.z()}, {vmin.x(), vmin.y(), vmax.z()});
0381 
0382   write({vmin.x(), vmax.y(), vmin.z()}, {vmax.x(), vmax.y(), vmin.z()},
0383         {vmax.x(), vmax.y(), vmax.z()}, {vmin.x(), vmax.y(), vmax.z()});
0384 
0385   write({vmin.x(), vmin.y(), vmin.z()}, {vmax.x(), vmin.y(), vmin.z()},
0386         {vmax.x(), vmax.y(), vmin.z()}, {vmin.x(), vmax.y(), vmin.z()});
0387 
0388   write({vmin.x(), vmin.y(), vmax.z()}, {vmax.x(), vmin.y(), vmax.z()},
0389         {vmax.x(), vmax.y(), vmax.z()}, {vmin.x(), vmax.y(), vmax.z()});
0390 }
0391 
0392 template <typename entity_t, typename value_t, std::size_t DIM>
0393 std::ostream& Acts::AxisAlignedBoundingBox<entity_t, value_t, DIM>::svg(
0394     std::ostream& os, value_type w, value_type h, value_type unit,
0395     const std::string& label, const std::string& fillcolor) const
0396   requires(DIM == 2)
0397 {
0398   static_assert(DIM == 2, "SVG is only supported in 2D");
0399 
0400   VertexType mid(w / 2., h / 2.);
0401 
0402   using transform_t = Eigen::Transform<value_t, DIM, Eigen::Affine>;
0403 
0404   transform_t trf = transform_t::Identity();
0405   trf.translate(mid);
0406   trf = trf * Eigen::Scaling(VertexType(1, -1));
0407   trf.scale(unit);
0408 
0409   auto draw_point = [&](const VertexType& p_, const std::string& color,
0410                         std::size_t r) {
0411     VertexType p = trf * p_;
0412     os << "<circle ";
0413     os << "cx=\"" << p.x() << "\" cy=\"" << p.y() << "\" r=\"" << r << "\"";
0414     os << " fill=\"" << color << "\"";
0415     os << "/>\n";
0416   };
0417 
0418   auto draw_rect = [&](const VertexType& center_, const VertexType& size_,
0419                        const std::string& color) {
0420     VertexType size = size_ * unit;
0421     VertexType center = trf * center_ - size * 0.5;
0422 
0423     os << "<rect ";
0424     os << "x=\"" << center.x() << "\" y=\"" << center.y() << "\" ";
0425     os << "width=\"" << size.x() << "\" height=\"" << size.y() << "\"";
0426     os << " fill=\"" << color << "\"";
0427     os << "/>\n";
0428   };
0429 
0430   auto draw_text = [&](const VertexType& center_, const std::string& text,
0431                        const std::string& color, std::size_t size) {
0432     VertexType center = trf * center_;
0433     os << "<text dominant-baseline=\"middle\" text-anchor=\"middle\" ";
0434     os << "fill=\"" << color << "\" font-size=\"" << size << "\" ";
0435     os << "x=\"" << center.x() << "\" y=\"" << center.y() << "\">";
0436     os << text << "</text>\n";
0437   };
0438 
0439   draw_rect(m_center, m_width, fillcolor);
0440   draw_point(m_vmin, "black", 2);
0441   draw_point(m_vmax, "black", 2);
0442   draw_text(m_center, label, "white", 10);
0443 
0444   return os;
0445 }
0446 
0447 template <typename box_t>
0448 box_t* octree_inner(std::vector<std::unique_ptr<box_t>>& store,
0449                     std::size_t max_depth,
0450                     typename box_t::vertex_array_type envelope,
0451                     const std::vector<box_t*>& lprims, std::size_t depth) {
0452   using VertexType = typename box_t::VertexType;
0453 
0454   assert(!lprims.empty());
0455   if (lprims.size() == 1) {
0456     // just return
0457     return lprims.front();
0458   }
0459 
0460   if (depth >= max_depth) {
0461     // just wrap them all up
0462     auto bb = std::make_unique<box_t>(lprims, envelope);
0463     store.push_back(std::move(bb));
0464     return store.back().get();
0465   }
0466 
0467   std::array<std::vector<box_t*>, 8> octants;
0468   // calc center of boxes
0469   const auto [vmin, vmax] = box_t::wrap(lprims);
0470   VertexType glob_ctr = (vmin + vmax) / 2.;
0471 
0472   for (auto* box : lprims) {
0473     VertexType ctr = box->center() - glob_ctr;
0474     if (ctr.x() < 0 && ctr.y() < 0 && ctr.z() < 0) {
0475       octants[0].push_back(box);
0476       continue;
0477     }
0478     if (ctr.x() > 0 && ctr.y() < 0 && ctr.z() < 0) {
0479       octants[1].push_back(box);
0480       continue;
0481     }
0482     if (ctr.x() < 0 && ctr.y() > 0 && ctr.z() < 0) {
0483       octants[2].push_back(box);
0484       continue;
0485     }
0486     if (ctr.x() > 0 && ctr.y() > 0 && ctr.z() < 0) {
0487       octants[3].push_back(box);
0488       continue;
0489     }
0490 
0491     if (ctr.x() < 0 && ctr.y() < 0 && ctr.z() > 0) {
0492       octants[4].push_back(box);
0493       continue;
0494     }
0495     if (ctr.x() > 0 && ctr.y() < 0 && ctr.z() > 0) {
0496       octants[5].push_back(box);
0497       continue;
0498     }
0499     if (ctr.x() < 0 && ctr.y() > 0 && ctr.z() > 0) {
0500       octants[6].push_back(box);
0501       continue;
0502     }
0503     if (ctr.x() > 0 && ctr.y() > 0 && ctr.z() > 0) {
0504       octants[7].push_back(box);
0505       continue;
0506     }
0507 
0508     // not in any quadrant (numerics probably)
0509     octants[0].push_back(box);
0510   }
0511 
0512   std::vector<box_t*> sub_octs;
0513   for (const auto& sub_prims : octants) {
0514     if (sub_prims.size() <= 8) {
0515       if (sub_prims.empty()) {
0516         // done
0517       } else if (sub_prims.size() == 1) {
0518         sub_octs.push_back(sub_prims.front());
0519       } else {
0520         store.push_back(std::make_unique<box_t>(sub_prims, envelope));
0521         sub_octs.push_back(store.back().get());
0522       }
0523     } else {
0524       // recurse
0525       sub_octs.push_back(
0526           octree_inner(store, max_depth, envelope, sub_prims, depth + 1));
0527     }
0528   }
0529 
0530   if (sub_octs.size() == 1) {
0531     return sub_octs.front();
0532   }
0533 
0534   auto bb = std::make_unique<box_t>(sub_octs, envelope);
0535   store.push_back(std::move(bb));
0536   return store.back().get();
0537 }
0538 
0539 template <typename box_t>
0540 box_t* Acts::make_octree(std::vector<std::unique_ptr<box_t>>& store,
0541                          const std::vector<box_t*>& prims,
0542                          std::size_t max_depth,
0543                          typename box_t::value_type envelope1) {
0544   static_assert(box_t::dim == 3, "Octree can only be created in 3D");
0545 
0546   using vertex_array_type = typename box_t::vertex_array_type;
0547 
0548   vertex_array_type envelope(vertex_array_type::Constant(envelope1));
0549 
0550   box_t* top = octree_inner(store, max_depth, envelope, prims, 0);
0551   return top;
0552 }
0553 
0554 template <typename T, typename U, std::size_t V>
0555 std::ostream& Acts::operator<<(
0556     std::ostream& os, const Acts::AxisAlignedBoundingBox<T, U, V>& box) {
0557   return box.toStream(os);
0558 }