|
Boost : |
From: Dave Abrahams (abrahams_at_[hidden])
Date: 2000-01-02 11:05:41
> #ifndef GEOMETRY_HPP
> #define GEOMETRY_HPP
>
> #include <boost/operators.hpp>
> #include <algorithm>
> #include <iosfwd>
>
> namespace boost {
>
> // ???
> // by the way - why original boost operator templates return 'T' instead
> // 'const T'?
>
> //--------------------------------------------------------------------------
> -
> template <class T, class U>
> class multipliable2
> {
> friend const T operator *( T x, const U& y ) { return x *= y; }
> friend const T operator *( const U& y, T x ) { return x *= y; }
> };
>
> //--------------------------------------------------------------------------
> -
> template <class T, class U>
> class dividable2
> {
> friend const T operator /( T x, const U& y ) { return x /= y; }
> };
Okay, I'm putting my foot down: we are going to have to implement all of the
2-type versions of the operator templates with this workaround for VC++
compatibility anyway. We can simply derive the specializations from the
versions with the '2' for those platforms which support that syntax.
Question: should we deprecate the use of the specialization (LEGAL C++
LANGUAGE FEATURE) so that we can one day simplify the library!??!
> namespace geometry {
>
> //--------------------------------------------------------------------------
> -
> // geometry::size
>
> template<class T>
> class size
> : less_than_comparable< size<T> >
> , equality_comparable< size<T> >
> , addable< size<T> >
> , subtractable< size<T> >
> , multipliable< size<T> >
> , multipliable2< size<T>, T >
> , dividable< size<T> >
> , dividable2< size<T>, T >
> {
> public:
> typedef T unit_type;
>
> private:
> typedef size<unit_type> self;
> public:
> // constructors
> size();
> size( unit_type width, unit_type height );
>
> // copying
> // template<class U> size( const size<U>& other );
> // template<class U> self& operator =( const size<U>& other );
>
> // accessors - const
> unit_type width() const;
> unit_type height() const;
> unit_type cols_num() const;
> unit_type rows_num() const;
cols_num and rows_num are nondescriptive and redundant. Furthermore, if this
is pure geometry (e.g. size<double>) there are no rows or columns.
> // accessors - mutable
> unit_type& width();
> unit_type& height();
> unit_type& cols_num();
> unit_type& rows_num();
>
> // 'set' methods
> self& set( unit_type new_width, unit_type new_height );
> self& width( unit_type new_width );
> self& height( unit_type new_height );
> self& cols_num( unit_type new_cols_num );
> self& rows_num( unit_type new_rows_num );
>
> // negation
> const self operator -() const;
>
> // scaling
> self& operator *=( unit_type scale );
> self& operator /=( unit_type scale );
> self& operator *=( const size<unit_type>& scale );
> self& operator /=( const size<unit_type>& scale );
>
> // arithmetics
> self& operator +=( const size<unit_type>& delta );
> self& operator -=( const size<unit_type>& delta );
>
> private:
> unit_type width_;
> unit_type height_;
> };
>
> // size comparison
> template<class T> bool operator <( const size<T>& s1, const size<T>& s2 );
> template<class T> bool operator ==( const size<T>& s1, const size<T>& s2 );
>
> // size stream output
> template<class T>
> std::ostream& operator <<( std::ostream& stream, const size<T>& s );
>
> //--------------------------------------------------------------------------
> -
> // geometry::point
>
> template<class T>
> class point
> : less_than_comparable< point<T> >
> , equality_comparable< point<T> >
> , addable< point<T> >
> , addable2< point<T>, size<T> >
> , subtractable2< point<T>, size<T> >
> , multipliable< point<T> >
> , multipliable2< point<T>, T >
> , multipliable2< point<T>, size<T> >
> , dividable< point<T> >
> , dividable2< point<T>, T >
> , dividable2< point<T>, size<T> >
> {
> public:
> typedef T unit_type;
>
> private:
> typedef point<unit_type> self;
>
> public:
> // constructors
> point();
> point( unit_type x, unit_type y );
>
> // copying
> // template<class U> point( const point<U>& other );
> // template<class U> self& operator =( const point<U>& other );
>
> // accessors - const
> unit_type x() const;
> unit_type y() const;
> unit_type col() const;
> unit_type row() const;
>
> // accessors - mutable
> unit_type& x();
> unit_type& y();
> unit_type& col();
> unit_type& row();
>
> // 'set' methods
> self& set( unit_type new_x, unit_type new_y );
This should be called "assign" for uniformity with the STL.
> self& x( unit_type new_y );
> self& y( unit_type new_x );
> self& col( unit_type new_col );
> self& row( unit_type new_row );
How many different ways do we need to set a coordinate?
We have p.x() = 1, p.x(1), p.col() = 1, p.col(1).
Too large an interface can be harmful, since there will be no single usage
idiom established. I argue strongly for the removal of the row/col
interfaces, and also for the removal of one of the x/y interfaces, probably
the one returning non-const references.
> // negation
> const point operator -() const;
>
> // scaling
> self& operator *=( unit_type scale );
> self& operator /=( unit_type scale );
> self& operator *=( const point<unit_type>& scale );
> self& operator /=( const point<unit_type>& scale );
> self& operator *=( const size<unit_type>& scale );
> self& operator /=( const size<unit_type>& scale );
>
> // moving
> self& move_by( unit_type x_offset, unit_type y_offset );
> self& move_by( const point<unit_type>& offset );
> self& move_by( const size<unit_type>& offset );
The last two move_by are completely redundant interface, since we have +=.
Since it's easy to write p += point<T>(x, y), I would dispense with the
first as well.
> self& operator +=( const point<unit_type>& offset );
> self& operator +=( const size<unit_type>& offset );
> self& operator -=( const size<unit_type>& offset );
>
> // aligning to grid
> self& align( unit_type granule );
> self& align( const size<unit_type>& granule );
I don't think these belong in this class. Rounding is essentially a scalar
operation having nothing to do with vectors in the mathematical sense.
> private:
> unit_type x_;
> unit_type y_;
> };
It certainly looks at first glance like there's an opportunity to save some
code duplication by factoring parts of point and size into a common base
class.
> // points substruction
> template<class T>
> size<T> operator -( const point<T>& p1, const point<T>& p2 );
>
> // point comparison
> template<class T> bool operator <( const point<T>& p1, const point<T>& p2 );
> template<class T> bool operator ==( const point<T>& p1, const point<T>& p2
> );
>
> // point stream output
> template<class T>
> std::ostream& operator <<( std::ostream& stream, const point<T>& p );
>
> //--------------------------------------------------------------------------
> -
> // geometry::rect
I hate abbreviations. 'rectangle' has worked very well for me in my code and
has never been a burden.
> template<class T>
> class rect
> : addable2< rect<T>, size<T> >
> , subtractable2< rect<T>, size<T> >
> , multipliable2< rect<T>, size<T> >
> , dividable2< rect<T>, size<T> >
> , andable< rect<T> >
> , orable< rect<T> >
> {
> public:
> typedef T unit_type;
>
> private:
> typedef rect<unit_type> self;
>
> public:
> // constructors
> rect();
You need more comments everywhere in this header. For example, above: 'A
canonical empty rectangle (0,0), (0,0)'
You could use some of the characters that you spend on making horizontal
lines with your comments which do nothing for readability for description
instead.
Also, I know we usually put this down to personal style but so far we have
avoided the 'indented brace style' in boost. I'm just asking that we uphold
the tradition.
> rect( unit_type left, unit_type top, unit_type right, unit_type bottom
> );
> rect( const point<unit_type>& origin, const point<unit_type>& corner );
Should be corner1, corner2. This is much more useful and means there are
fewer ways to use rect erroneously.
> rect( const point<unit_type>& origin, const size<unit_type>& rect_size
> );
> rect( const point<unit_type>& origin );
What does the above constructor do?
> // copying
> // template<class U> rect( const rect<U>& other );
> // template<class U> self& operator =( const rect<U>& other );
>
> // accessors - const
> const point<unit_type>& origin() const;
> const point<unit_type>& corner() const;
Sorry, I hate these names. A rectangle has 4 corners. Which one do you get
when you call corner()? Is the rectangle's origin at its center? (I know it
isn't of course).
> const point<unit_type>& left_top() const;
> const point<unit_type>& right_bottom() const;
I don't know why, but I always say top_left and bottom_right. I think this
order is more natural for native English speakers. Do these names seem weird
to anyone else?
What about bottom_left and top_right? My rectangles have those, too.
> unit_type left() const;
> unit_type top() const;
> unit_type right() const;
> unit_type bottom() const;
>
> // accessors - mutable
> point<unit_type>& origin();
> point<unit_type>& corner();
>
> point<unit_type>& left_top();
> point<unit_type>& right_bottom();
>
> unit_type& left();
> unit_type& top();
> unit_type& right();
> unit_type& bottom();
>
> // 'set' methods
> self& set( unit_type left, unit_type top, unit_type right, unit_type
> bottom );
> self& set( const point<unit_type>& origin, const point<unit_type>&
> corner );
> self& set( const point<unit_type>& origin, const size<unit_type>&
> rest_size );
Use 'assign' instead if you want to provide these.
> self& set_empty();
> self& normalize();
What does the above do?
N.B. point and rect are generally cheap to assign, so I would consider
eliminating functions like set_empty() which can easily be expressed as 'r =
rectangle<T>()'. I tend to favor a more minimal interface, though I can see
the readability advantage of set_empty().
>
> self& origin( const point<unit_type>& new_origin );
> self& corner( const point<unit_type>& new_corner );
>
> self& left_top( const point<unit_type>& new_left_top );
> self& right_bottom( const point<unit_type>& new_right_bottom );
I don't think I believe in returning a reference to self from a mutating
function. Please convince me.
> self& left( unit_type new_left );
> self& top( unit_type new_top );
> self& right( unit_type new_right );
> self& bottom( unit_type new_bottom );
>
> self& size( const size<unit_type>& new_size );
> self& width( unit_type new_width );
> self& height( unit_type new_height );
>
> // computed attributes
> const point<unit_type> left_bottom() const;
> const point<unit_type> right_top() const;
> const size<unit_type> size() const;
> const point<unit_type> center() const;
> unit_type width() const;
> unit_type height() const;
>
> // moving
> self& move_by( const size<unit_type>& offset );
> self& move_to( const point<unit_type>& new_origin );
Too much redundancy for my taste. r += offset and r = rectangle(new_origin,
r.size()) work nicely.
> self& operator +=( const size<unit_type>& offset );
> self& operator -=( const size<unit_type>& offset );
>
> // scaling
> self& operator *=( const size<unit_type>& scale );
> self& operator /=( const size<unit_type>& scale );
>
> // inflating/deflating
> self& inflate( unit_type delta = 1 );
> self& inflate( unit_type x_delta, unit_type y_delta );
> self& inflate( const size<unit_type>& delta );
>
> self& inflate_size( unit_type delta = 1 );
> self& inflate_size( unit_type x_delta, unit_type y_delta );
> self& inflate_size( const size<unit_type>& delta );
What's the difference between inflate_size and inflate? I had to look at the
code to figure it out. inflate_size is nondescriptive IMO and unneccessary:
r.bottom_right(r.bottom_right + delta) is much clearer.
> self& deflate( unit_type delta = 1 );
> self& deflate( unit_type x_delta, unit_type y_delta );
> self& deflate( const size<unit_type>& delta );
>
> self& deflate_size( unit_type delta = 1 );
> self& deflate_size( unit_type x_delta, unit_type y_delta );
> self& deflate_size( const size<unit_type>& delta );
>
> // aligning
> self& align( unit_type granule );
> self& align( const size<unit_type>& granule );
Again, I think this is outside the scope of geometry.
> // testing
> bool is_empty() const;
Should be called empty() for uniformity with STL.
> int hit_test( const point<unit_type>& p ) const;
Outside the scope of geometry again, I think.
> bool inside( const rect<unit_type>& other ) const;
> bool contains( const point<unit_type>& p ) const;
> bool contains( const rect<unit_type>& other ) const;
If you have 'contains' I don't think you need 'inside'
> // unionization / intersection
> self& operator |=( const rect<unit_type>& other );
> self& operator &=( const rect<unit_type>& other );
you also need:
bool intersects(const rect<unit_type>&) const
> private:
> point<unit_type> origin_;
> point<unit_type> corner_;
> };
>
> // rect comparison/intersection test
> template<class T> bool operator ==( const rect<T>& r1, const rect<T>& r2 );
> template<class T> bool operator &( const rect<T>& r1, const rect<T>& r2 );
where's the free operator|() ?
> // rect stream output
> template<class T>
> std::ostream& operator <<( std::ostream& stream, const rect<T>& r );
>
> ////////////////////////////////////////////////////////////////////////////
> /
> // implementation geometry::size
>
> // constructors
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>::size()
> : width_( 0 )
> , height_( 0 )
> {
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>::size( unit_type width, unit_type height )
> : width_( width )
> , height_( height )
> {
> }
>
> // copying
> //--------------------------------------------------------------------------
> -
> //template<class T>
> //template<class U>
> //inline
> //size<T>::size( const size<U>& other )
> // : width_( other.width() )
> // , height_( other.height() )
> // {
> // }
>
> //--------------------------------------------------------------------------
> -
> //template<class T>
> //template<class U>
> //inline
> //size<T>&
> //size<T>::operator =( const size<U>& other )
> // {
> // width_ = other.width();
> // height_ = other.height();
> // return *this;
> // }
>
You should probably remove commented-out code which will never be used.
> // accessors - const
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>::unit_type
> size<T>::width() const
> {
> return width_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>::unit_type
> size<T>::height() const
> {
> return height_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>::unit_type
> size<T>::cols_num() const
> {
> return width();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>::unit_type
> size<T>::rows_num() const
> {
> return height();
> }
>
> // accessors - mutable
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>::unit_type&
> size<T>::width()
> {
> return width_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>::unit_type&
> size<T>::height()
> {
> return height_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>::unit_type&
> size<T>::cols_num()
> {
> return width();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>::unit_type&
> size<T>::rows_num()
> {
> return height();
> }
>
> // 'set' methods
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>&
> size<T>::set( unit_type new_width, unit_type new_height )
> {
> width_ = new_width;
> height_ = new_height;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>&
> size<T>::width( unit_type new_width )
> {
> width_ = new_width;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>&
> size<T>::height( unit_type new_height )
> {
> height_ = new_height;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>&
> size<T>::cols_num( unit_type new_cols_num )
> {
> return width( new_cols_num );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>&
> size<T>::rows_num( unit_type new_rows_num )
> {
> return height( new_rows_num );
> }
>
> // negation
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> const size<T>
> size<T>::operator -() const
> {
> return self( -width(), -height() );
> }
>
> // scaling
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>&
> size<T>::operator *=( unit_type scale )
> {
> width_ *= scale;
> height_ *= scale;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>&
> size<T>::operator /=( unit_type scale )
> {
> width_ /= scale;
> height_ /= scale;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>&
> size<T>::operator *=( const size<unit_type>& scale )
> {
> width_ *= scale.width();
> height_ *= scale.height();
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>&
> size<T>::operator /=( const size<unit_type>& scale )
> {
> width_ /= scale.width();
> height_ /= scale.height();
> return *this;
> }
>
> // arithmetics
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>&
> size<T>::operator +=( const size<unit_type>& delta )
> {
> width_ += delta.width();
> height_ += delta.height();
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>&
> size<T>::operator -=( const size<unit_type>& delta )
> {
> width_ -= delta.width();
> height_ -= delta.height();
> return *this;
> }
>
> // comparisons
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> bool
> operator ==( const size<T>& s1, const size<T>& s2 )
> {
> return s1.width() == s2.width() && s1.height() == s2.height();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> bool
> operator <( const size<T>& s1, const size<T>& s2 )
> {
> return s1.width() < s2.width() && s1.height() < s2.height();
> }
>
> // stream output
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> std::ostream&
> operator<< ( std::ostream& stream, const size<T>& s )
> {
> return ( stream << "(" << s.width() << ", " << s.height() << ")" );
> }
>
> ////////////////////////////////////////////////////////////////////////////
> /
> // implementation geometry::point
>
> // constructors
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>::point()
> : x_( 0 )
> , y_( 0 )
> {
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>::point( unit_type x, unit_type y )
> : x_( x )
> , y_( y )
> {
> }
>
> // copying
> //--------------------------------------------------------------------------
> -
> //template<class T>
> //template<class U>
> //inline
> //point<T>::point( const point<U>& other )
> // : x_( other.x() )
> // , y_( other.y() )
> // {
> // }
>
> //--------------------------------------------------------------------------
> -
> //template<class T>
> //template<class U>
> //inline
> //point<T>&
> //point<T>::operator =( const point<U>& other )
> // {
> // x_ = other.x();
> // y_ = other.y();
> // return *this;
> // }
>
> // accessors - const
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>::unit_type
> point<T>::x() const
> {
> return x_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>::unit_type
> point<T>::y() const
> {
> return y_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>::unit_type
> point<T>::col() const
> {
> return x();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>::unit_type
> point<T>::row() const
> {
> return y();
> }
>
> // accessors - mutable
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>::unit_type&
> point<T>::x()
> {
> return x_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>::unit_type&
> point<T>::y()
> {
> return y_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>::unit_type&
> point<T>::col()
> {
> return x();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>::unit_type&
> point<T>::row()
> {
> return y();
> }
>
> // 'set' methods
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::set( unit_type new_x, unit_type new_y )
> {
> x_ = new_x;
> y_ = new_y;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::x( unit_type new_x )
> {
> x_ = new_x;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::y( unit_type new_y )
> {
> y_ = new_y;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::col( unit_type new_col )
> {
> return x( new_col );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::row( unit_type new_row )
> {
> return y( new_row );
> }
>
> // negation
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> const point<T>
> point<T>::operator -() const
> {
> return self( -x(), -y() );
> }
>
> // scaling
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::operator *=( unit_type scale )
> {
> x_ *= scale;
> y_ *= scale;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::operator /=( unit_type scale )
> {
> x_ /= scale;
> y_ /= scale;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::operator *=( const point<unit_type>& scale )
> {
> x_ *= scale.x();
> y_ *= scale.y();
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::operator /=( const point<unit_type>& scale )
> {
> x_ /= scale.x();
> y_ /= scale.y();
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::operator *=( const size<unit_type>& scale )
> {
> x_ *= scale.width();
> y_ *= scale.height();
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::operator /=( const size<unit_type>& scale )
> {
> x_ /= scale.width();
> y_ /= scale.height();
> return *this;
> }
>
> // moving
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::move_by( unit_type x_offset, unit_type y_offset )
> {
> x_ += x_offset;
> y_ += y_offset;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::move_by( const point<unit_type>& offset )
> {
> return move_by( offset.x(), offset.y() );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::move_by( const size<unit_type>& offset )
> {
> return move_by( offset.width(), offset.height() );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::operator +=( const point<unit_type>& offset )
> {
> return move_by( offset );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::operator +=( const size<unit_type>& offset )
> {
> return move_by( offset );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::operator -=( const size<unit_type>& offset )
> {
> return move_by( -offset );
> }
>
> // aligning to grid
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::align( unit_type granule )
> {
> // resulting coordinate values are always less or equal than original ones
This has non-obvious semantics, and rounding direction for negative numbers
is platform-dependent.
> x_ = ( x_ / granule ) * granule;
> y_ = ( y_ / granule ) * granule;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<T>&
> point<T>::align( const size<unit_type>& granule )
> {
> x_ = ( x_ / granule.width() ) * granule.width();
> y_ = ( y_ / granule.height() ) * granule.height();
> return *this;
> }
>
> // points substruction
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> size<T>
> operator -( const point<T>& p1, const point<T>& p2 )
> {
> return size<T>( p1.x() - p2.x(), p1.y() - p2.y() );
> }
>
> // comparisons
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> bool
> operator ==( const point<T>& p1, const point<T>& p2 )
> {
> return p1.x() == p2.x() && p1.y() == p2.y();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> bool
> operator <( const point<T>& p1, const point<T>& p2 )
> {
> return p1.x() < p2.x() && p1.y() < p2.y();
> }
>
> // stream output
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> std::ostream&
> operator<< ( std::ostream& stream, const point<T>& p )
> {
> return ( stream << "(" << p.x() << ", " << p.y() << ")" );
> }
>
> ////////////////////////////////////////////////////////////////////////////
> /
> // implementation geometry::rect
>
> // constructors
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::rect()
> {
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::rect( unit_type left, unit_type top, unit_type right, unit_type
> bottom )
> : origin_( left, top )
> , corner_( right, bottom )
> {
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::rect( const point<unit_type>& origin, const point<unit_type>&
> corner )
> : origin_( origin )
> , corner_( corner )
> {
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::rect( const point<unit_type>& origin, const
> geometry::size<unit_type>& rect_size )
> : origin_( origin )
> , corner_( origin + rect_size )
> {
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::rect( const point<unit_type>& origin )
> : origin_( origin )
> , corner_( origin )
> {
> }
>
> // copying
> //--------------------------------------------------------------------------
> -
> //template<class T>
> //template<class U>
> //inline
> //rect<T>::rect( const rect<U>& other )
> // : origin_( other.origin() )
> // , corner_( other.corner() )
> // {
> // }
>
> //--------------------------------------------------------------------------
> -
> //template<class T>
> //template<class U>
> //inline
> //rect<T>&
> //rect<T>::operator =( const rect<U>& other )
> // {
> // origin_ = other.origin();
> // corner_ = other.corner();
> // return *this;
> // }
>
> // accessors - const
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> const point<rect<T>::unit_type>&
> rect<T>::origin() const
> {
> return origin_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> const point<rect<T>::unit_type>&
> rect<T>::corner() const
> {
> return corner_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> const point<rect<T>::unit_type>&
> rect<T>::left_top() const
> {
> return origin();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> const point<rect<T>::unit_type>&
> rect<T>::right_bottom() const
> {
> return corner();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::unit_type
> rect<T>::left() const
> {
> return origin().x();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::unit_type
> rect<T>::top() const
> {
> return origin().y();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::unit_type
> rect<T>::right() const
> {
> return corner().x();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::unit_type
> rect<T>::bottom() const
> {
> return corner().y();
> }
>
> // accessors - mutable
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<rect<T>::unit_type>&
> rect<T>::origin()
> {
> return origin_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<rect<T>::unit_type>&
> rect<T>::corner()
> {
> return corner_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<rect<T>::unit_type>&
> rect<T>::left_top()
> {
> return origin();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> point<rect<T>::unit_type>&
> rect<T>::right_bottom()
> {
> return corner();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::unit_type&
> rect<T>::left()
> {
> return origin().x();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::unit_type&
> rect<T>::top()
> {
> return origin().y();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::unit_type&
> rect<T>::right()
> {
> return corner().x();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::unit_type&
> rect<T>::bottom()
> {
> return corner().y();
> }
>
> // 'set' methods
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::set( unit_type left, unit_type top, unit_type right, unit_type
> bottom )
> {
> origin_.set( left, top );
> corner_.set( right, bottom );
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::set( const point<unit_type>& origin, const point<unit_type>& corner
> )
> {
> origin_ = origin;
> corner_ = corner;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::set( const point<unit_type>& origin, const
> geometry::size<unit_type>& rest_size )
> {
> origin_ = origin;
> corner_ = origin + rect_size;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::set_empty()
> {
> corner_ = origin_;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::normalize()
> {
This is surprising. I expected "normalize" to move the top_left to 0,0 for
some reason.
You could easily arrange for rectangles to always be normalized. I believe
that to be a superior arrangement. Rectangles created un-normalized would be
made purely empty at construction time (bottom_right = top_left). This makes
everything else much simpler.
> if ( left() > right() ) std::swap( left(), right() );
> if ( top() > bottom() ) std::swap( top(), bottom() );
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::origin( const point<unit_type>& new_origin )
> {
> origin_ = new_origin;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::corner( const point<unit_type>& new_corner )
> {
> conrner_ = new_corner;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::left_top( const point<unit_type>& new_left_top )
> {
> return origin( new_left_top );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::right_bottom( const point<unit_type>& new_right_bottom )
> {
> return corner( new_right_bottom );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::left( unit_type new_left )
> {
> left() = new_left;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::top( unit_type new_top )
> {
> top() = new_top;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::right( unit_type new_right )
> {
> right() = new_right;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::bottom( unit_type new_bottom )
> {
> bottom() = new_bottom;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::size( const geometry::size<unit_type>& new_size )
> {
> corner_ = origin_ + new_size;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::width( unit_type new_width )
> {
> right() = left() + new_width;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::height( unit_type new_height )
> {
> bottom() = top() + new_height;
> return *this;
> }
>
> // computed attributes
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> const point<rect<T>::unit_type>
> rect<T>::left_bottom() const
> {
> return point<unit_type>( left(), bottom() );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> const point<rect<T>::unit_type>
> rect<T>::right_top() const
> {
> return point<unit_type>( right(), top() );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> const size<rect<T>::unit_type>
> rect<T>::size() const
> {
> return corner_ - origin_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> const point<rect<T>::unit_type>
> rect<T>::center() const
> {
> return origin_ + size() / 2;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::unit_type
> rect<T>::width() const
> {
> return right() - left();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>::unit_type
> rect<T>::height() const
> {
> return bottom() - top();
> }
>
> // moving
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::move_by( const geometry::size<unit_type>& offset )
> {
> origin_ += offset;
> corner_ += offset;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::move_to( const point<unit_type>& new_origin )
> {
> return move_by( new_origin - origin_ );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::operator +=( const geometry::size<unit_type>& offset )
> {
> return move_by( offset );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::operator -=( const geometry::size<unit_type>& offset )
> {
> return move_by( -offset );
> }
>
> // scaling
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::operator *=( const geometry::size<unit_type>& scale )
> {
> origin_ *= scale;
> corner_ *= scale;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::operator /=( const geometry::size<unit_type>& scale )
> {
> origin_ /= scale;
> corner_ /= scale;
> return *this;
> }
>
> // inflating/deflating
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::inflate( unit_type x_delta, unit_type y_delta )
> {
> left() -= x_delta;
> top() -= y_delta;
> right() += x_delta;
> bottom() += y_delta;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::inflate( unit_type delta )
> {
> return inflate( delta, delta );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::inflate( const geometry::size<unit_type>& delta )
> {
> return inflate( delta.width(), delta.height() );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::inflate_size( unit_type x_delta, unit_type y_delta )
> {
> right() += x_delta;
> bottom() += y_delta;
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::inflate_size( unit_type delta )
> {
> return inflate_size( delta, delta );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::inflate_size( const geometry::size<unit_type>& delta )
> {
> return inflate_size( delta.width(), delta.height() );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::deflate( unit_type delta )
> {
> return inflate( -delta );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::deflate( unit_type x_delta, unit_type y_delta )
> {
> return inflate( -x_delta, -y_delta );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::deflate( const geometry::size<unit_type>& delta )
> {
> return inflate( -delta );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::deflate_size( unit_type delta )
> {
> return inflate_size( -delta );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::deflate_size( unit_type x_delta, unit_type y_delta )
> {
> return inflate_size( -x_delta, -y_delta );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::deflate_size( const geometry::size<unit_type>& delta )
> {
> return inflate_size( -delta );
> }
>
> // aligning
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::align( unit_type granule )
> {
> origin_.align( granule );
> return corner_.move_by( granule - 1 ).align( granule );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::align( const geometry::size<unit_type>& granule )
> {
> origin_.align( granule );
> return corner_
> .move_by( granule.width() - 1, granule.height() - 1 ) )
> .align( granule )
> ;
> }
>
> // testing
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> bool
> rect<T>::is_empty() const
> {
> return origin_ == corner_;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> int
> rect<T>::hit_test( const point<unit_type>& p ) const
> {
> // return a bitmask created by the following sheme
> // (0000 - point inside rect):
> //
> // 1010 | 0010 | 0110
> // -----+------+-----
> // 1000 | 0000 | 0100
> // -----+------+-----
> // 1001 | 0001 | 0101
> //
> return ( ( p.x() < left() ) << 3 )
> | ( ( p.x() > right() ) << 2 )
> | ( ( p.y() < top() ) << 1 )
> | ( ( p.y() > bottom() ) )
> ;
> }
The utility of this is completely un-obvious to me. Also I think it contains
fencepost errors. The point (0,0) should be inside the rectangle(0,0,1,1)
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> bool
> rect<T>::inside( const rect<unit_type>& other ) const
> {
> return left() >= other.left()
> && top() >= other.top()
> && right() <= other.right()
> && bottom() <= other.bottom()
> ;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> bool
> rect<T>::contains( const point<unit_type>& p ) const
> {
> return !( p.x() < left()
> || p.x() >= right()
> || p.y() < top()
> || p.y() >= bottom()
> );
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> bool
> rect<T>::contains( const rect<unit_type>& other ) const
> {
> return other.left() >= left()
> && other.top() >= top()
> && other.right() <= right()
> && other.bottom() <= bottom()
> ;
> }
>
> // unionization / intersection
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::operator |=( const rect<unit_type>& other )
> {
> origin_ = std::min( origin_, other.origin_ );
> corner_ = std::max( corner_, other.corner_ );
> return *this;
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> inline
> rect<T>&
> rect<T>::operator &=( const rect<unit_type>& other )
> {
> origin_ = std::max( origin_, other.origin_ );
> corner_ = std::min( corner_, other.corner_ );
> if ( left() > right() || top() > bottom() ) set_empty();
> return *this;
> }
>
> // rect comparison/intersection test
> //--------------------------------------------------------------------------
> -
> template<class T>
> bool
> operator ==( const rect<T>& r1, const rect<T>& r2 )
> {
> return r1.origin() == r2.origin() && r1.corner() == r2.corner();
> }
>
> //--------------------------------------------------------------------------
> -
> template<class T>
> bool
> operator &( const rect<T>& r1, const rect<T>& r2 )
> {
> return !( r2.left() >= r1.right()
> || r2.top() >= r1.bottom()
> || r2.right() <= r1.left()
> || r2.bottom() <= r1.top()
> );
> }
Huh?!?! If operator&= does intersection, then so should the free operator&!
This is terribly surprising!
> // rect stream output
> //--------------------------------------------------------------------------
> -
> template<class T>
> std::ostream&
> operator <<( std::ostream& stream, const rect<T>& r )
> {
> return stream << r.origin() << "-" << r.corner();
> }
>
> } // namespace geometry
>
> } // namespace boost
>
> #endif // #ifndef GEOMETRY_HPP
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk