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