summaryrefslogtreecommitdiff
path: root/include/math/box2.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/math/box2.h')
-rw-r--r--include/math/box2.h476
1 files changed, 476 insertions, 0 deletions
diff --git a/include/math/box2.h b/include/math/box2.h
new file mode 100644
index 0000000..5bf6ddd
--- /dev/null
+++ b/include/math/box2.h
@@ -0,0 +1,476 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
+ * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors.
+ * Copyright (C) 2013 CERN
+ * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef __BOX2_H
+#define __BOX2_H
+
+#include <math/vector2d.h>
+#include <limits>
+
+#include <boost/optional.hpp>
+
+/**
+ * Class BOX2
+ * handles a 2-D bounding box, built on top of an origin point
+ * and size vector, both of templated class Vec
+ */
+template <class Vec>
+class BOX2
+{
+private:
+ Vec m_Pos; // Rectangle Origin
+ Vec m_Size; // Rectangle Size
+
+public:
+ typedef typename Vec::coord_type coord_type;
+ typedef typename Vec::extended_type ecoord_type;
+ typedef typename std::numeric_limits<coord_type> coord_limits;
+
+ BOX2() {};
+
+ BOX2( const Vec& aPos, const Vec& aSize ) :
+ m_Pos( aPos ),
+ m_Size( aSize )
+ {
+ Normalize();
+ }
+
+ void SetMaximum()
+ {
+ m_Pos.x = m_Pos.y = coord_limits::min() / 2 + coord_limits::epsilon();
+ m_Size.x = m_Size.y = coord_limits::max() - coord_limits::epsilon();
+ }
+
+ Vec Centre() const
+ {
+ return Vec( m_Pos.x + ( m_Size.x / 2 ),
+ m_Pos.y + ( m_Size.y / 2 ) );
+ }
+
+ /**
+ * @brief Compute the bounding box from a given list of points.
+ *
+ * @param aPointList is the list points of the object.
+ */
+ template <class Container>
+ void Compute( const Container& aPointList )
+ {
+ Vec vmin, vmax;
+
+ typename Container::const_iterator i;
+
+ if( !aPointList.size() )
+ return;
+
+ vmin = vmax = aPointList[0];
+
+ for( i = aPointList.begin(); i != aPointList.end(); ++i )
+ {
+ Vec p( *i );
+ vmin.x = std::min( vmin.x, p.x );
+ vmin.y = std::min( vmin.y, p.y );
+ vmax.x = std::max( vmax.x, p.x );
+ vmax.y = std::max( vmax.y, p.y );
+ }
+
+ SetOrigin( vmin );
+ SetSize( vmax - vmin );
+ }
+
+ /**
+ * Function Move
+ * moves the rectangle by the \a aMoveVector.
+ * @param aMoveVector A point that is the value to move this rectangle
+ */
+ void Move( const Vec& aMoveVector )
+ {
+ m_Pos += aMoveVector;
+ }
+
+ /**
+ * Function Normalize
+ * ensures that the height ant width are positive.
+ */
+ BOX2<Vec>& Normalize()
+ {
+ if( m_Size.y < 0 )
+ {
+ m_Size.y = -m_Size.y;
+ m_Pos.y -= m_Size.y;
+ }
+
+ if( m_Size.x < 0 )
+ {
+ m_Size.x = -m_Size.x;
+ m_Pos.x -= m_Size.x;
+ }
+
+ return *this;
+ }
+
+ /**
+ * Function Contains
+ * @param aPoint = the point to test
+ * @return true if aPoint is inside the boundary box. A point on a edge is seen as inside
+ */
+ bool Contains( const Vec& aPoint ) const
+ {
+ Vec rel_pos = aPoint - m_Pos;
+ Vec size = m_Size;
+
+ if( size.x < 0 )
+ {
+ size.x = -size.x;
+ rel_pos.x += size.x;
+ }
+
+ if( size.y < 0 )
+ {
+ size.y = -size.y;
+ rel_pos.y += size.y;
+ }
+
+ return ( rel_pos.x >= 0 ) && ( rel_pos.y >= 0 ) && ( rel_pos.y <= size.y) && ( rel_pos.x <= size.x);
+ }
+
+ /**
+ * Function Contains
+ * @param x = the x coordinate of the point to test
+ * @param y = the x coordinate of the point to test
+ * @return true if point is inside the boundary box. A point on a edge is seen as inside
+ */
+ bool Contains( coord_type x, coord_type y ) const { return Contains( Vec( x, y ) ); }
+
+ /**
+ * Function Contains
+ * @param aRect = the BOX2 to test
+ * @return true if aRect is Contained. A common edge is seen as contained
+ */
+ bool Contains( const BOX2<Vec>& aRect ) const
+ {
+ return Contains( aRect.GetOrigin() ) && Contains( aRect.GetEnd() );
+ }
+
+ const Vec& GetSize() const { return m_Size; }
+ coord_type GetX() const { return m_Pos.x; }
+ coord_type GetY() const { return m_Pos.y; }
+
+ const Vec& GetOrigin() const { return m_Pos; }
+ const Vec& GetPosition() const { return m_Pos; }
+ const Vec GetEnd() const { return Vec( GetRight(), GetBottom() ); }
+
+ coord_type GetWidth() const { return m_Size.x; }
+ coord_type GetHeight() const { return m_Size.y; }
+ coord_type GetRight() const { return m_Pos.x + m_Size.x; }
+ coord_type GetBottom() const { return m_Pos.y + m_Size.y; }
+
+ // Compatibility aliases
+ coord_type GetLeft() const { return GetX(); }
+ coord_type GetTop() const { return GetY(); }
+ void MoveTopTo( coord_type aTop ) { m_Pos.y = aTop; }
+ void MoveBottomTo( coord_type aBottom ) { m_Size.y = aBottom - m_Pos.y; }
+ void MoveLeftTo( coord_type aLeft ) { m_Pos.x = aLeft; }
+ void MoveRightTo( coord_type aRight ) { m_Size.x = aRight - m_Pos.x; }
+
+ void SetOrigin( const Vec& pos ) { m_Pos = pos; }
+ void SetOrigin( coord_type x, coord_type y ) { m_Pos.x = x; m_Pos.y = y; }
+ void SetSize( const Vec& size ) { m_Size = size; }
+ void SetSize( coord_type w, coord_type h ) { m_Size.x = w; m_Size.y = h; }
+ void Offset( coord_type dx, coord_type dy ) { m_Pos.x += dx; m_Pos.y += dy; }
+ void Offset( const Vec& offset )
+ {
+ m_Pos.x += offset.x; m_Pos.y +=
+ offset.y;
+ }
+
+ void SetX( coord_type val ) { m_Pos.x = val; }
+ void SetY( coord_type val ) { m_Pos.y = val; }
+ void SetWidth( coord_type val ) { m_Size.x = val; }
+ void SetHeight( coord_type val ) { m_Size.y = val; }
+ void SetEnd( coord_type x, coord_type y ) { SetEnd( Vec( x, y ) ); }
+ void SetEnd( const Vec& pos )
+ {
+ m_Size.x = pos.x - m_Pos.x; m_Size.y = pos.y - m_Pos.y;
+ }
+
+ /**
+ * Function Intersects
+ * @return bool - true if the argument rectangle intersects this rectangle.
+ * (i.e. if the 2 rectangles have at least a common point)
+ */
+ bool Intersects( const BOX2<Vec>& aRect ) const
+ {
+ // this logic taken from wxWidgets' geometry.cpp file:
+ bool rc;
+
+ BOX2<Vec> me( *this );
+ BOX2<Vec> rect( aRect );
+ me.Normalize(); // ensure size is >= 0
+ rect.Normalize(); // ensure size is >= 0
+
+ // calculate the left common area coordinate:
+ int left = std::max( me.m_Pos.x, rect.m_Pos.x );
+ // calculate the right common area coordinate:
+ int right = std::min( me.m_Pos.x + me.m_Size.x, rect.m_Pos.x + rect.m_Size.x );
+ // calculate the upper common area coordinate:
+ int top = std::max( me.m_Pos.y, aRect.m_Pos.y );
+ // calculate the lower common area coordinate:
+ int bottom = std::min( me.m_Pos.y + me.m_Size.y, rect.m_Pos.y + rect.m_Size.y );
+
+ // if a common area exists, it must have a positive (null accepted) size
+ if( left <= right && top <= bottom )
+ rc = true;
+ else
+ rc = false;
+
+ return rc;
+ }
+
+ const std::string Format() const
+ {
+ std::stringstream ss;
+
+ ss << "( box corner " << m_Pos.Format() << " w " << m_Size.x << " h " << m_Size.y << " )";
+
+ return ss.str();
+ }
+
+ /**
+ * Function Inflate
+ * inflates the rectangle horizontally by \a dx and vertically by \a dy. If \a dx
+ * and/or \a dy is negative the rectangle is deflated.
+ */
+ BOX2<Vec>& Inflate( coord_type dx, coord_type dy )
+ {
+ if( m_Size.x >= 0 )
+ {
+ if( m_Size.x < -2 * dx )
+ {
+ // Don't allow deflate to eat more width than we have,
+ m_Pos.x += m_Size.x / 2;
+ m_Size.x = 0;
+ }
+ else
+ {
+ // The inflate is valid.
+ m_Pos.x -= dx;
+ m_Size.x += 2 * dx;
+ }
+ }
+ else // size.x < 0:
+ {
+ if( m_Size.x > -2 * dx )
+ {
+ // Don't allow deflate to eat more width than we have,
+ m_Pos.x -= m_Size.x / 2;
+ m_Size.x = 0;
+ }
+ else
+ {
+ // The inflate is valid.
+ m_Pos.x += dx;
+ m_Size.x -= 2 * dx; // m_Size.x <0: inflate when dx > 0
+ }
+ }
+
+ if( m_Size.y >= 0 )
+ {
+ if( m_Size.y < -2 * dy )
+ {
+ // Don't allow deflate to eat more height than we have,
+ m_Pos.y += m_Size.y / 2;
+ m_Size.y = 0;
+ }
+ else
+ {
+ // The inflate is valid.
+ m_Pos.y -= dy;
+ m_Size.y += 2 * dy;
+ }
+ }
+ else // size.y < 0:
+ {
+ if( m_Size.y > 2 * dy )
+ {
+ // Don't allow deflate to eat more height than we have,
+ m_Pos.y -= m_Size.y / 2;
+ m_Size.y = 0;
+ }
+ else
+ {
+ // The inflate is valid.
+ m_Pos.y += dy;
+ m_Size.y -= 2 * dy; // m_Size.y <0: inflate when dy > 0
+ }
+ }
+
+ return *this;
+ }
+
+ /**
+ * Function Inflate
+ * inflates the rectangle horizontally and vertically by \a aDelta. If \a aDelta
+ * is negative the rectangle is deflated.
+ */
+ BOX2<Vec>& Inflate( int aDelta )
+ {
+ Inflate( aDelta, aDelta );
+ return *this;
+ }
+
+ /**
+ * Function Merge
+ * modifies the position and size of the rectangle in order to contain \a aRect. It is
+ * mainly used to calculate bounding boxes.
+ * @param aRect The rectangle to merge with this rectangle.
+ */
+ BOX2<Vec>& Merge( const BOX2<Vec>& aRect )
+ {
+ Normalize(); // ensure width and height >= 0
+ BOX2<Vec> rect = aRect;
+ rect.Normalize(); // ensure width and height >= 0
+ Vec end = GetEnd();
+ Vec rect_end = rect.GetEnd();
+
+ // Change origin and size in order to contain the given rect
+ m_Pos.x = std::min( m_Pos.x, rect.m_Pos.x );
+ m_Pos.y = std::min( m_Pos.y, rect.m_Pos.y );
+ end.x = std::max( end.x, rect_end.x );
+ end.y = std::max( end.y, rect_end.y );
+ SetEnd( end );
+ return *this;
+ }
+
+ /**
+ * Function Merge
+ * modifies the position and size of the rectangle in order to contain the given point.
+ * @param aPoint The point to merge with the rectangle.
+ */
+ BOX2<Vec>& Merge( const Vec& aPoint )
+ {
+ Normalize(); // ensure width and height >= 0
+
+ Vec end = GetEnd();
+ // Change origin and size in order to contain the given rect
+ m_Pos.x = std::min( m_Pos.x, aPoint.x );
+ m_Pos.y = std::min( m_Pos.y, aPoint.y );
+ end.x = std::max( end.x, aPoint.x );
+ end.y = std::max( end.y, aPoint.y );
+ SetEnd( end );
+ return *this;
+ }
+
+ /**
+ * Function GetArea
+ * returns the area of the rectangle.
+ * @return The area of the rectangle.
+ */
+ ecoord_type GetArea() const
+ {
+ return (ecoord_type) GetWidth() * (ecoord_type) GetHeight();
+ }
+
+ /**
+ * Function GetArea
+ * returns the length of the diagonal of the rectangle.
+ * @return The area of the diagonal.
+ */
+ ecoord_type Diagonal() const
+ {
+ return m_Size.EuclideanNorm();
+ }
+
+ ecoord_type SquaredDistance( const Vec& aP ) const
+ {
+ ecoord_type x2 = m_Pos.x + m_Size.x;
+ ecoord_type y2 = m_Pos.y + m_Size.y;
+ ecoord_type xdiff = std::max( aP.x < m_Pos.x ? m_Pos.x - aP.x : m_Pos.x - x2, (ecoord_type) 0 );
+ ecoord_type ydiff = std::max( aP.y < m_Pos.y ? m_Pos.y - aP.y : m_Pos.y - y2, (ecoord_type) 0 );
+ return xdiff * xdiff + ydiff * ydiff;
+ }
+
+ ecoord_type Distance( const Vec& aP ) const
+ {
+ return sqrt( SquaredDistance( aP ) );
+ }
+
+ /**
+ * Function SquaredDistance
+ * returns the square of the minimum distance between self and box aBox
+ * @param aBox: the other box
+ * @return The distance, squared
+ */
+ ecoord_type SquaredDistance( const BOX2<Vec>& aBox ) const
+ {
+ ecoord_type s = 0;
+
+ if( aBox.m_Pos.x + aBox.m_Size.x < m_Pos.x )
+ {
+ ecoord_type d = aBox.m_Pos.x + aBox.m_Size.x - m_Pos.x;
+ s += d * d;
+ }
+ else if( aBox.m_Pos.x > m_Pos.x + m_Size.x )
+ {
+ ecoord_type d = aBox.m_Pos.x - m_Size.x - m_Pos.x;
+ s += d * d;
+ }
+
+ if( aBox.m_Pos.y + aBox.m_Size.y < m_Pos.y )
+ {
+ ecoord_type d = aBox.m_Pos.y + aBox.m_Size.y - m_Pos.y;
+ s += d * d;
+ }
+ else if( aBox.m_Pos.y > m_Pos.y + m_Size.y )
+ {
+ ecoord_type d = aBox.m_Pos.y - m_Size.y - m_Pos.y;
+ s += d * d;
+ }
+
+ return s;
+ }
+
+ /**
+ * Function Distance
+ * returns the minimum distance between self and box aBox
+ * @param aBox: the other box
+ * @return The distance
+ */
+ ecoord_type Distance( const BOX2<Vec>& aBox ) const
+ {
+ return sqrt( SquaredDistance( aBox ) );
+ }
+};
+
+/* Default specializations */
+typedef BOX2<VECTOR2I> BOX2I;
+typedef BOX2<VECTOR2D> BOX2D;
+
+typedef boost::optional<BOX2I> OPT_BOX2I;
+
+// FIXME should be removed to avoid multiple typedefs for the same type
+typedef BOX2D DBOX;
+
+#endif