Back to home page

EIC code displayed by LXR

 
 

    


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

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 <algorithm>
0012 #include <array>
0013 #include <cassert>
0014 #include <limits>
0015 #include <sstream>
0016 #include <string>
0017 
0018 namespace Acts {
0019 /// @brief An orthogonal range in an arbitrary number of dimensions
0020 ///
0021 /// By combining a number one-dimensional ranges we can (under the assumption
0022 /// that our axes are orthogonal) construct an orthogonal range of values. In
0023 /// other words, a hyperrectangular volume in space.
0024 ///
0025 /// @tparam Dims The number of dimensions in our range
0026 /// @tparam Type The scalar type of our ranges
0027 /// @tparam Vector The vector type used to define coordinates
0028 template <std::size_t Dims, typename Type,
0029           template <typename, std::size_t> typename Vector = std::array>
0030 class RangeXD {
0031  private:
0032   // @TODO: Replace with std::span or boost::span once available
0033   template <typename, std::size_t>
0034   struct SingleElementContainer {
0035     Type* element;
0036 
0037     Type& operator[](std::size_t i) {
0038       (void)i;
0039       assert(i == 0);
0040 
0041       return *element;
0042     }
0043   };
0044 
0045  public:
0046   RangeXD() {
0047     for (std::size_t i = 0; i < Dims; ++i) {
0048       min(i) = std::numeric_limits<Type>::lowest();
0049       max(i) = std::numeric_limits<Type>::max();
0050     }
0051   }
0052 
0053   /// @brief Construct a range from a pair of minimum and maximum values
0054   /// @param minima The minimum values of the range
0055   /// @param maxima The maximum values of the range
0056   RangeXD(Vector<Type, Dims> minima, Vector<Type, Dims> maxima)
0057       : m_minima(minima), m_maxima(maxima) {}
0058 
0059   /// @brief Construct a range from a pair of single minimum and maximum values
0060   /// @note Only available for one-dimensional ranges
0061   /// @param minimum The minimum value of the range
0062   /// @param maximum The maximum value of the range
0063   RangeXD(Type minimum, Type maximum)
0064     requires(Dims == 1)
0065       : m_minima({minimum}), m_maxima({maximum}) {}
0066 
0067   /// @brief Construct a range from a pair of minimum and maximum values
0068   /// @note Only available for one-dimensional ranges
0069   /// @param p The pair of minimum and maximum values
0070   RangeXD(const std::pair<Type, Type>& p)
0071     requires(Dims == 1)
0072       : m_minima({p.first}), m_maxima({p.second}) {}
0073 
0074   /// @brief Determine whether this range is degenerate
0075   ///
0076   /// A degenerate multi-dimensional range has no volume and cannot contain
0077   /// any values. This is the case if any of its dimensions are degenerate.
0078   ///
0079   /// @return true The range is degenerate
0080   /// @return false The range is not degenerate
0081   bool degenerate() const {
0082     for (std::size_t i = 0; i < Dims; ++i) {
0083       if (min(i) >= max(i)) {
0084         return true;
0085       }
0086     }
0087     return false;
0088   }
0089 
0090   /// @brief Determine whether the range contains a certain point
0091   ///
0092   /// This is true if and only if the range contains the point in all of its
0093   /// dimensions.
0094   ///
0095   /// @param v The coordinate to check for membership in the range
0096   ///
0097   /// @return true The coordinate is inside the range
0098   /// @return false The coordinate is outside the range
0099   template <template <typename, std::size_t> typename coordinate_t = std::array>
0100   bool contains(const coordinate_t<Type, Dims>& v) const {
0101     for (std::size_t i = 0; i < Dims; ++i) {
0102       if (!(min(i) <= v[i] && v[i] < max(i))) {
0103         return false;
0104       }
0105     }
0106 
0107     return true;
0108   }
0109 
0110   /// @brief Access one of the dimensional ranges of the volume
0111   ///
0112   /// @param i The index of the dimension to access
0113   /// @return A reference to the dimension contained in this range
0114   RangeXD<1, Type, SingleElementContainer> operator[](const std::size_t& i) {
0115     return RangeXD<1, Type, SingleElementContainer>{{&min(i)}, {&max(i)}};
0116   }
0117 
0118   /// @brief Access one of the dimensional ranges of the volume
0119   ///
0120   /// @param i The index of the dimension to access
0121   /// @return A reference to the dimension contained in this range
0122   RangeXD<1, Type> operator[](const std::size_t& i) const {
0123     return RangeXD<1, Type>{{min(i)}, {max(i)}};
0124   }
0125 
0126   /// @brief Assignment operator
0127   ///
0128   /// Copy the right-hand range into the left-hand range, which means setting
0129   /// the minimum and maximum to equal the minimum and maximum of the
0130   /// right-hand side.
0131   ///
0132   /// @param o The range of values to copy
0133   ///
0134   /// @return This range
0135   template <template <typename, std::size_t> typename V>
0136   RangeXD& operator=(const RangeXD<Dims, Type, V>& o) {
0137     for (std::size_t i = 0; i < Dims; ++i) {
0138       min(i) = o.min(i);
0139       max(i) = o.max(i);
0140     }
0141 
0142     return *this;
0143   }
0144 
0145   /// @brief Determine whether two ranges are equal
0146   ///
0147   /// Two n-dimensional ranges are equal if and only if they are equal in each
0148   /// of their n dimensions.
0149   ///
0150   /// @param o The other range to check for equality
0151   ///
0152   /// @return true The ranges are equal
0153   /// @return false The ranges are not equal
0154   bool operator==(const RangeXD<Dims, Type, Vector>& o) const {
0155     for (std::size_t i = 0; i < Dims; ++i) {
0156       if (!(min(i) == o.min(i) && max(i) == o.max(i))) {
0157         return false;
0158       }
0159     }
0160 
0161     return true;
0162   }
0163 
0164   /// @brief Determine whether one range is a subset of another range
0165   ///
0166   /// One range is a subset of another range if and only if all points
0167   /// contained within the first set are also contained within the second set.
0168   /// Alternatively, this is equivalent to each of the first range's
0169   /// one-dimensional ranges being a subset of the second range's equivalent
0170   /// one-dimensional range.
0171   ///
0172   /// @param o The other range to compare to
0173   ///
0174   /// @return true The first range is a subset of the second range
0175   /// @return false The first range is not a subset of the second range
0176   bool operator<=(const RangeXD<Dims, Type, Vector>& o) const {
0177     for (std::size_t i = 0; i < Dims; ++i) {
0178       if (!(min(i) >= o.min(i) && max(i) <= o.max(i))) {
0179         return false;
0180       }
0181     }
0182 
0183     return true;
0184   }
0185 
0186   /// @brief Determine whether one range is a superset of another range
0187   ///
0188   /// One range is a superset of another range if and only if all points
0189   /// contained within the second range are also contained within the first
0190   /// range. Alternatively, this is equivalent to each of the one-dimensional
0191   /// ranges in the first range being a superset of the corresponding
0192   /// one-dimensional range in the second range.
0193   ///
0194   /// @param o The other range to compare to
0195   ///
0196   /// @return true The left-hand range is a superset of the right-hand range
0197   /// @return false The left-hand range is not a superset of the right-hand
0198   /// range
0199   bool operator>=(const RangeXD<Dims, Type, Vector>& o) const {
0200     for (std::size_t i = 0; i < Dims; ++i) {
0201       if (!(min(i) <= o.min(i) && max(i) >= o.max(i))) {
0202         return false;
0203       }
0204     }
0205 
0206     return true;
0207   }
0208 
0209   /// @brief Compute the intersection of this range with another range
0210   ///
0211   /// The intersection of one orthogonal range with another orthogonal range
0212   /// is in itself an orthogonal range. This operation is commutative. This
0213   /// intersection between two n-dimensional ranges is defined simply as the
0214   /// intersection in each dimension of the two ranges.
0215   ///
0216   /// @param o The orthogonal range to compute the intersection with
0217   ///
0218   /// @return The intersection between the ranges
0219   RangeXD<Dims, Type, Vector> operator&(
0220       const RangeXD<Dims, Type, Vector>& o) const {
0221     RangeXD<Dims, Type> res;
0222 
0223     for (std::size_t i = 0; i < Dims; ++i) {
0224       res.min(i) = std::max(min(i), o.min(i));
0225       res.max(i) = std::min(max(i), o.max(i));
0226     }
0227 
0228     return res;
0229   }
0230 
0231   /// @brief Update the range to the intersection with another range
0232   ///
0233   /// This is the assignment version of the operator& method, meaning that it
0234   /// updates the object on which it is called rather than producing a new
0235   /// range.
0236   ///
0237   /// @param o The range to compute the intersection with
0238   ///
0239   /// @return This object
0240   RangeXD<Dims, Type, Vector>& operator&=(
0241       const RangeXD<Dims, Type, Vector>& o) {
0242     for (std::size_t i = 0; i < Dims; ++i) {
0243       min(i) = std::max(min(i), o.min(i));
0244       max(i) = std::min(max(i), o.max(i));
0245     }
0246 
0247     return *this;
0248   }
0249 
0250   /// @brief Determine whether this range intersects another
0251   ///
0252   /// Two n-dimensional ranges intersect if and only if they intersect in
0253   /// every one of their n dimensions. Otherwise, they are disjoint.
0254   ///
0255   /// @param r The other range to check
0256   ///
0257   /// @return true The ranges intersect
0258   /// @return false The ranges do not intersect
0259   bool operator&&(const RangeXD<Dims, Type, Vector>& r) const {
0260     for (std::size_t i = 0; i < Dims; ++i) {
0261       if (!(min(i) < r.max(i) && r.min(i) < max(i))) {
0262         return false;
0263       }
0264     }
0265 
0266     return true;
0267   }
0268 
0269   /// @brief Represent the range as a string
0270   ///
0271   /// This method produces a helpful string that can be used to debug the
0272   /// range if needed. Not really designed to be used in production code.
0273   ///
0274   /// @return A string representing the range
0275   std::string toString(void) const {
0276     std::stringstream s;
0277 
0278     for (std::size_t i = 0; i < Dims; ++i) {
0279       s << min(i) << " <= v[" << i << "] <= " << max(i);
0280       if (i != Dims - 1) {
0281         s << ", ";
0282       }
0283     }
0284 
0285     return s.str();
0286   }
0287 
0288   /// @brief Shrink a range by increasing the minimum value
0289   ///
0290   /// Shrink the range by increasing the minimum value. If the given value is
0291   /// smaller than the current minimum (in other words, if the proposed new
0292   /// range would be larger than the current range), this is a no-op.
0293   ///
0294   /// @param i The index of the dimension to shrink
0295   /// @param v The proposed new minimum for the range
0296   void shrinkMin(std::size_t i, const Type& v) { min(i) = std::max(min(i), v); }
0297 
0298   /// @brief Shrink a range by decreasing the maximum value
0299   ///
0300   /// Shrink the range by decreasing the maximum value. If the given value is
0301   /// larger than the current maximum (in other words, if the proposed new
0302   /// range would be larger than the current range), this is a no-op.
0303   ///
0304   /// @param i The index of the dimension to shrink
0305   /// @param v The proposed new maximum for the range
0306   void shrinkMax(std::size_t i, const Type& v) { max(i) = std::min(max(i), v); }
0307 
0308   /// @brief Shrink a range on both ends
0309   ///
0310   /// Shrink a range by increasing the minimum value as well as decreasing the
0311   /// maximum value. If either of the values are already smaller or larger
0312   /// (respectively) than the proposed values, then that particular boundary
0313   /// of the interval is not shrunk.
0314   ///
0315   /// @note After this operation, the range is always equal to or smaller than
0316   /// [min, max].
0317   ///
0318   /// @param i The index of the dimension to shrink
0319   /// @param min The proposed new minimum for the range
0320   /// @param max The proposed new maximum for the range
0321   void shrink(std::size_t i, const Type& min, const Type& max) {
0322     shrinkMin(i, min);
0323     shrinkMax(i, max);
0324   }
0325 
0326   /// @brief Expand a range by decreasing the minimum value
0327   ///
0328   /// Expand the range by decreasing the minimum value. If the given value is
0329   /// larger than the current minimum (in other words, if the proposed new
0330   /// range would be smaller than the current range), this is a no-op.
0331   ///
0332   /// @param i The index of the dimension to expand
0333   /// @param v The proposed new minimum for the range
0334   void expandMin(std::size_t i, const Type& v) { min(i) = std::min(min(i), v); }
0335 
0336   /// @brief Expand a range by increasing the maximum value
0337   ///
0338   /// Expand the range by increasing the maximum value. If the given value is
0339   /// smaller than the current maximum (in other words, if the proposed new
0340   /// range would be smaller than the current range), this is a no-op.
0341   ///
0342   /// @param i The index of the dimension to expand
0343   /// @param v The proposed new maximum for the range
0344   void expandMax(std::size_t i, const Type& v) { max(i) = std::max(max(i), v); }
0345 
0346   /// @brief Expand a range on both ends
0347   ///
0348   /// Expand a range by decreasing the minimum value as well as increasing the
0349   /// maximum value. If either of the values are already larger or smaller
0350   /// (respectively) than the proposed values, then that particular boundary
0351   /// of the interval is not expanded.
0352   ///
0353   /// @note After this operation, the range is always equal to or larger than
0354   /// [min, max].
0355   ///
0356   /// @param i The index of the dimension to expand
0357   /// @param min The proposed new minimum for the range
0358   /// @param max The proposed new maximum for the range
0359   void expand(std::size_t i, const Type& min, const Type& max) {
0360     expandMin(i, min);
0361     expandMax(i, max);
0362   }
0363 
0364   /// @brief Set the minimum value
0365   ///
0366   /// Override the minimum value of the range, regardless of what was already
0367   /// set.
0368   ///
0369   /// @note If you want to shrink or expand the range, use the shrink and
0370   /// expand methods.
0371   ///
0372   /// @param i The index of the dimension to set
0373   /// @param v The value to use as the new minimum
0374   void setMin(std::size_t i, const Type& v) { min(i) = v; }
0375 
0376   /// @brief Set the maximum value
0377   ///
0378   /// Override the maximum value of the range, regardless of what was already
0379   /// set.
0380   ///
0381   /// @note If you want to shrink or expand the range, use the shrink and
0382   /// expand methods.
0383   ///
0384   /// @param i The index of the dimension to set
0385   /// @param v The value to use as the new maximum
0386   void setMax(std::size_t i, const Type& v) { max(i) = v; }
0387 
0388   /// @brief Set the minimum and maximum value
0389   ///
0390   /// Override both the minimum and maximum value of the range, regardless of
0391   /// what they were set to.
0392   ///
0393   /// @note If you want to shrink or expand the range, use the shrink and
0394   /// expand methods.
0395   ///
0396   /// @note After this operation, the range should be exactly equal to [min,
0397   /// max]
0398   ///
0399   /// @param i The index of the dimension to set
0400   /// @param min The new minimum value of the range
0401   /// @param max The new maximum value of the range
0402   void set(std::size_t i, const Type& min, const Type& max) {
0403     setMin(i, min);
0404     setMax(i, max);
0405   }
0406 
0407   /// @brief Return the minimum value of the range @p i (inclusive)
0408   /// @param i The index of the dimension to access
0409   Type& min(std::size_t i) { return m_minima[i]; }
0410 
0411   /// @brief Return the maximum value of the range @p i (inclusive)
0412   /// @param i The index of the dimension to access
0413   Type& max(std::size_t i) { return m_maxima[i]; }
0414 
0415   /// @brief Return the minimum value of the range @p i (inclusive)
0416   /// @param i The index of the dimension to access
0417   Type min(std::size_t i) const { return m_minima[i]; }
0418 
0419   /// @brief Return the maximum value of the range @p i (inclusive)
0420   /// @param i The index of the dimension to access
0421   Type max(std::size_t i) const { return m_maxima[i]; }
0422 
0423   /// Methods for manipulating a range of dimension 1
0424   /// @{
0425 
0426   /// @brief Expand a range by decreasing the minimum value
0427   ///
0428   /// Expand the range by decreasing the minimum value. If the given value is
0429   /// larger than the current minimum (in other words, if the proposed new
0430   /// range would be smaller than the current range), this is a no-op.
0431   ///
0432   /// @param v The proposed new minimum for the range
0433   void expandMin(const Type& v)
0434     requires(Dims == 1)
0435   {
0436     min() = std::min(min(), v);
0437   }
0438 
0439   /// @brief Expand a range by increasing the maximum value
0440   ///
0441   /// Expand the range by increasing the maximum value. If the given value is
0442   /// smaller than the current maximum (in other words, if the proposed new
0443   /// range would be smaller than the current range), this is a no-op.
0444   ///
0445   /// @param v The proposed new maximum for the range
0446   void expandMax(const Type& v)
0447     requires(Dims == 1)
0448   {
0449     max() = std::max(max(), v);
0450   }
0451 
0452   /// @brief Expand a range on both ends
0453   ///
0454   /// Expand a range by decreasing the minimum value as well as increasing the
0455   /// maximum value. If either of the values are already larger or smaller
0456   /// (respectively) than the proposed values, then that particular boundary
0457   /// of the interval is not expanded.
0458   ///
0459   /// @note After this operation, the range is always equal to or larger than
0460   /// [min, max].
0461   ///
0462   /// @param min The proposed new minimum for the range
0463   /// @param max The proposed new maximum for the range
0464   void expand(const Type& min, const Type& max)
0465     requires(Dims == 1)
0466   {
0467     expandMin(min);
0468     expandMax(max);
0469   }
0470 
0471   /// @brief Shrink a range by increasing the minimum value
0472   ///
0473   /// Shrink the range by increasing the minimum value. If the given value is
0474   /// smaller than the current minimum (in other words, if the proposed new
0475   /// range would be larger than the current range), this is a no-op.
0476   ///
0477   /// @param v The proposed new minimum for the range
0478   void shrinkMin(const Type& v)
0479     requires(Dims == 1)
0480   {
0481     min() = std::max(min(), v);
0482   }
0483 
0484   /// @brief Shrink a range by decreasing the maximum value
0485   ///
0486   /// Shrink the range by decreasing the maximum value. If the given value is
0487   /// larger than the current maximum (in other words, if the proposed new
0488   /// range would be larger than the current range), this is a no-op.
0489   ///
0490   /// @param v The proposed new maximum for the range
0491   void shrinkMax(const Type& v)
0492     requires(Dims == 1)
0493   {
0494     max() = std::min(max(), v);
0495   }
0496 
0497   /// @brief Shrink a range on both ends
0498   ///
0499   /// Shrink a range by increasing the minimum value as well as decreasing the
0500   /// maximum value. If either of the values are already smaller or larger
0501   /// (respectively) than the proposed values, then that particular boundary
0502   /// of the interval is not shrunk.
0503   ///
0504   /// @note After this operation, the range is always equal to or smaller than
0505   /// [min, max].
0506   ///
0507   /// @param min The proposed new minimum for the range
0508   /// @param max The proposed new maximum for the range
0509   void shrink(const Type& min, const Type& max)
0510     requires(Dims == 1)
0511   {
0512     shrinkMin(min);
0513     shrinkMax(max);
0514   }
0515 
0516   /// @brief Set the minimum value
0517   ///
0518   /// Override the minimum value of the range, regardless of what was already
0519   /// set.
0520   ///
0521   /// @note If you want to shrink or expand the range, use the shrink and
0522   /// expand methods.
0523   ///
0524   /// @param v The value to use as the new minimum
0525   void setMin(const Type& v)
0526     requires(Dims == 1)
0527   {
0528     min() = v;
0529   }
0530 
0531   /// @brief Set the maximum value
0532   ///
0533   /// Override the maximum value of the range, regardless of what was already
0534   /// set.
0535   ///
0536   /// @note If you want to shrink or expand the range, use the shrink and
0537   /// expand methods.
0538   ///
0539   /// @param v The value to use as the new maximum
0540   void setMax(const Type& v)
0541     requires(Dims == 1)
0542   {
0543     max() = v;
0544   }
0545 
0546   /// @brief Set the minimum and maximum value
0547   ///
0548   /// Override both the minimum and maximum value of the range, regardless of
0549   /// what they were set to.
0550   ///
0551   /// @note If you want to shrink or expand the range, use the shrink and
0552   /// expand methods.
0553   ///
0554   /// @note After this operation, the range should be exactly equal to [min,
0555   /// max]
0556   ///
0557   /// @param min The new minimum value of the range
0558   /// @param max The new maximum value of the range
0559   void set(const Type& min, const Type& max)
0560     requires(Dims == 1)
0561   {
0562     setMin(min);
0563     setMax(max);
0564   }
0565 
0566   /// @brief Return the minimum value of the range (inclusive)
0567   Type min() const
0568     requires(Dims == 1)
0569   {
0570     return min(0);
0571   }
0572 
0573   /// @brief Return the minimum value of the range (inclusive)
0574   Type& min()
0575     requires(Dims == 1)
0576   {
0577     return min(0);
0578   }
0579 
0580   /// @brief Return the maximum value of the range (inclusive)
0581   Type max() const
0582     requires(Dims == 1)
0583   {
0584     return max(0);
0585   }
0586 
0587   /// @brief Return the maximum value of the range (inclusive)
0588   Type& max()
0589     requires(Dims == 1)
0590   {
0591     return max(0);
0592   }
0593 
0594   /// @brief Compute the size of the range
0595   ///
0596   /// The size of a range is defined as the difference between the minimum and
0597   /// the maximum. For degenerate ranges, this is zero.
0598   ///
0599   /// @warning Due to the nature of numbers, the result of this function can be
0600   /// somewhat ambiguous. For natural numbers, you could argue that the range
0601   /// [n, n] has size 0 or size 1. In this case we say it has size 0. The
0602   /// uncountable nature of the reals means this doesn't matter for them, but
0603   /// this can be awkward when working with integers.
0604   ///
0605   /// @return The size of the range
0606   Type size() const
0607     requires(Dims == 1)
0608   {
0609     return std::max(static_cast<Type>(0), max() - min());
0610   }
0611 
0612   /// @brief Determine if the range contains a given value
0613   ///
0614   /// A value is inside a range if and only if it is greater than the minimum
0615   /// and smaller than the maximum.
0616   ///
0617   /// @param v The value to check
0618   ///
0619   /// @return true The value is inside the range
0620   /// @return false The value is not inside the range
0621   bool contains(const Type& v) const
0622     requires(Dims == 1)
0623   {
0624     return min() <= v && v < max();
0625   }
0626 
0627   /// @}
0628 
0629  private:
0630   Vector<Type, Dims> m_minima{};
0631   Vector<Type, Dims> m_maxima{};
0632 };
0633 
0634 template <typename Type,
0635           template <typename, std::size_t> typename Vector = std::array>
0636 using Range1D = RangeXD<1, Type, Vector>;
0637 
0638 }  // namespace Acts