1 #pragma once 2 3 /// \file Box.h 4 /// \brief Object representing a three-dimensional axis-aligned box 5 /// \author Pavel Sevecek (sevecek at sirrah.troja.mff.cuni.cz) 6 /// \date 2016-2021 7 8 #include "objects/containers/Array.h" 9 #include "objects/geometry/Indices.h" 10 #include "objects/geometry/Vector.h" 11 12 NAMESPACE_SPH_BEGIN 13 14 /// \brief Helper object defining three-dimensional interval (box). 15 /// 16 /// Degenerated box (one or more dimensions equal to zero) is a valid state of the object. 17 class Box { 18 private: 19 Vector minBound = Vector(LARGE); 20 Vector maxBound = Vector(-LARGE); 21 22 public: 23 /// \brief Constructs empty box with negative dimensions. 24 /// 25 /// The box in this state will throw assert if member function are called. Function \ref extend is an 26 /// exception, it will simply move both lower and upper bound of the box to the position of new point, 27 /// resulting in box of zero dimensions. Another exception is method contains, that simply returns false 28 /// for all points. 29 INLINE Box() = default; 30 31 /// \brief Constructs a box given its 'corners'. 32 /// 33 /// Components of minBound must be lower or equal to components of maxBound, checked by assert. Box(const Vector & minBound,const Vector & maxBound)34 INLINE Box(const Vector& minBound, const Vector& maxBound) 35 : minBound(minBound) 36 , maxBound(maxBound) { 37 SPH_ASSERT(isValid()); 38 } 39 40 /// \brief Syntactic sugar, returns a default-constructed (empty) box EMPTY()41 static Box EMPTY() { 42 return Box(); 43 } 44 45 /// \brief Enlarges the box to contain the vector. 46 /// 47 /// If the box already contains given vectors, it is left unchanged. If the box was previously empty, it 48 /// now contains the given point extend(const Vector & v)49 INLINE void extend(const Vector& v) { 50 maxBound = max(maxBound, v); 51 minBound = min(minBound, v); 52 } 53 54 /// \brief Enlarges the box to contain another box. 55 /// 56 /// The other box can be invalid, this box is then unaffected, no assert is issued. If an empty (invalid) 57 /// box is extended with other empty box, it is still empty. extend(const Box & other)58 INLINE void extend(const Box& other) { 59 maxBound = max(maxBound, other.maxBound); 60 minBound = min(minBound, other.minBound); 61 } 62 63 /// \brief Checks if the vector lies inside the box. 64 /// 65 /// If the vector lies on the boundary, it is assumed to within the box and the function returns true. contains(const Vector & v)66 INLINE bool contains(const Vector& v) const { 67 for (int i = 0; i < 3; ++i) { 68 if (v[i] < minBound[i] || v[i] > maxBound[i]) { 69 return false; 70 } 71 } 72 return true; 73 } 74 75 /// \brief Clamps all components of the vector to fit within the box clamp(const Vector & v)76 INLINE Vector clamp(const Vector& v) const { 77 SPH_ASSERT(isValid()); 78 return Sph::clamp(v, minBound, maxBound); 79 } 80 81 /// \brief Returns lower bounds of the box. lower()82 INLINE const Vector& lower() const { 83 SPH_ASSERT(isValid()); 84 return minBound; 85 } 86 87 /// \brief Returns lower bounds of the box. lower()88 INLINE Vector& lower() { 89 SPH_ASSERT(isValid()); 90 return minBound; 91 } 92 93 /// \brief Returns upper bounds of the box upper()94 INLINE const Vector& upper() const { 95 SPH_ASSERT(isValid()); 96 return maxBound; 97 } 98 99 /// \brief Returns upper bounds of the box upper()100 INLINE Vector& upper() { 101 SPH_ASSERT(isValid()); 102 return maxBound; 103 } 104 105 /// \brief Returns box dimensions. size()106 INLINE Vector size() const { 107 SPH_ASSERT(isValid()); 108 return maxBound - minBound; 109 } 110 111 /// \brief Returns the center of the box. center()112 INLINE Vector center() const { 113 SPH_ASSERT(isValid()); 114 return 0.5_f * (minBound + maxBound); 115 } 116 117 /// \brief Returns the volume of the box. volume()118 INLINE Float volume() const { 119 const Vector s = size(); 120 return s[X] * s[Y] * s[Z]; 121 } 122 123 /// \brief Compares two boxes, return true if box lower and upper bounds are equal. 124 INLINE bool operator==(const Box& other) const { 125 return minBound == other.minBound && maxBound == other.maxBound; 126 } 127 128 /// \brief Checks for inequality of boxes. 129 INLINE bool operator!=(const Box& other) const { 130 return minBound != other.minBound || maxBound != other.maxBound; 131 } 132 133 /// \brief Returns a box with specified offset. translate(const Vector & offset)134 INLINE Box translate(const Vector& offset) const { 135 return Box(minBound + offset, maxBound + offset); 136 } 137 138 /// \brief Splits the box along given coordinate. 139 /// 140 /// The splitting plane must pass through the box. 141 /// \param dim Dimension, can be X, Y or Z. 142 /// \param x Coordinate in given dimension used for the split 143 /// \return Two boxes created by the split. split(const Size dim,const Float x)144 INLINE Pair<Box> split(const Size dim, const Float x) const { 145 SPH_ASSERT(isValid()); 146 SPH_ASSERT(dim < 3); 147 SPH_ASSERT(x >= minBound[dim] && x <= maxBound[dim]); 148 Box b1 = *this, b2 = *this; 149 b1.maxBound[dim] = x; 150 b2.minBound[dim] = x; 151 return { b1, b2 }; 152 } 153 154 /// \brief Computes the intersection of this box with another one. intersect(const Box & other)155 INLINE Box intersect(const Box& other) const { 156 Box is; 157 is.minBound = max(minBound, other.minBound); 158 is.maxBound = min(maxBound, other.maxBound); 159 if (is.isValid()) { 160 return is; 161 } else { 162 return Box::EMPTY(); 163 } 164 } 165 166 /// \brief Execute functor for all possible values of vector (with constant stepping) 167 template <typename TFunctor> iterate(const Vector & step,TFunctor && functor)168 void iterate(const Vector& step, TFunctor&& functor) const { 169 SPH_ASSERT(isValid()); 170 for (Float x = minBound[X]; x <= maxBound[X]; x += step[X]) { 171 for (Float y = minBound[Y]; y <= maxBound[Y]; y += step[Y]) { 172 for (Float z = minBound[Z]; z <= maxBound[Z]; z += step[Z]) { 173 functor(Vector(x, y, z)); 174 } 175 } 176 } 177 } 178 179 /// \brief Execute functor for all possible values of vector (with constant stepping), passing auxiliary 180 /// indices together with the vector 181 template <typename TFunctor> iterateWithIndices(const Vector & step,TFunctor && functor)182 void iterateWithIndices(const Vector& step, TFunctor&& functor) const { 183 SPH_ASSERT(isValid()); 184 Size i = 0, j = 0, k = 0; 185 for (Float z = minBound[Z]; z <= maxBound[Z]; z += step[Z], k++) { 186 i = 0; 187 j = 0; 188 for (Float y = minBound[Y]; y <= maxBound[Y]; y += step[Y], j++) { 189 i = 0; 190 for (Float x = minBound[X]; x <= maxBound[X]; x += step[X], i++) { 191 functor(Indices(i, j, k), Vector(x, y, z)); 192 } 193 } 194 } 195 } 196 197 /// \brief Prints the bounds of the box into the stream. 198 /// 199 /// The box can be empty, in which case EMPTY is written instead of the bounds. 200 template <typename TStream> 201 friend TStream& operator<<(TStream& stream, const Box& box) { 202 if (box == Box::EMPTY()) { 203 stream << "EMPTY"; 204 } else { 205 stream << box.lower() << box.upper(); 206 } 207 return stream; 208 } 209 210 private: isValid()211 bool isValid() const { 212 return minElement(maxBound - minBound) >= 0._f; 213 } 214 }; 215 216 217 NAMESPACE_SPH_END 218