vigra - vigra/box.hxx Source File

[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

box.hxx |

1 /************************************************************************/
2 /* */
3 /* Copyright 2009-2010 by Ullrich Koethe and Hans Meine */
4 /* */
5 /* This file is part of the VIGRA computer vision library. */
6 /* The VIGRA Website is */
7 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
8 /* Please direct questions, bug reports, and contributions to */
9 /* ullrich.koethe@iwr.uni-heidelberg.de or */
10 /* vigra@informatik.uni-hamburg.de */
11 /* */
12 /* Permission is hereby granted, free of charge, to any person */
13 /* obtaining a copy of this software and associated documentation */
14 /* files (the "Software"), to deal in the Software without */
15 /* restriction, including without limitation the rights to use, */
16 /* copy, modify, merge, publish, distribute, sublicense, and/or */
17 /* sell copies of the Software, and to permit persons to whom the */
18 /* Software is furnished to do so, subject to the following */
19 /* conditions: */
20 /* */
21 /* The above copyright notice and this permission notice shall be */
22 /* included in all copies or substantial portions of the */
23 /* Software. */
24 /* */
25 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32 /* OTHER DEALINGS IN THE SOFTWARE. */
33 /* */
34 /************************************************************************/
35
36 #ifndef VIGRA_BOX_HXX
37 #define VIGRA_BOX_HXX
38
39 #include "metaprogramming.hxx"
40 #include "numerictraits.hxx"
41 #include "tinyvector.hxx"
42
43 namespace vigra {
44
45 namespace detail {
46
47 // RangePolicy used for floating point coordinate types
48 template<class VALUETYPE>
49 struct EndInsidePolicy
50 {
51 static inline bool isEmptyRange(VALUETYPE b, VALUETYPE e)
52 {
53 return e < b; // <=
54 }
55
56 static inline VALUETYPE pointEnd(VALUETYPE p)
57 {
58 return p; // +1
59 }
60 };
61
62 // RangePolicy used for integer coordinate types
63 template<class VALUETYPE>
64 struct EndOutsidePolicy
65 {
66 static inline bool isEmptyRange(VALUETYPE b, VALUETYPE e)
67 {
68 return e <= b;
69 }
70
71 static inline VALUETYPE pointEnd(VALUETYPE p)
72 {
73 return p+1;
74 }
75 };
76
77 } // namespace vigra::detail
78
79 /** \addtogroup RangesAndPoints */
80 //@{
81 /** \brief Represent an n-dimensional box as a (begin, end) pair.
82 * Depending on the value type, end() is considered to be
83 * outside the box (as in the STL, for integer types), or
84 * inside (for floating point types). size() will always be
85 * end() - begin().
86 */
87 template<class VALUETYPE, unsigned int DIMENSION>
88 class Box
89 {
90 public:
91 /** STL-compatible definition of coordinate valuetype
92 */
93 typedef VALUETYPE value_type;
94
95 /** Promoted coordinate valuetype, used for volume()
96 */
97 typedef typename NumericTraits<VALUETYPE>::Promote VolumeType;
98
99 /** Vector type used for begin() and end()
100 */
101 typedef TinyVector<VALUETYPE, DIMENSION> Vector;
102
103 enum { Dimension = DIMENSION };
104
105 protected:
106 Vector begin_, end_;
107
108 /** Range policy (EndInsidePolicy/EndOutsidePolicy, depending on valuetype)
109 */
110 typedef typename If<typename NumericTraits<VALUETYPE>::isIntegral,
111 detail::EndOutsidePolicy<VALUETYPE>,
112 detail::EndInsidePolicy<VALUETYPE> >::type RangePolicy;
113
114 public:
115 /** Construct an empty box (isEmpty() will return true).
116 * (Internally, this will initialize all dimensions with the
117 * empty range [1..0].)
118 */
119 Box()
120 : begin_(NumericTraits<Vector>::one())
121 {}
122
123 /** Construct a box representing the given range. Depending
124 * on the value type, end() is considered to be outside the
125 * box (as in the STL, for integer types), or inside (for
126 * floating point types).
127 */
128 Box(Vector const &begin, Vector const &end)
129 : begin_(begin), end_(end)
130 {}
131
132 /** Construct a box of given size at the origin (i.e. end() ==
133 * size()).
134 */
135 explicit Box(Vector const &size)
136 : end_(size)
137 {}
138
139 /** Get begin vector (i.e. smallest coordinates for each
140 * dimension). This is the first point (scan-order wise)
141 * which is considered to be "in" the box.
142 */
143 Vector const & begin() const
144 {
145 return begin_;
146 }
147
148 /** Access begin vector (i.e. smallest coordinates for each
149 * dimension). This is the first point (scan-order wise)
150 * which is considered to be "in" the box.
151 */
152 Vector & begin()
153 {
154 return begin_;
155 }
156
157 /** Get end vector (i.e. coordinates higher than begin() in
158 * each dimension for non-empty boxes). This is begin() +
159 * size(), and depending on the valuetype (float/int), this is
160 * the last point within or the first point outside the box,
161 * respectively.
162 */
163 Vector const & end() const
164 {
165 return end_;
166 }
167
168 /** Access end vector (i.e. coordinates higher than begin() in
169 * each dimension for non-empty boxes). This is begin() +
170 * size(), and depending on the valuetype (float/int), this is
171 * the last point within or the first point outside the box,
172 * respectively.
173 */
174 Vector & end()
175 {
176 return end_;
177 }
178
179 /** Change begin() without changing end(), changing size()
180 * accordingly.
181 */
182 void setBegin(Vector const &begin)
183 {
184 begin_ = begin;
185 }
186
187 /** Change end() without changing begin(), which will change
188 * the size() most probably.
189 */
190 void setEnd(Vector const &end)
191 {
192 end_ = end;
193 }
194
195 /** Move the whole box so that the given point will be
196 * begin() afterwards.
197 */
198 void moveTo(Vector const &newBegin)
199 {
200 end_ += newBegin - begin_;
201 begin_ = newBegin;
202 }
203
204 /** Move the whole box by the given offset.
205 * (Equivalent to operator+=)
206 */
207 void moveBy(Vector const &offset)
208 {
209 begin_ += offset;
210 end_ += offset;
211 }
212
213 /** Determine and return the area of this box. That is,
214 * if this rect isEmpty(), returns zero, otherwise returns the
215 * product of the extents in each dimension.
216 */
217 VolumeType volume() const
218 {
219 if(isEmpty())
220 return 0;
221
222 VolumeType result(end_[0] - begin_[0]);
223 for(unsigned int i = 1; i < DIMENSION; ++i)
224 result *= end_[i] - begin_[i];
225 return result;
226 }
227
228 /** Determine and return the size of this box. The size
229 * might be zero or even negative in one or more dimensions,
230 * and if so, isEmpty() will return true.
231 */
232 Vector size() const
233 {
234 return end_ - begin_;
235 }
236
237 /** Resize this box to the given extents. This will
238 * change end() only.
239 */
240 void setSize(Vector const &size)
241 {
242 end_ = begin_ + size;
243 }
244
245 /** Increase the size of the box by the given
246 * offset. This will move end() only. (If any of offset's
247 * components is negative, the box will get smaller
248 * accordingly.)
249 */
250 void addSize(Vector const &offset)
251 {
252 end_ += offset;
253 }
254
255 /** Adds a border of the given width around the box. That
256 * means, begin()'s components are moved by -borderWidth
257 * and end()'s by borderWidth. (If borderWidth is
258 * negative, the box will get smaller accordingly.)
259 */
260 void addBorder(VALUETYPE borderWidth)
261 {
262 for(unsigned int i = 0; i < DIMENSION; ++i)
263 {
264 begin_[i] -= borderWidth;
265 end_[i] += borderWidth;
266 }
267 }
268
269 /// equality check
270 bool operator==(Box const &r) const
271 {
272 return (begin_ == r.begin_) && (end_ == r.end_);
273 }
274
275 /// inequality check
276 bool operator!=(Box const &r) const
277 {
278 return (begin_ != r.begin_) || (end_ != r.end_);
279 }
280
281 /** Return whether this box is considered empty. It is
282 * non-empty if all end() coordinates are greater than (or
283 * equal, for floating point valuetypes) the corresponding
284 * begin() coordinates. Uniting an empty box with something
285 * will return the bounding box of the 'something', and
286 * intersecting any box with an empty box will again yield an
287 * empty box.
288 */
289 bool isEmpty() const
290 {
291 for(unsigned int i = 0; i < DIMENSION; ++i)
292 if(RangePolicy::isEmptyRange(begin_[i], end_[i]))
293 return true;
294 return false;
295 }
296
297 /** Return whether this box contains the given point.
298 * That is, if the point lies within the range [begin, end] in
299 * each dimension (excluding end() itself for integer valuetypes).
300 */
301 bool contains(Vector const &p) const
302 {
303 for(unsigned int i = 0; i < DIMENSION; ++i)
304 if((p[i] < begin_[i]) ||
305 RangePolicy::isEmptyRange(p[i], end_[i]))
306 return false;
307 return true;
308 }
309
310 /** Return whether this box contains the given
311 * one. <tt>r1.contains(r2)</tt> returns the same as
312 * <tt>r1 == (r1|r2)</tt> (but is of course more
313 * efficient). That also means, a box (even an empty one!)
314 * contains() any empty box.
315 */
316 bool contains(Box const &r) const
317 {
318 if(r.isEmpty())
319 return true;
320 if(!contains(r.begin_))
321 return false;
322 for(unsigned int i = 0; i < DIMENSION; ++i)
323 if(r.end_[i] > end_[i])
324 return false;
325 return true;
326 }
327
328 /** Return whether this box overlaps with the given
329 * one. <tt>r1.intersects(r2)</tt> returns the same as
330 * <tt>!(r1&r2).isEmpty()</tt> (but is of course much more
331 * efficient).
332 */
333 bool intersects(Box const &r) const
334 {
335 if(r.isEmpty() || isEmpty())
336 return false;
337 for(unsigned int i = 0; i < DIMENSION; ++i)
338 if(RangePolicy::isEmptyRange(r.begin_[i], end_[i]) ||
339 RangePolicy::isEmptyRange(begin_[i], r.end_[i]))
340 return false;
341 return true;
342 }
343
344 /** Modifies this box by including the given point.
345 * The result will be the bounding box of the box and the
346 * point. If isEmpty() returns true on the original box, the
347 * union will be a box containing only the given point.
348 */
349 Box &operator|=(Vector const &p)
350 {
351 if(isEmpty())
352 {
353 begin_ = p;
354 for(unsigned int i = 0; i < DIMENSION; ++i)
355 end_[i] = RangePolicy::pointEnd(p[i]);
356 }
357 else
358 {
359 for(unsigned int i = 0; i < DIMENSION; ++i)
360 {
361 if(p[i] < begin_[i])
362 begin_[i] = p[i];
363 if(RangePolicy::isEmptyRange(p[i], end_[i]))
364 end_[i] = RangePolicy::pointEnd(p[i]);
365 }
366 }
367 return *this;
368 }
369
370 /** Returns the union of this box and the given point.
371 * The result will be the bounding box of the box and the
372 * point. If isEmpty() returns true on the original box, the
373 * union will be a box containing only the given point.
374 */
375 Box operator|(Vector const &p) const
376 {
377 Box result(*this);
378 result |= p;
379 return result;
380 }
381
382 /** Modifies this box by uniting it with the given one.
383 * The result will be the bounding box of both boxs. If one of
384 * the boxes isEmpty(), the union will be the other one.
385 */
386 Box &operator|=(Box const &r)
387 {
388 if(r.isEmpty())
389 return *this;
390 if(isEmpty())
391 return this->operator=(r);
392
393 for(unsigned int i = 0; i < DIMENSION; ++i)
394 {
395 if(r.begin_[i] < begin_[i])
396 begin_[i] = r.begin_[i];
397 if(end_[i] < r.end_[i])
398 end_[i] = r.end_[i];
399 }
400 return *this;
401 }
402
403 /** Returns the union of this box and the given one.
404 * The result will be the bounding box of both boxs. If one of
405 * the boxes isEmpty(), the union will be the other one.
406 */
407 Box operator|(Box const &r) const
408 {
409 Box result(*this);
410 result |= r;
411 return result;
412 }
413
414 /** Modifies this box by intersecting it with the given one.
415 * The result will be the maximal box contained in both
416 * original ones. Intersecting with an empty box will yield
417 * again an empty box.
418 */
419 Box &operator&=(Box const &r)
420 {
421 if(isEmpty())
422 return *this;
423 if(r.isEmpty())
424 return this->operator=(r);
425
426 for(unsigned int i = 0; i < DIMENSION; ++i)
427 {
428 if(begin_[i] < r.begin_[i])
429 begin_[i] = r.begin_[i];
430 if(r.end_[i] < end_[i])
431 end_[i] = r.end_[i];
432 }
433 return *this;
434 }
435
436 /** Intersects this box with the given one.
437 * The result will be the maximal box contained in both
438 * original ones. Intersecting with an empty box will yield
439 * again an empty box.
440 */
441 Box operator&(Box const &r) const
442 {
443 Box result(*this);
444 result &= r;
445 return result;
446 }
447
448 /**
449 * Scale box by scalar multiply-assignment. The same scalar
450 * multiply-assignment operation will be performed on both
451 * begin() and end().
452 */
453 Box &operator*=(double scale)
454 {
455 begin_ *= scale;
456 end_ *= scale;
457 return *this;
458 }
459
460 /**
461 * Return box scaled by given factor. The same scalar
462 * multiplication will be performed on both begin() and end().
463 */
464 Box operator*(double scale)
465 {
466 Box result(*this);
467 result *= scale;
468 return result;
469 }
470
471 /**
472 * Scale box by scalar divide-assignment. The same scalar
473 * divide-assignment operation will be performed on both
474 * begin() and end().
475 */
476 Box &operator/=(double scale)
477 {
478 begin_ /= scale;
479 end_ /= scale;
480 return *this;
481 }
482
483 /**
484 * Return box scaled by inverse of given factor. The same scalar
485 * division will be performed on both begin() and end().
486 */
487 Box operator/(double scale)
488 {
489 Box result(*this);
490 result /= scale;
491 return result;
492 }
493
494 /**
495 * Translate box by vector addition-assignment. The same vector
496 * addition-assignment operation will be performed on both
497 * begin() and end().
498 */
499 Box &operator+=(const Vector &offset)
500 {
501 begin_ += offset;
502 end_ += offset;
503 return *this;
504 }
505
506 /**
507 * Translate box by vector addition. The same vector addition
508 * operation will be performed on both begin() and end().
509 */
510 Box operator+(const Vector &offset)
511 {
512 Box result(*this);
513 result += offset;
514 return result;
515 }
516
517 /**
518 * Translate box by vector subtract-assignment. The same vector
519 * subtract-assignment operation will be performed on both
520 * begin() and end().
521 */
522 Box &operator-=(const Vector &offset)
523 {
524 begin_ -= offset;
525 end_ -= offset;
526 return *this;
527 }
528
529 /**
530 * Translate box by vector subtract. The same vector subtract
531 * operation will be performed on both begin() and end().
532 */
533 Box operator-(const Vector &offset)
534 {
535 Box result(*this);
536 result -= offset;
537 return result;
538 }
539 };
540
541 //@}
542
543 } // namespace vigra
544
545 #endif // VIGRA_BOX_HXX

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de) Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany | html generated using doxygen and Python vigra 1.10.0 (Thu Jan 8 2015)