Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 09:07:51

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file orange/BoundingBoxUtils.hh
0006 //! \brief Utilities for bounding boxes
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include <cmath>
0011 #include <iosfwd>
0012 
0013 #include "corecel/Assert.hh"
0014 #include "corecel/cont/Range.hh"
0015 #include "corecel/math/Algorithms.hh"
0016 #include "geocel/BoundingBox.hh"
0017 
0018 #include "OrangeTypes.hh"
0019 
0020 namespace celeritas
0021 {
0022 //---------------------------------------------------------------------------//
0023 class Translation;
0024 class Transformation;
0025 
0026 //---------------------------------------------------------------------------//
0027 /*!
0028  * Check if a bounding box spans (-inf, inf) in every direction.
0029  */
0030 template<class T>
0031 inline bool is_infinite(BoundingBox<T> const& bbox)
0032 {
0033     auto all_equal = [](Array<T, 3> const& values, T rhs) {
0034         return all_of(
0035             values.begin(), values.end(), [rhs](T lhs) { return lhs == rhs; });
0036     };
0037     constexpr T inf = numeric_limits<T>::infinity();
0038 
0039     return all_equal(bbox.lower(), -inf) && all_equal(bbox.upper(), inf);
0040 }
0041 
0042 //---------------------------------------------------------------------------//
0043 /*!
0044  * Check if a bounding box has no infinities.
0045  *
0046  * \pre The bounding box cannot be null
0047  */
0048 template<class T>
0049 inline bool is_finite(BoundingBox<T> const& bbox)
0050 {
0051     CELER_EXPECT(bbox);
0052 
0053     auto isinf = [](T value) { return std::isinf(value); };
0054     return !any_of(bbox.lower().begin(), bbox.lower().end(), isinf)
0055            && !any_of(bbox.upper().begin(), bbox.upper().end(), isinf);
0056 }
0057 
0058 //---------------------------------------------------------------------------//
0059 /*!
0060  * Check if a bounding box has zero length in any direction.
0061  *
0062  * \pre The bounding box cannot be null
0063  */
0064 template<class T>
0065 inline bool is_degenerate(BoundingBox<T> const& bbox)
0066 {
0067     CELER_EXPECT(bbox);
0068 
0069     auto axes = range(to_int(Axis::size_));
0070     return any_of(axes.begin(), axes.end(), [&bbox](int ax) {
0071         return bbox.lower()[ax] == bbox.upper()[ax];
0072     });
0073 }
0074 
0075 //---------------------------------------------------------------------------//
0076 /*!
0077  * Whether any axis has an infinity on one bound but not the other.
0078  */
0079 template<class T>
0080 inline bool is_half_inf(BoundingBox<T> const& bbox)
0081 {
0082     auto axes = range(to_int(Axis::size_));
0083     return any_of(axes.begin(), axes.end(), [&bbox](int ax) {
0084         return std::isinf(bbox.lower()[ax]) != std::isinf(bbox.upper()[ax]);
0085     });
0086 }
0087 
0088 //---------------------------------------------------------------------------//
0089 /*!
0090  * Calculate the center of a bounding box.
0091  *
0092  * \pre The bounding box cannot be null, or "semi-infinite" (i.e., it may not
0093  * have a finite lower/upper value in a particular dimension, with a
0094  * corresponding infinite upper/lower value).
0095  */
0096 template<class T>
0097 inline Array<T, 3> calc_center(BoundingBox<T> const& bbox)
0098 {
0099     CELER_EXPECT(bbox);
0100     CELER_EXPECT(!is_half_inf(bbox));
0101 
0102     Array<T, 3> center;
0103     for (auto ax : range(to_int(Axis::size_)))
0104     {
0105         center[ax] = (bbox.lower()[ax] + bbox.upper()[ax]) / 2;
0106         if (CELER_UNLIKELY(std::isnan(center[ax])))
0107         {
0108             // Infinite or half-infinite
0109             center[ax] = 0;
0110         }
0111     }
0112 
0113     return center;
0114 }
0115 
0116 //---------------------------------------------------------------------------//
0117 /*!
0118  * Calculate the half widths of the bounding box.
0119  *
0120  * \pre The bounding box cannot be null
0121  */
0122 template<class T>
0123 inline Array<T, 3> calc_half_widths(BoundingBox<T> const& bbox)
0124 {
0125     CELER_EXPECT(bbox);
0126 
0127     Array<T, 3> hw;
0128     for (auto ax : range(to_int(Axis::size_)))
0129     {
0130         hw[ax] = (bbox.upper()[ax] - bbox.lower()[ax]) / 2;
0131     }
0132 
0133     return hw;
0134 }
0135 
0136 //---------------------------------------------------------------------------//
0137 /*!
0138  * Calculate the surface area of a bounding box.
0139  *
0140  * \pre The bounding box cannot be null
0141  */
0142 template<class T>
0143 inline T calc_surface_area(BoundingBox<T> const& bbox)
0144 {
0145     CELER_EXPECT(bbox);
0146 
0147     Array<T, 3> lengths;
0148 
0149     for (auto ax : range(to_int(Axis::size_)))
0150     {
0151         lengths[ax] = bbox.upper()[ax] - bbox.lower()[ax];
0152     }
0153 
0154     return 2
0155            * (lengths[to_int(Axis::x)] * lengths[to_int(Axis::y)]
0156               + lengths[to_int(Axis::x)] * lengths[to_int(Axis::z)]
0157               + lengths[to_int(Axis::y)] * lengths[to_int(Axis::z)]);
0158 }
0159 
0160 //---------------------------------------------------------------------------//
0161 /*!
0162  * Calculate the volume of a bounding box.
0163  *
0164  * \pre The bounding box cannot be null
0165  */
0166 template<class T>
0167 inline T calc_volume(BoundingBox<T> const& bbox)
0168 {
0169     CELER_EXPECT(bbox);
0170 
0171     T result{1};
0172 
0173     for (auto ax : range(to_int(Axis::size_)))
0174     {
0175         result *= bbox.upper()[ax] - bbox.lower()[ax];
0176     }
0177 
0178     return result;
0179 }
0180 
0181 //---------------------------------------------------------------------------//
0182 /*!
0183  * Calculate the smallest bounding box enclosing two bounding boxes.
0184  */
0185 template<class T>
0186 inline constexpr BoundingBox<T>
0187 calc_union(BoundingBox<T> const& a, BoundingBox<T> const& b)
0188 {
0189     Array<T, 3> lower{};
0190     Array<T, 3> upper{};
0191 
0192     for (auto ax : range(to_int(Axis::size_)))
0193     {
0194         lower[ax] = celeritas::min(a.lower()[ax], b.lower()[ax]);
0195         upper[ax] = celeritas::max(a.upper()[ax], b.upper()[ax]);
0196     }
0197 
0198     return BoundingBox<T>::from_unchecked(lower, upper);
0199 }
0200 
0201 //---------------------------------------------------------------------------//
0202 /*!
0203  * Calculate the intersection of two bounding boxes.
0204  *
0205  * If there is no intersection, the result will be a null bounding box.
0206  */
0207 template<class T>
0208 inline constexpr BoundingBox<T>
0209 calc_intersection(BoundingBox<T> const& a, BoundingBox<T> const& b)
0210 {
0211     Array<T, 3> lower{};
0212     Array<T, 3> upper{};
0213 
0214     for (auto ax : range(to_int(Axis::size_)))
0215     {
0216         lower[ax] = celeritas::max(a.lower()[ax], b.lower()[ax]);
0217         upper[ax] = celeritas::min(a.upper()[ax], b.upper()[ax]);
0218     }
0219 
0220     return BoundingBox<T>::from_unchecked(lower, upper);
0221 }
0222 
0223 //---------------------------------------------------------------------------//
0224 /*!
0225  * Check if all points inside the small bbox are in the big bbox.
0226  *
0227  * All bounding boxes should enclose a "null" bounding box (there are no points
0228  * in the null box, so no points are outside the big box). The null bounding
0229  * box will enclose no real bounding boxes. Comparing two null bounding boxes
0230  * is unspecified (forbidden for now).
0231  */
0232 template<class T>
0233 inline bool encloses(BoundingBox<T> const& big, BoundingBox<T> const& small)
0234 {
0235     CELER_EXPECT(big || small);
0236 
0237     auto axes = range(to_int(Axis::size_));
0238     return all_of(axes.begin(), axes.end(), [&big, &small](int ax) {
0239         return big.lower()[ax] <= small.lower()[ax]
0240                && big.upper()[ax] >= small.upper()[ax];
0241     });
0242 }
0243 
0244 //---------------------------------------------------------------------------//
0245 /*!
0246  * Calculate the distance to the inside of the bbox from a pos and dir.
0247  *
0248  * The supplied position is expected to be outside of the bbox. If there is no
0249  * intersection, the result will be inf.
0250  */
0251 template<class T, class U>
0252 inline CELER_FUNCTION T calc_dist_to_inside(BoundingBox<T> const& bbox,
0253                                             Array<U, 3> const& pos,
0254                                             Array<U, 3> const& dir)
0255 {
0256     CELER_EXPECT(!is_inside(bbox, pos));
0257 
0258     // Test if an intersection is outside the bbox for a given axis
0259     auto out_of_bounds = [&bbox](T intersect, int ax) {
0260         return !(intersect >= bbox.lower()[ax]
0261                  && intersect <= bbox.upper()[ax]);
0262     };
0263 
0264     // Check that the intersection point occurs within the region
0265     // bounded by the planes of the other two axes
0266     auto in_bounds = [&](int ax, T dist) {
0267         for (auto other_ax : range(to_int(Axis::size_)))
0268         {
0269             if (other_ax == ax)
0270                 continue;
0271 
0272             auto intersect
0273                 = celeritas::fma<T>(dist, dir[other_ax], pos[other_ax]);
0274             if (out_of_bounds(intersect, other_ax))
0275                 return false;
0276         }
0277         return true;
0278     };
0279 
0280     // Loop over all 6 planes to find the minimum intersection
0281     T min_dist = numeric_limits<T>::infinity();
0282     for (auto bound : range(Bound::size_))
0283     {
0284         for (auto ax : range(to_int(Axis::size_)))
0285         {
0286             if (dir[ax] == 0)
0287             {
0288                 // Short circut if there is not movement in this dir
0289                 continue;
0290             }
0291 
0292             T dist = (bbox.point(static_cast<Bound>(bound))[ax] - pos[ax])
0293                      / dir[ax];
0294             if (dist <= 0)
0295             {
0296                 // Short circut if the plane is behind us
0297                 continue;
0298             }
0299 
0300             if (in_bounds(ax, dist))
0301             {
0302                 min_dist = celeritas::min(min_dist, dist);
0303             }
0304         }
0305     }
0306 
0307     return min_dist;
0308 }
0309 
0310 //---------------------------------------------------------------------------//
0311 /*!
0312  * Bump a bounding box outward and possibly convert to another type.
0313  * \tparam T destination type
0314  * \tparam U source type
0315  *
0316  * The upper and lower coordinates are bumped outward independently using the
0317  * relative and absolute tolerances. To ensure that the outward bump is
0318  * not truncated in the destination type, the "std::nextafter" function
0319  * advances to the next floating point representable number.
0320  */
0321 template<class T, class U = T>
0322 class BoundingBoxBumper
0323 {
0324   public:
0325     //!@{
0326     //! \name Type aliases
0327     using TolU = Tolerance<U>;
0328     using result_type = BoundingBox<T>;
0329     using argument_type = BoundingBox<U>;
0330     //!@}
0331 
0332   public:
0333     //! Construct with default "soft equal" tolerances
0334     BoundingBoxBumper() : tol_{TolU::from_softequal()} {}
0335 
0336     //! Construct with ORANGE tolerances
0337     explicit BoundingBoxBumper(TolU const& tol) : tol_{tol}
0338     {
0339         CELER_EXPECT(tol_);
0340     }
0341 
0342     //! Return the expanded and converted bounding box
0343     result_type operator()(argument_type const& bbox)
0344     {
0345         Array<T, 3> lower;
0346         Array<T, 3> upper;
0347 
0348         for (auto ax : range(to_int(Axis::size_)))
0349         {
0350             lower[ax] = this->bumped<-1>(bbox.lower()[ax]);
0351             upper[ax] = this->bumped<+1>(bbox.upper()[ax]);
0352         }
0353 
0354         return result_type::from_unchecked(lower, upper);
0355     }
0356 
0357   private:
0358     TolU tol_;
0359 
0360     //! Calculate the bump distance given a point: see detail::BumpCalculator
0361     template<int S>
0362     T bumped(U value) const
0363     {
0364         U bumped = value
0365                    + S * celeritas::max(tol_.abs, tol_.rel * std::fabs(value));
0366         return std::nextafter(static_cast<T>(bumped),
0367                               S * numeric_limits<T>::infinity());
0368     }
0369 };
0370 
0371 //---------------------------------------------------------------------------//
0372 // Calculate the bounding box of a transformed box
0373 BBox calc_transform(Translation const& tr, BBox const& a);
0374 
0375 BBox calc_transform(Transformation const& tr, BBox const& a);
0376 
0377 //---------------------------------------------------------------------------//
0378 template<class T>
0379 std::ostream& operator<<(std::ostream&, BoundingBox<T> const& bbox);
0380 
0381 //---------------------------------------------------------------------------//
0382 }  // namespace celeritas