1 /***************************************************************************
2 * Mechanized Assault and Exploration Reloaded Projectfile *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18 ***************************************************************************/
19
20 #ifndef utility_boxH
21 #define utility_boxH
22
23 #include <algorithm>
24 #include <cassert>
25
26 #include <SDL.h>
27
28 /**
29 * An axis aligned bounding box (AABB).
30 *
31 * @tparam PointType The point type to be used in the box.
32 */
33 template<typename PointType>
34 class cBox
35 {
36 public:
37 cBox();
38 cBox (const PointType& minCorner, const PointType& maxCorner);
39
40 PointType& getMinCorner();
41 PointType& getMaxCorner();
42
43 const PointType& getMinCorner() const;
44 const PointType& getMaxCorner() const;
45
46 PointType getSize() const;
47 void resize (const PointType& newSize);
48
49 void add (const PointType& point);
50 void add (const cBox<PointType>& box);
51
52 /**
53 * Checks whether the point lies within the box.
54 *
55 * @param point The point to check against.
56 * @return True if the point is in the box.
57 * False if it is outside or on the boundary of the box.
58 */
59 bool within (const PointType& point) const;
60
61 /**
62 * Checks whether the point lies within the box or on its boundary.
63 *
64 * @param point The point to check against.
65 * @return True if the point is on the box or on the boundary of the box.
66 * False if it is outside of the box.
67 */
68 bool withinOrTouches (const PointType& point) const;
69
70 /**
71 * Checks whether the box intersects the other one.
72 *
73 * @param other The second box to check against.
74 * @return True if the other box intersects the current one.
75 * False if the other box lies completely outside the current box
76 * or if the two boxes only touch at their boundaries.
77 */
78 bool intersects (const cBox<PointType>& other) const;
79
80 cBox<PointType> intersection (const cBox<PointType>& other) const;
81
82 SDL_Rect toSdlRect() const;
83
84 void fromSdlRect (const SDL_Rect& rect);
85 private:
86 PointType minCorner;
87 PointType maxCorner;
88 };
89
90 //------------------------------------------------------------------------------
91 template<typename PointType>
cBox()92 cBox<PointType>::cBox()
93 {}
94
95 //------------------------------------------------------------------------------
96 template<typename PointType>
cBox(const PointType & minCorner_,const PointType & maxCorner_)97 cBox<PointType>::cBox (const PointType& minCorner_, const PointType& maxCorner_) :
98 minCorner (minCorner_),
99 maxCorner (maxCorner_)
100 {}
101
102 //------------------------------------------------------------------------------
103 template<typename PointType>
getMinCorner()104 PointType& cBox<PointType>::getMinCorner()
105 {
106 return minCorner;
107 }
108
109 //------------------------------------------------------------------------------
110 template<typename PointType>
getMaxCorner()111 PointType& cBox<PointType>::getMaxCorner()
112 {
113 return maxCorner;
114 }
115
116 //------------------------------------------------------------------------------
117 template<typename PointType>
getMinCorner()118 const PointType& cBox<PointType>::getMinCorner() const
119 {
120 return minCorner;
121 }
122
123 //------------------------------------------------------------------------------
124 template<typename PointType>
getMaxCorner()125 const PointType& cBox<PointType>::getMaxCorner() const
126 {
127 return maxCorner;
128 }
129
130 //------------------------------------------------------------------------------
131 template<typename PointType>
getSize()132 PointType cBox<PointType>::getSize() const
133 {
134 auto diff = maxCorner - minCorner;
135 if (std::is_integral<typename PointType::value_type>::value)
136 {
137 diff += 1;
138 }
139 return diff;
140 }
141
142 //------------------------------------------------------------------------------
143 template<typename PointType>
resize(const PointType & newSize)144 void cBox<PointType>::resize (const PointType& newSize)
145 {
146 maxCorner = minCorner + newSize;
147 if (std::is_integral<typename PointType::value_type>::value)
148 {
149 if (std::is_unsigned<typename PointType::value_type>::value) for (size_t d = 0; d < PointType::const_size::value; ++d) assert (newSize[d] != 0);
150 maxCorner -= 1;
151 }
152 }
153
154 //------------------------------------------------------------------------------
155 template<typename PointType>
add(const PointType & point)156 void cBox<PointType>::add (const PointType& point)
157 {
158 for (size_t d = 0; d < point.size(); ++d)
159 {
160 minCorner[d] = std::min (minCorner[d], point[d]);
161 maxCorner[d] = std::max (maxCorner[d], point[d]);
162 }
163 }
164
165 //------------------------------------------------------------------------------
166 template<typename PointType>
add(const cBox<PointType> & box)167 void cBox<PointType>::add (const cBox<PointType>& box)
168 {
169 for (size_t d = 0; d < PointType::const_size::value; ++d)
170 {
171 minCorner[d] = std::min (minCorner[d], box.minCorner[d]);
172 maxCorner[d] = std::max (maxCorner[d], box.maxCorner[d]);
173 }
174 }
175
176 //------------------------------------------------------------------------------
177 template<typename PointType>
within(const PointType & point)178 bool cBox<PointType>::within (const PointType& point) const
179 {
180 for (size_t d = 0; d < point.size(); ++d)
181 {
182 if (point[d] <= minCorner[d] || point[d] >= maxCorner[d]) return false;
183 }
184 return true;
185 }
186
187 //------------------------------------------------------------------------------
188 template<typename PointType>
withinOrTouches(const PointType & point)189 bool cBox<PointType>::withinOrTouches (const PointType& point) const
190 {
191 for (size_t d = 0; d < point.size(); ++d)
192 {
193 if (point[d] < minCorner[d] || point[d] > maxCorner[d]) return false;
194 }
195 return true;
196 }
197
198 //------------------------------------------------------------------------------
199 template<typename PointType>
intersects(const cBox<PointType> & other)200 bool cBox<PointType>::intersects (const cBox<PointType>& other) const
201 {
202 for (size_t d = 0; d < PointType::const_size::value; ++d)
203 {
204 if (std::min (maxCorner[d], other.getMaxCorner()[d]) < std::max (minCorner[d], other.getMinCorner()[d])) return false;
205 }
206 return true;
207 }
208
209 //------------------------------------------------------------------------------
210 template<typename PointType>
intersection(const cBox<PointType> & other)211 cBox<PointType> cBox<PointType>::intersection (const cBox<PointType>& other) const
212 {
213 assert (intersects (other));
214
215 cBox<PointType> result;
216 for (size_t d = 0; d < PointType::const_size::value; ++d)
217 {
218 result.getMinCorner()[d] = std::max (minCorner[d], other.getMinCorner()[d]);
219 result.getMaxCorner()[d] = std::min (maxCorner[d], other.getMaxCorner()[d]);
220 }
221 return result;
222 }
223
224 //------------------------------------------------------------------------------
225 template<typename PointType>
toSdlRect()226 SDL_Rect cBox<PointType>::toSdlRect() const
227 {
228 static_assert (PointType::const_size::value == 2, "Converting to SDL_Rect not support in dimension other than 2.");
229 static_assert (std::is_same<typename PointType::value_type, int>::value, "Converting to SDL_Rect not support if point scalar value is other than int."); // NOTE: we may could allow all non-narrowing casts here (e.g. short to int).
230
231 const auto diff = getSize();
232
233 SDL_Rect result = {minCorner[0], minCorner[1], diff[0], diff[1]};
234 return result;
235 }
236
237 //------------------------------------------------------------------------------
238 template<typename PointType>
fromSdlRect(const SDL_Rect & rect)239 void cBox<PointType>::fromSdlRect (const SDL_Rect& rect)
240 {
241 minCorner[0] = rect.x;
242 minCorner[1] = rect.y;
243
244 maxCorner[0] = rect.x + rect.w - 1;
245 maxCorner[1] = rect.y + rect.h - 1;
246 }
247
248 #endif // utility_boxH
249