2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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.
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.
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
31 #include <cassert> // for inlines
32 #include <cmath> // for floor / ceil
40 /// Valid range, using finite values
43 /// A NULL range is a range enclosing NO points.
47 /// A WORLD range2d is a range including
48 /// all points on the plane.
50 /// Note that scaling, shifting and unioning
51 /// will NOT change a WORLD range.
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.
64 /// The two dimensions are called X and Y.
66 /// Note that the range is "open", which means that the points
67 /// on its boundary are considered internal to the range.
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
);
89 return static_cast<T
>(v
);
94 return static_cast<T
>(v
);
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
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
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.
140 Range2d(RangeKind kind
=nullRange
)
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
)
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() ) {
188 } else if ( from
.isNull() ) {
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
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();
215 /// Returns true if this is the WORLD Range2d
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();
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
)
267 /// Return true if this rectangle contains the given rectangle.
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
;
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;
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;
322 _xmin
= std::min(_xmin
, x
);
323 _ymin
= std::min(_ymin
, y
);
324 _xmax
= std::max(_xmax
, x
);
325 _ymax
= std::max(_ymax
, y
);
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
);
349 /// Set ourself to bound the given point
351 /// @return a reference to this instance
353 Range2d
<T
>& setTo(T x
, T y
)
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
)
377 // use the default ctor to make a NULL Range2d
378 assert(_xmin
<= _xmax
);
379 assert(_ymin
<= _ymax
);
384 /// Return width this Range2d
386 /// Don't call this function on a WORLD rectangle!
390 assert ( ! isWorld() );
391 if ( isNull() ) return 0;
395 /// Return height this Range2dangle
397 /// Don't call this function on a WORLD rectangle!
401 assert ( ! isWorld() );
402 if ( isNull() ) return 0;
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;
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;
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!).
474 /// The horizontal scale factor. It's a float
475 /// to allow for fractional scale even for integer
479 /// The vertical scale factor. It's a float
480 /// to allow for fractional scale even for integer
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 )
498 _xmin
= scaleMin(_xmin
, xfactor
);
499 _xmax
= scaleMax(_xmax
, xfactor
);
500 assert(_xmin
<= _xmax
); // in case of overflow...
505 _ymin
= scaleMin(_ymin
, yfactor
);
506 _ymax
= scaleMax(_ymax
, yfactor
);
507 assert(_ymin
<= _ymax
); // in case of overflow...
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.
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.
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
;
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.
569 /// The amount of T to shink this range in all directions.
570 /// If negative the range will grow.
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
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();
610 /// Get min X ordinate.
612 /// Don't call this against a NULL or WORLD Range2
616 assert ( isFinite() );
620 /// Get max X ordinate.
622 /// Don't call this against a NULL or WORLD Range2d
626 assert ( isFinite() );
630 /// Get min Y ordinate.
632 /// Don't call this against a NULL or WORLD Range2d
636 assert ( isFinite() );
640 /// Get max Y ordinate.
642 /// Don't call this against a NULL or WORLD Range2d
646 assert ( isFinite() );
651 /// Get area (width*height)
655 assert ( !isWorld() );
656 if ( isNull() ) return 0;
657 return (_xmax
- _xmin
) * (_ymax
- _ymin
);
658 // this implementation is for float types, see specialization below
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
)
671 // the given range will add nothing
677 // being null ourself, we'll equal the given range
682 if ( isWorld() || r
.isWorld() )
684 // union with world is always world...
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
)
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
764 if ( r2
.isWorld() ) {
765 // WORLD range intersect everything
769 if ( ! r1
.intersects(r2
) ) {
770 // No intersection results in a NULL range
771 return Range2d
<T
>(nullRange
);
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.
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.
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.
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.
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.
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.
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
850 #endif // GNASH_RANGE2D_H
855 // indent-tabs-mode: t