Fix test for bug #32625
[gnash.git] / libbase / Range2d.h
blob92b4910b3f7a758478a7096d249478b35c727aee
1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
3 // Foundation, Inc
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 // Original author: Sandro Santilli <strk@keybit.net>
25 #ifndef GNASH_RANGE2D_H
26 #define GNASH_RANGE2D_H
28 #include <ostream>
29 #include <limits>
30 #include <algorithm>
31 #include <cassert> // for inlines
32 #include <cmath> // for floor / ceil
34 namespace gnash {
36 namespace geometry {
38 /// Kinds of a range
39 enum RangeKind {
40 /// Valid range, using finite values
41 finiteRange,
43 /// A NULL range is a range enclosing NO points.
44 nullRange,
46 /// \brief
47 /// A WORLD range2d is a range including
48 /// all points on the plane.
49 //
50 /// Note that scaling, shifting and unioning
51 /// will NOT change a WORLD range.
52 ///
53 worldRange
56 /// 2d Range template class
58 /// The class stores 4 values of the type specified
59 /// as template argument, representing the set of points
60 /// enclosed by the given min and max values for the 2 dimensions,
61 /// and provides methods for manipulating them.
62 /// The parameter type must be a numeric type.
63 ///
64 /// The two dimensions are called X and Y.
65 ///
66 /// Note that the range is "open", which means that the points
67 /// on its boundary are considered internal to the range.
68 ///
69 ///
70 template <typename T>
71 class Range2d
73 private:
75 T _xmin, _xmax, _ymin, _ymax;
77 T scaleMin(T min, float scale)
79 return roundMin(static_cast<float>(min) * scale);
82 T scaleMax(T max, float scale)
84 return roundMax(static_cast<float>(max) * scale);
87 T roundMin(float v)
89 return static_cast<T>(v);
92 T roundMax(float v)
94 return static_cast<T>(v);
97 public:
99 /// Ouput operator
100 template <typename U>
101 friend std::ostream& operator<< (std::ostream& os, const Range2d<U>& rect);
103 /// Equality operator
105 /// This is needed to take NULL kind into account
106 /// since we don't explicitly set all members when constructing
107 /// NULL ranges
109 template <typename U>
110 friend bool operator== (const Range2d<U>& r1, const Range2d<U>& r2);
112 /// Inequality operator
114 /// This is needed to take NULL kind into account
115 /// since we don't explicitly set all members when constructing
116 /// NULL ranges
118 template <typename U>
119 friend bool operator!= (const Range2d<U>& r1, const Range2d<U>& r2);
121 /// Return a rectangle being the intersetion of the two rectangles
123 /// Any NULL operand will make the result also NULL.
125 template <typename U> friend Range2d<U>
126 Intersection(const Range2d<U>& r1, const Range2d<U>& r2);
128 /// Return a rectangle being the union of the two rectangles
129 template <typename U> friend Range2d<U>
130 Union(const Range2d<U>& r1, const Range2d<U>& r2);
132 /// Construct a Range2d of the given kind.
134 /// The default is building a nullRange.
135 /// If finiteRange is given the range will be set to
136 /// enclose the origin.
138 /// See RangeKind
140 Range2d(RangeKind kind=nullRange)
142 _xmin(T()),
143 _xmax(T()),
144 _ymin(T()),
145 _ymax(T())
147 switch ( kind )
149 case worldRange:
150 setWorld();
151 break;
152 case nullRange:
153 setNull();
154 break;
155 default:
156 case finiteRange:
157 break;
161 /// Construct a finite Range2d with the given values
163 /// Make sure that the min <= max, or an assertion
164 /// would fail. We could as well swap the values
165 /// in this case, but it is probably better to
166 /// force caller to deal with this, as a similar
167 /// case might as well expose a bug in the code.
169 Range2d(T xmin, T ymin, T xmax, T ymax)
171 _xmin(xmin),
172 _xmax(xmax),
173 _ymin(ymin),
174 _ymax(ymax)
176 // use the default ctor to make a NULL Range2d
177 assert(_xmin <= _xmax);
178 assert(_ymin <= _ymax);
179 // .. or should we raise an exception .. ?
182 /// Templated copy constructor, for casting between range types
183 template <typename U>
184 Range2d(const Range2d<U>& from)
186 if ( from.isWorld() ) {
187 setWorld();
188 } else if ( from.isNull() ) {
189 setNull();
190 } else {
191 _xmin = roundMin(from.getMinX());
192 _ymin = roundMin(from.getMinY());
193 _xmax = roundMax(from.getMaxX());
194 _ymax = roundMax(from.getMaxY());
198 /// Returns true if this is the NULL Range2d
199 bool isNull() const
201 return _xmax < _xmin;
204 /// Set the Range2d to the NULL value
206 /// @return a reference to this instance
208 Range2d<T>& setNull()
210 _xmin = std::numeric_limits<T>::max();
211 _xmax = std::numeric_limits<T>::min();
212 return *this;
215 /// Returns true if this is the WORLD Range2d
216 bool isWorld() const
218 return _xmax == std::numeric_limits<T>::max()
219 && _xmin == std::numeric_limits<T>::min();
222 /// Returns true if this is a finite Range2d
224 /// See RangeKind::finiteRange
226 bool isFinite() const
228 return ( ! isNull() && ! isWorld() );
231 /// Set the Range2d to the WORLD value
233 /// This is implemented using the minimun and maximun
234 /// values of the parameter type.
236 /// See RangeType::worldRange
238 /// @return a reference to this instance
240 Range2d<T>& setWorld()
242 _xmin = std::numeric_limits<T>::min();
243 _xmax = std::numeric_limits<T>::max();
244 return *this;
247 /// \brief
248 /// Return true if this rectangle contains the point with
249 /// given coordinates (boundaries are inclusive).
251 /// Note that WORLD rectangles contain every point
252 /// and NULL rectangles contain no point.
254 template <typename U>
255 bool contains(U x, U y) const
257 if ( isNull() ) return false;
258 if ( isWorld() ) return true;
259 if (x < _xmin || x > _xmax || y < _ymin || y > _ymax)
261 return false;
263 return true;
266 /// \brief
267 /// Return true if this rectangle contains the given rectangle.
269 /// Note that:
271 /// - WORLD ranges contain every range except NULL ones
272 /// and are only contained in WORLD ranges
274 /// - NULL ranges contain no ranges and are contained in no ranges.
276 bool contains(const Range2d<T>& other) const
278 if ( isNull() || other.isNull() ) return false;
279 if ( isWorld() ) return true;
280 if ( other.isWorld() ) return false;
282 return _xmin <= other._xmin &&
283 _xmax >= other._xmax &&
284 _ymin <= other._ymin &&
285 _ymax >= other._ymax;
288 /// \brief
289 /// Return true if this rectangle intersects the point with
290 /// given coordinates (boundaries are inclusive).
292 /// Note that NULL rectangles don't intersect anything
293 /// and WORLD rectangles intersects everything except a NULL rectangle.
295 bool intersects(const Range2d<T>& other) const
297 if ( isNull() || other.isNull() ) return false;
298 if ( isWorld() || other.isWorld() ) return true;
300 if ( _xmin > other._xmax ) return false;
301 if ( _xmax < other._xmin ) return false;
302 if ( _ymin > other._ymax ) return false;
303 if ( _ymax < other._ymin ) return false;
304 return true;
307 /// Expand this Range2d to enclose the given point.
309 /// @return a reference to this instance
311 Range2d<T>& expandTo(T x, T y)
313 // A WORLD range already enclose every point
314 if ( isWorld() ) return *this;
316 if ( isNull() )
318 setTo(x,y);
320 else
322 _xmin = std::min(_xmin, x);
323 _ymin = std::min(_ymin, y);
324 _xmax = std::max(_xmax, x);
325 _ymax = std::max(_ymax, y);
328 return *this;
331 /// Expand this Range2d to enclose the given circle.
333 /// @return a reference to this instance
335 Range2d<T>& expandToCircle(T x, T y, T radius)
337 // A WORLD range already enclose every point
338 if ( isWorld() ) return *this;
340 expandTo(x-radius, y);
341 expandTo(x+radius, y);
343 expandTo(x, y-radius);
344 expandTo(x, y+radius);
346 return *this;
349 /// Set ourself to bound the given point
351 /// @return a reference to this instance
353 Range2d<T>& setTo(T x, T y)
355 _xmin = _xmax = x;
356 _ymin = _ymax = y;
357 return *this;
360 /// Set coordinates to given values
362 /// Make sure that the min <= max, or an assertion
363 /// would fail. We could as well swap the values
364 /// in this case, but it is probably better to
365 /// force caller to deal with this, as a similar
366 /// case might as well expose a bug in the code.
368 /// @return a reference to this instance
370 Range2d<T>& setTo(T xmin, T ymin, T xmax, T ymax)
372 _xmin = xmin;
373 _xmax = xmax;
374 _ymin = ymin;
375 _ymax = ymax;
377 // use the default ctor to make a NULL Range2d
378 assert(_xmin <= _xmax);
379 assert(_ymin <= _ymax);
381 return *this;
384 /// Return width this Range2d
386 /// Don't call this function on a WORLD rectangle!
388 T width() const
390 assert ( ! isWorld() );
391 if ( isNull() ) return 0;
392 return _xmax-_xmin;
395 /// Return height this Range2dangle
397 /// Don't call this function on a WORLD rectangle!
399 T height() const
401 assert ( ! isWorld() );
402 if ( isNull() ) return 0;
403 return _ymax-_ymin;
406 /// Shift this Range2dangle horizontally
408 /// A positive offset will shift to the right,
409 /// A negative offset will shift to the left.
411 /// WORLD or NULL ranges will be unchanged
413 /// @return a reference to this instance
415 Range2d<T>& shiftX(T offset)
417 if ( isNull() || isWorld() ) return *this;
418 _xmin += offset;
419 _xmax += offset;
420 return *this;
423 /// Shift this Range2dangle vertically
425 /// A positive offset will increment y values.
426 /// A negative offset will decrement y values.
428 /// WORLD or NULL ranges will be unchanged
430 /// @return a reference to this instance
432 Range2d<T>& shiftY(T offset)
434 if ( isNull() || isWorld() ) return *this;
435 _ymin += offset;
436 _ymax += offset;
437 return *this;
440 /// Scale this Range2d horizontally
441 Range2d<T>& scaleX(float factor)
443 return scale(factor, 1);
446 /// Scale this Range2d vertically
447 Range2d<T>& scaleY(float factor)
449 return scale(1, factor);
452 /// Scale this Range2d
454 /// WORLD or NULL ranges will be unchanged
456 /// For finite ranges:
457 /// Any factor of 0 will make the range NULL.
458 /// A factor of 1 will leave the corresponding size unchanged.
459 /// A factor > 1 will make the corresponding size bigger.
460 /// A factor < 1 factor will make the corresponding size smaller.
462 /// Computation is done in single floating point precision.
463 /// Specializations for integer types ensure that when rounding
464 /// back the resulting range is not smaller then the floating
465 /// range computed during scaling (in all directions).
467 /// Control point is the origin (0,0).
469 /// If the range so scaled will hit the numerical limit
470 /// of the range an assertion will fail
471 /// (TODO: throw an exception instead!).
473 /// @param xfactor
474 /// The horizontal scale factor. It's a float
475 /// to allow for fractional scale even for integer
476 /// ranges.
478 /// @param yfactor
479 /// The vertical scale factor. It's a float
480 /// to allow for fractional scale even for integer
481 /// ranges.
483 /// @return a reference to this instance
485 Range2d<T>& scale(float xfactor, float yfactor)
487 assert(xfactor >= 0 && yfactor >= 0);
489 if ( ! isFinite() ) return *this;
491 if ( xfactor == 0 || yfactor == 0 )
493 return setNull();
496 if ( xfactor != 1 )
498 _xmin = scaleMin(_xmin, xfactor);
499 _xmax = scaleMax(_xmax, xfactor);
500 assert(_xmin <= _xmax); // in case of overflow...
503 if ( yfactor != 1 )
505 _ymin = scaleMin(_ymin, yfactor);
506 _ymax = scaleMax(_ymax, yfactor);
507 assert(_ymin <= _ymax); // in case of overflow...
510 return *this;
513 /// Scale this Range2d in both directions with the same factor
514 Range2d<T>& scale(float factor)
516 return scale(factor, factor);
519 /// Grow this range by the given amout in all directions.
521 /// WORLD or NULL ranges will be unchanged.
523 /// If a growing range hits the numerical limit for T
524 /// it will be set to the WORLD range.
526 /// @param amount
527 /// The amount of T to grow this range in all directions.
528 /// If negative the range will shrink.
529 /// If negative the range will shrink.
530 /// See shrinkBy().
532 /// @return a reference to this instance
534 Range2d<T>& growBy(T amount)
536 if ( isNull() || isWorld() || amount==0 ) return *this;
538 // NOTE: triggers a compiler warning when T is an unsigned type
539 if ( amount < 0 ) return shrinkBy(-amount);
541 T newxmin = _xmin - amount;
542 if (newxmin > _xmin ) return setWorld();
543 else _xmin = newxmin;
545 T newxmax = _xmax + amount;
546 if (newxmax < _xmax ) return setWorld();
547 else _xmax = newxmax;
549 T newymin = _ymin - amount;
550 if (newymin > _ymin ) return setWorld();
551 else _ymin = newymin;
553 T newymax = _ymax + amount;
554 if (newymax < _ymax ) return setWorld();
555 else _ymax = newymax;
557 return *this;
561 /// Shirnk this range by the given amout in all directions.
563 /// WORLD or NULL ranges will be unchanged.
565 /// If a shrinking range will collapse in either the horizontal
566 /// or vertical dimension it will be set to the NULL range.
568 /// @param amount
569 /// The amount of T to shink this range in all directions.
570 /// If negative the range will grow.
571 /// See growBy().
573 /// @return a reference to this instance
575 /// NOTE: This method assumes that the numerical type used
576 /// as parameter does allow both positive and negative
577 /// values. Using this method against an instance of
578 /// an 'unsigned' Range2d will likely raise unexpected
579 /// results.
580 ///
581 /// TODO: change the interface to never make the Range null,
582 /// as we might always use the Range *center* point
583 /// instead of forgetting about it!
585 Range2d<T>& shrinkBy(T amount)
587 if ( isNull() || isWorld() || amount==0 ) return *this;
589 // NOTE: whith will likely trigger a compiler
590 // warning when T is an unsigned type
591 if ( amount < 0 ) return growBy(-amount);
593 // Turn this range into the NULL range
594 // if any dimension collapses.
595 // Don't use width() and height() to
596 // avoid superflous checks.
598 if ( _xmax - _xmin <= amount ) return setNull();
599 if ( _ymax - _ymin <= amount ) return setNull();
601 _xmin += amount;
602 _ymin += amount;
603 _xmax -= amount;
604 _ymax -= amount;
606 return *this;
610 /// Get min X ordinate.
612 /// Don't call this against a NULL or WORLD Range2
614 T getMinX() const
616 assert ( isFinite() );
617 return _xmin;
620 /// Get max X ordinate.
622 /// Don't call this against a NULL or WORLD Range2d
624 T getMaxX() const
626 assert ( isFinite() );
627 return _xmax;
630 /// Get min Y ordinate.
632 /// Don't call this against a NULL or WORLD Range2d
634 T getMinY() const
636 assert ( isFinite() );
637 return _ymin;
640 /// Get max Y ordinate.
642 /// Don't call this against a NULL or WORLD Range2d
644 T getMaxY() const
646 assert ( isFinite() );
647 return _ymax;
651 /// Get area (width*height)
652 ///
653 T getArea() const
655 assert ( !isWorld() );
656 if ( isNull() ) return 0;
657 return (_xmax - _xmin) * (_ymax - _ymin);
658 // this implementation is for float types, see specialization below
659 // for ints...
662 /// Expand this range to include the given Range2d
664 /// WORLD ranges force result to be the WORLD range.
665 /// A NULL range will have no effect on the result.
667 void expandTo(const Range2d<T>& r)
669 if ( r.isNull() )
671 // the given range will add nothing
672 return;
675 if ( isNull() )
677 // being null ourself, we'll equal the given range
678 *this = r;
679 return;
682 if ( isWorld() || r.isWorld() )
684 // union with world is always world...
685 setWorld();
686 return;
689 _xmin = std::min(_xmin, r._xmin);
690 _xmax = std::max(_xmax, r._xmax);
691 _ymin = std::min(_ymin, r._ymin);
692 _ymax = std::max(_ymax, r._ymax);
699 template <typename T> inline std::ostream&
700 operator<< (std::ostream& os, const Range2d<T>& rect)
702 if ( rect.isNull() ) return os << "Null range";
703 if ( rect.isWorld() ) return os << "World range";
705 return os << "Finite range (" << rect._xmin << "," << rect._ymin
706 << " " << rect._xmax << "," << rect._ymax << ")";
709 template <typename T> inline bool
710 operator== (const Range2d<T>& r1, const Range2d<T>& r2)
712 // These checks are needed becase
713 // we don't initialize *all* memebers
714 // when setting to Null or World
716 if ( r1.isNull() ) return r2.isNull();
717 if ( r2.isNull() ) return r1.isNull();
718 if ( r1.isWorld() ) return r2.isWorld();
719 if ( r2.isWorld() ) return r1.isWorld();
721 return r1._xmin == r2._xmin && r1._ymin == r2._ymin &&
722 r1._xmax == r2._xmax && r1._ymax == r2._ymax;
725 template <typename T> inline bool
726 operator!= (const Range2d<T>& r1, const Range2d<T>& r2)
728 return ! ( r1 == r2 );
731 /// Return true of the two ranges intersect (boundaries included)
732 template <typename T> inline bool
733 Intersect(const Range2d<T>& r1, const Range2d<T>& r2)
735 return r1.intersects(r2);
738 /// Return a rectangle being the union of the two rectangles
739 template <typename T> inline Range2d<T>
740 Union(const Range2d<T>& r1, const Range2d<T>& r2)
742 Range2d<T> ret = r1;
743 ret.expandTo(r2);
744 return ret;
747 /// Return a rectangle being the intersetion of the two rectangles
749 /// Any NULL operand will make the result also NULL.
751 template <typename T> inline Range2d<T>
752 Intersection(const Range2d<T>& r1, const Range2d<T>& r2)
754 if ( r1.isNull() || r2.isNull() ) {
755 // NULL ranges intersect nothing
756 return Range2d<T>(nullRange);
759 if ( r1.isWorld() ) {
760 // WORLD range intersect everything
761 return r2;
764 if ( r2.isWorld() ) {
765 // WORLD range intersect everything
766 return r1;
769 if ( ! r1.intersects(r2) ) {
770 // No intersection results in a NULL range
771 return Range2d<T>(nullRange);
774 return Range2d<T> (
775 std::max(r1._xmin, r2._xmin), // xmin
776 std::max(r1._ymin, r2._ymin), // ymin
777 std::min(r1._xmax, r2._xmax), // xmax
778 std::min(r1._ymax, r2._ymax) // ymax
783 /// Specialization of minimum value rounding for int type.
785 /// Use floor.
787 template <> inline int
788 Range2d<int>::roundMin(float min)
790 return static_cast<int>(std::floor(min));
793 /// Specialization of minimum value rounding for unsigned int type.
795 /// Use floor.
797 template <> inline unsigned int
798 Range2d<unsigned int>::roundMin(float min)
800 return static_cast<unsigned int>(std::floor(min));
803 /// Specialization of maximum value rounding for int type.
805 /// Use ceil.
807 template <> inline int
808 Range2d<int>::roundMax(float max)
810 return static_cast<int>(std::ceil(max));
813 /// Specialization of maximum value rounding for unsigned int type.
815 /// Use ceil.
817 template <> inline unsigned int
818 Range2d<unsigned int>::roundMax(float max)
820 return static_cast<unsigned int>(std::ceil(static_cast<float>(max)));
823 /// Specialization of area value for int type.
825 /// Add one.
827 template <> inline int
828 Range2d<int>::getArea() const
830 assert ( !isWorld() );
831 if ( isNull() ) return 0;
832 return (_xmax - _xmin + 1) * (_ymax - _ymin + 1);
835 /// Specialization of area value for unsigned int type.
837 /// Add one.
839 template <> inline unsigned int
840 Range2d<unsigned int>::getArea() const
842 assert ( isFinite() );
843 return (_xmax - _xmin + 1) * (_ymax - _ymin + 1);
847 } // namespace gnash::geometry
848 } // namespace gnash
850 #endif // GNASH_RANGE2D_H
853 // Local Variables:
854 // mode: C++
855 // indent-tabs-mode: t
856 // End: