Gnash
0.8.10
|
00001 // 00002 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 00003 // Free Software Foundation, Inc 00004 // 00005 // This program is free software; you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation; either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // This program is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with this program; if not, write to the Free Software 00017 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00018 00019 // 00020 // Original author: Sandro Santilli <strk@keybit.net> 00021 // 00022 00023 #ifndef GNASH_RANGE2D_H 00024 #define GNASH_RANGE2D_H 00025 00026 #include <ostream> 00027 #include <limits> 00028 #include <algorithm> 00029 #include <cassert> // for inlines 00030 #include <cmath> // for floor / ceil 00031 00032 namespace gnash { 00033 00034 namespace geometry { 00035 00037 enum RangeKind { 00039 finiteRange, 00040 00042 nullRange, 00043 00047 // 00051 worldRange 00052 }; 00053 00055 // 00068 template <typename T> 00069 class Range2d 00070 { 00071 public: 00072 00074 template <typename U> 00075 friend std::ostream& operator<< (std::ostream& os, const Range2d<U>& rect); 00076 00078 // 00083 template <typename U> 00084 friend bool operator== (const Range2d<U>& r1, const Range2d<U>& r2); 00085 00087 // 00092 template <typename U> 00093 friend bool operator!= (const Range2d<U>& r1, const Range2d<U>& r2); 00094 00096 // 00099 template <typename U> friend Range2d<U> 00100 Intersection(const Range2d<U>& r1, const Range2d<U>& r2); 00101 00103 template <typename U> friend Range2d<U> 00104 Union(const Range2d<U>& r1, const Range2d<U>& r2); 00105 00107 // 00114 Range2d(RangeKind kind=nullRange) 00115 : 00116 _xmin(T()), 00117 _xmax(T()), 00118 _ymin(T()), 00119 _ymax(T()) 00120 { 00121 switch ( kind ) 00122 { 00123 case worldRange: 00124 setWorld(); 00125 break; 00126 case nullRange: 00127 setNull(); 00128 break; 00129 default: 00130 case finiteRange: 00131 break; 00132 } 00133 } 00134 00136 // 00143 Range2d(T xmin, T ymin, T xmax, T ymax) 00144 : 00145 _xmin(xmin), 00146 _xmax(xmax), 00147 _ymin(ymin), 00148 _ymax(ymax) 00149 { 00150 // use the default ctor to make a NULL Range2d 00151 assert(_xmin <= _xmax); 00152 assert(_ymin <= _ymax); 00153 // .. or should we raise an exception .. ? 00154 } 00155 00157 template <typename U> 00158 Range2d(const Range2d<U>& from) 00159 { 00160 if ( from.isWorld() ) { 00161 setWorld(); 00162 } else if ( from.isNull() ) { 00163 setNull(); 00164 } else { 00165 _xmin = roundMin(from.getMinX()); 00166 _ymin = roundMin(from.getMinY()); 00167 _xmax = roundMax(from.getMaxX()); 00168 _ymax = roundMax(from.getMaxY()); 00169 } 00170 } 00171 00173 bool isNull() const 00174 { 00175 return _xmax < _xmin; 00176 } 00177 00179 // 00182 Range2d<T>& setNull() 00183 { 00184 _xmin = std::numeric_limits<T>::max(); 00185 _xmax = std::numeric_limits<T>::min(); 00186 return *this; 00187 } 00188 00190 bool isWorld() const 00191 { 00192 return _xmax == std::numeric_limits<T>::max() 00193 && _xmin == std::numeric_limits<T>::min(); 00194 } 00195 00197 // 00200 bool isFinite() const 00201 { 00202 return ( ! isNull() && ! isWorld() ); 00203 } 00204 00206 // 00214 Range2d<T>& setWorld() 00215 { 00216 _xmin = std::numeric_limits<T>::min(); 00217 _xmax = std::numeric_limits<T>::max(); 00218 return *this; 00219 } 00220 00224 // 00228 template <typename U> 00229 bool contains(U x, U y) const 00230 { 00231 if ( isNull() ) return false; 00232 if ( isWorld() ) return true; 00233 if (x < _xmin || x > _xmax || y < _ymin || y > _ymax) 00234 { 00235 return false; 00236 } 00237 return true; 00238 } 00239 00242 // 00250 bool contains(const Range2d<T>& other) const 00251 { 00252 if ( isNull() || other.isNull() ) return false; 00253 if ( isWorld() ) return true; 00254 if ( other.isWorld() ) return false; 00255 00256 return _xmin <= other._xmin && 00257 _xmax >= other._xmax && 00258 _ymin <= other._ymin && 00259 _ymax >= other._ymax; 00260 } 00261 00265 // 00269 bool intersects(const Range2d<T>& other) const 00270 { 00271 if ( isNull() || other.isNull() ) return false; 00272 if ( isWorld() || other.isWorld() ) return true; 00273 00274 if ( _xmin > other._xmax ) return false; 00275 if ( _xmax < other._xmin ) return false; 00276 if ( _ymin > other._ymax ) return false; 00277 if ( _ymax < other._ymin ) return false; 00278 return true; 00279 } 00280 00282 // 00285 Range2d<T>& expandTo(T x, T y) 00286 { 00287 // A WORLD range already enclose every point 00288 if ( isWorld() ) return *this; 00289 00290 if ( isNull() ) 00291 { 00292 setTo(x,y); 00293 } 00294 else 00295 { 00296 _xmin = std::min(_xmin, x); 00297 _ymin = std::min(_ymin, y); 00298 _xmax = std::max(_xmax, x); 00299 _ymax = std::max(_ymax, y); 00300 } 00301 00302 return *this; 00303 } 00304 00306 // 00309 Range2d<T>& expandToCircle(T x, T y, T radius) 00310 { 00311 // A WORLD range already enclose every point 00312 if ( isWorld() ) return *this; 00313 00314 expandTo(x-radius, y); 00315 expandTo(x+radius, y); 00316 00317 expandTo(x, y-radius); 00318 expandTo(x, y+radius); 00319 00320 return *this; 00321 } 00322 00324 // 00327 Range2d<T>& setTo(T x, T y) 00328 { 00329 _xmin = _xmax = x; 00330 _ymin = _ymax = y; 00331 return *this; 00332 } 00333 00335 // 00341 // 00344 Range2d<T>& setTo(T xmin, T ymin, T xmax, T ymax) 00345 { 00346 _xmin = xmin; 00347 _xmax = xmax; 00348 _ymin = ymin; 00349 _ymax = ymax; 00350 00351 // use the default ctor to make a NULL Range2d 00352 assert(_xmin <= _xmax); 00353 assert(_ymin <= _ymax); 00354 00355 return *this; 00356 } 00357 00359 // 00362 T width() const 00363 { 00364 assert ( ! isWorld() ); 00365 if ( isNull() ) return 0; 00366 return _xmax-_xmin; 00367 } 00368 00370 // 00373 T height() const 00374 { 00375 assert ( ! isWorld() ); 00376 if ( isNull() ) return 0; 00377 return _ymax-_ymin; 00378 } 00379 00381 // 00389 Range2d<T>& shiftX(T offset) 00390 { 00391 if ( isNull() || isWorld() ) return *this; 00392 _xmin += offset; 00393 _xmax += offset; 00394 return *this; 00395 } 00396 00398 // 00406 Range2d<T>& shiftY(T offset) 00407 { 00408 if ( isNull() || isWorld() ) return *this; 00409 _ymin += offset; 00410 _ymax += offset; 00411 return *this; 00412 } 00413 00415 Range2d<T>& scaleX(float factor) 00416 { 00417 return scale(factor, 1); 00418 } 00419 00421 Range2d<T>& scaleY(float factor) 00422 { 00423 return scale(1, factor); 00424 } 00425 00427 // 00459 Range2d<T>& scale(float xfactor, float yfactor) 00460 { 00461 assert(xfactor >= 0 && yfactor >= 0); 00462 00463 if ( ! isFinite() ) return *this; 00464 00465 if ( xfactor == 0 || yfactor == 0 ) 00466 { 00467 return setNull(); 00468 } 00469 00470 if ( xfactor != 1 ) 00471 { 00472 _xmin = scaleMin(_xmin, xfactor); 00473 _xmax = scaleMax(_xmax, xfactor); 00474 assert(_xmin <= _xmax); // in case of overflow... 00475 } 00476 00477 if ( yfactor != 1 ) 00478 { 00479 _ymin = scaleMin(_ymin, yfactor); 00480 _ymax = scaleMax(_ymax, yfactor); 00481 assert(_ymin <= _ymax); // in case of overflow... 00482 } 00483 00484 return *this; 00485 } 00486 00488 Range2d<T>& scale(float factor) 00489 { 00490 return scale(factor, factor); 00491 } 00492 00494 // 00508 Range2d<T>& growBy(T amount) 00509 { 00510 if ( isNull() || isWorld() || amount==0 ) return *this; 00511 00512 // NOTE: triggers a compiler warning when T is an unsigned type 00513 if ( amount < 0 ) return shrinkBy(-amount); 00514 00515 T newxmin = _xmin - amount; 00516 if (newxmin > _xmin ) return setWorld(); 00517 else _xmin = newxmin; 00518 00519 T newxmax = _xmax + amount; 00520 if (newxmax < _xmax ) return setWorld(); 00521 else _xmax = newxmax; 00522 00523 T newymin = _ymin - amount; 00524 if (newymin > _ymin ) return setWorld(); 00525 else _ymin = newymin; 00526 00527 T newymax = _ymax + amount; 00528 if (newymax < _ymax ) return setWorld(); 00529 else _ymax = newymax; 00530 00531 return *this; 00532 00533 } 00534 00536 // 00559 Range2d<T>& shrinkBy(T amount) 00560 { 00561 if ( isNull() || isWorld() || amount==0 ) return *this; 00562 00563 // NOTE: whith will likely trigger a compiler 00564 // warning when T is an unsigned type 00565 if ( amount < 0 ) return growBy(-amount); 00566 00567 // Turn this range into the NULL range 00568 // if any dimension collapses. 00569 // Don't use width() and height() to 00570 // avoid superflous checks. 00571 00572 if ( _xmax - _xmin <= amount ) return setNull(); 00573 if ( _ymax - _ymin <= amount ) return setNull(); 00574 00575 _xmin += amount; 00576 _ymin += amount; 00577 _xmax -= amount; 00578 _ymax -= amount; 00579 00580 return *this; 00581 00582 } 00583 00585 // 00588 T getMinX() const 00589 { 00590 assert ( isFinite() ); 00591 return _xmin; 00592 } 00593 00595 // 00598 T getMaxX() const 00599 { 00600 assert ( isFinite() ); 00601 return _xmax; 00602 } 00603 00605 // 00608 T getMinY() const 00609 { 00610 assert ( isFinite() ); 00611 return _ymin; 00612 } 00613 00615 // 00618 T getMaxY() const 00619 { 00620 assert ( isFinite() ); 00621 return _ymax; 00622 } 00623 00626 T getArea() const { 00627 assert ( !isWorld() ); 00628 if ( isNull() ) return 0; 00629 return (_xmax - _xmin) * (_ymax - _ymin); 00630 // this implementation is for float types, see specialization below 00631 // for ints... 00632 } 00633 00635 // 00639 void expandTo(const Range2d<T>& r) 00640 { 00641 if ( r.isNull() ) 00642 { 00643 // the given range will add nothing 00644 return; 00645 } 00646 00647 if ( isNull() ) 00648 { 00649 // being null ourself, we'll equal the given range 00650 *this = r; 00651 return; 00652 } 00653 00654 if ( isWorld() || r.isWorld() ) 00655 { 00656 // union with world is always world... 00657 setWorld(); 00658 return; 00659 } 00660 00661 _xmin = std::min(_xmin, r._xmin); 00662 _xmax = std::max(_xmax, r._xmax); 00663 _ymin = std::min(_ymin, r._ymin); 00664 _ymax = std::max(_ymax, r._ymax); 00665 00666 } 00667 00668 private: 00669 00670 T _xmin, _xmax, _ymin, _ymax; 00671 00672 T scaleMin(T min, float scale) const { 00673 return roundMin(static_cast<float>(min) * scale); 00674 } 00675 00676 T scaleMax(T max, float scale) const { 00677 return roundMax(static_cast<float>(max) * scale); 00678 } 00679 00680 T roundMin(float v) const { 00681 return static_cast<T>(v); 00682 } 00683 00684 T roundMax(float v) const { 00685 return static_cast<T>(v); 00686 } 00687 00688 00689 }; 00690 00691 template <typename T> inline std::ostream& 00692 operator<< (std::ostream& os, const Range2d<T>& rect) 00693 { 00694 if ( rect.isNull() ) return os << "Null range"; 00695 if ( rect.isWorld() ) return os << "World range"; 00696 00697 return os << "Finite range (" << rect._xmin << "," << rect._ymin 00698 << " " << rect._xmax << "," << rect._ymax << ")"; 00699 } 00700 00701 template <typename T> inline bool 00702 operator== (const Range2d<T>& r1, const Range2d<T>& r2) 00703 { 00704 // These checks are needed becase 00705 // we don't initialize *all* memebers 00706 // when setting to Null or World 00707 00708 if ( r1.isNull() ) return r2.isNull(); 00709 if ( r2.isNull() ) return r1.isNull(); 00710 if ( r1.isWorld() ) return r2.isWorld(); 00711 if ( r2.isWorld() ) return r1.isWorld(); 00712 00713 return r1._xmin == r2._xmin && r1._ymin == r2._ymin && 00714 r1._xmax == r2._xmax && r1._ymax == r2._ymax; 00715 } 00716 00717 template <typename T> inline bool 00718 operator!= (const Range2d<T>& r1, const Range2d<T>& r2) 00719 { 00720 return ! ( r1 == r2 ); 00721 } 00722 00724 template <typename T> inline bool 00725 Intersect(const Range2d<T>& r1, const Range2d<T>& r2) 00726 { 00727 return r1.intersects(r2); 00728 } 00729 00731 template <typename T> inline Range2d<T> 00732 Union(const Range2d<T>& r1, const Range2d<T>& r2) 00733 { 00734 Range2d<T> ret = r1; 00735 ret.expandTo(r2); 00736 return ret; 00737 } 00738 00740 // 00743 template <typename T> inline Range2d<T> 00744 Intersection(const Range2d<T>& r1, const Range2d<T>& r2) 00745 { 00746 if ( r1.isNull() || r2.isNull() ) { 00747 // NULL ranges intersect nothing 00748 return Range2d<T>(nullRange); 00749 } 00750 00751 if ( r1.isWorld() ) { 00752 // WORLD range intersect everything 00753 return r2; 00754 } 00755 00756 if ( r2.isWorld() ) { 00757 // WORLD range intersect everything 00758 return r1; 00759 } 00760 00761 if ( ! r1.intersects(r2) ) { 00762 // No intersection results in a NULL range 00763 return Range2d<T>(nullRange); 00764 } 00765 00766 return Range2d<T> ( 00767 std::max(r1._xmin, r2._xmin), // xmin 00768 std::max(r1._ymin, r2._ymin), // ymin 00769 std::min(r1._xmax, r2._xmax), // xmax 00770 std::min(r1._ymax, r2._ymax) // ymax 00771 ); 00772 00773 } 00774 00776 // 00779 template<> inline int 00780 Range2d<int>::roundMin(float min) const 00781 { 00782 return static_cast<int>(std::floor(min)); 00783 } 00784 00786 // 00789 template<> inline unsigned int 00790 Range2d<unsigned int>::roundMin(float min) const 00791 { 00792 return static_cast<unsigned int>(std::floor(min)); 00793 } 00794 00796 // 00799 template<> inline int 00800 Range2d<int>::roundMax(float max) const 00801 { 00802 return static_cast<int>(std::ceil(max)); 00803 } 00804 00806 // 00809 template<> inline unsigned int 00810 Range2d<unsigned int>::roundMax(float max) const 00811 { 00812 return static_cast<unsigned int>(std::ceil(static_cast<float>(max))); 00813 } 00814 00816 // 00819 template<> inline int 00820 Range2d<int>::getArea() const 00821 { 00822 assert ( !isWorld() ); 00823 if ( isNull() ) return 0; 00824 return (_xmax - _xmin + 1) * (_ymax - _ymin + 1); 00825 } 00826 00828 // 00831 template<> inline unsigned int 00832 Range2d<unsigned int>::getArea() const 00833 { 00834 assert ( isFinite() ); 00835 return (_xmax - _xmin + 1) * (_ymax - _ymin + 1); 00836 } 00837 00838 00839 } // namespace gnash::geometry 00840 } // namespace gnash 00841 00842 #endif // GNASH_RANGE2D_H 00843 00844 00845 // Local Variables: 00846 // mode: C++ 00847 // indent-tabs-mode: t 00848 // End: