You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

528 lines
16 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  5. * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors.
  6. * Copyright (C) 2013 CERN
  7. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #ifndef __BOX2_H
  27. #define __BOX2_H
  28. #include <math/vector2d.h>
  29. #include <limits>
  30. #include <core/optional.h>
  31. /**
  32. * Class BOX2
  33. * handles a 2-D bounding box, built on top of an origin point
  34. * and size vector, both of templated class Vec
  35. */
  36. template <class Vec>
  37. class BOX2
  38. {
  39. private:
  40. Vec m_Pos; // Rectangle Origin
  41. Vec m_Size; // Rectangle Size
  42. public:
  43. typedef typename Vec::coord_type coord_type;
  44. typedef typename Vec::extended_type ecoord_type;
  45. typedef std::numeric_limits<coord_type> coord_limits;
  46. BOX2() {};
  47. BOX2( const Vec& aPos, const Vec& aSize ) :
  48. m_Pos( aPos ),
  49. m_Size( aSize )
  50. {
  51. Normalize();
  52. }
  53. #ifdef WX_COMPATIBILITY
  54. /// Constructor with a wxRect as argument
  55. BOX2( const wxRect& aRect ) :
  56. m_Pos( aRect.GetPosition() ),
  57. m_Size( aRect.GetSize() )
  58. {
  59. Normalize();
  60. }
  61. #endif
  62. void SetMaximum()
  63. {
  64. m_Pos.x = m_Pos.y = coord_limits::lowest() / 2 + coord_limits::epsilon();
  65. m_Size.x = m_Size.y = coord_limits::max() - coord_limits::epsilon();
  66. }
  67. Vec Centre() const
  68. {
  69. return Vec( m_Pos.x + ( m_Size.x / 2 ),
  70. m_Pos.y + ( m_Size.y / 2 ) );
  71. }
  72. /**
  73. * @brief Compute the bounding box from a given list of points.
  74. *
  75. * @param aPointList is the list points of the object.
  76. */
  77. template <class Container>
  78. void Compute( const Container& aPointList )
  79. {
  80. Vec vmin, vmax;
  81. typename Container::const_iterator i;
  82. if( !aPointList.size() )
  83. return;
  84. vmin = vmax = aPointList[0];
  85. for( i = aPointList.begin(); i != aPointList.end(); ++i )
  86. {
  87. Vec p( *i );
  88. vmin.x = std::min( vmin.x, p.x );
  89. vmin.y = std::min( vmin.y, p.y );
  90. vmax.x = std::max( vmax.x, p.x );
  91. vmax.y = std::max( vmax.y, p.y );
  92. }
  93. SetOrigin( vmin );
  94. SetSize( vmax - vmin );
  95. }
  96. /**
  97. * Function Move
  98. * moves the rectangle by the \a aMoveVector.
  99. * @param aMoveVector A point that is the value to move this rectangle
  100. */
  101. void Move( const Vec& aMoveVector )
  102. {
  103. m_Pos += aMoveVector;
  104. }
  105. /**
  106. * Function Normalize
  107. * ensures that the height ant width are positive.
  108. */
  109. BOX2<Vec>& Normalize()
  110. {
  111. if( m_Size.y < 0 )
  112. {
  113. m_Size.y = -m_Size.y;
  114. m_Pos.y -= m_Size.y;
  115. }
  116. if( m_Size.x < 0 )
  117. {
  118. m_Size.x = -m_Size.x;
  119. m_Pos.x -= m_Size.x;
  120. }
  121. return *this;
  122. }
  123. /**
  124. * Function Contains
  125. * @param aPoint = the point to test
  126. * @return true if aPoint is inside the boundary box. A point on a edge is seen as inside
  127. */
  128. bool Contains( const Vec& aPoint ) const
  129. {
  130. Vec rel_pos = aPoint - m_Pos;
  131. Vec size = m_Size;
  132. if( size.x < 0 )
  133. {
  134. size.x = -size.x;
  135. rel_pos.x += size.x;
  136. }
  137. if( size.y < 0 )
  138. {
  139. size.y = -size.y;
  140. rel_pos.y += size.y;
  141. }
  142. return ( rel_pos.x >= 0 ) && ( rel_pos.y >= 0 ) && ( rel_pos.y <= size.y) && ( rel_pos.x <= size.x);
  143. }
  144. /**
  145. * Function Contains
  146. * @param x = the x coordinate of the point to test
  147. * @param y = the x coordinate of the point to test
  148. * @return true if point is inside the boundary box. A point on a edge is seen as inside
  149. */
  150. bool Contains( coord_type x, coord_type y ) const { return Contains( Vec( x, y ) ); }
  151. /**
  152. * Function Contains
  153. * @param aRect = the BOX2 to test
  154. * @return true if aRect is Contained. A common edge is seen as contained
  155. */
  156. bool Contains( const BOX2<Vec>& aRect ) const
  157. {
  158. return Contains( aRect.GetOrigin() ) && Contains( aRect.GetEnd() );
  159. }
  160. const Vec& GetSize() const { return m_Size; }
  161. coord_type GetX() const { return m_Pos.x; }
  162. coord_type GetY() const { return m_Pos.y; }
  163. const Vec& GetOrigin() const { return m_Pos; }
  164. const Vec& GetPosition() const { return m_Pos; }
  165. const Vec GetEnd() const { return Vec( GetRight(), GetBottom() ); }
  166. coord_type GetWidth() const { return m_Size.x; }
  167. coord_type GetHeight() const { return m_Size.y; }
  168. coord_type GetRight() const { return m_Pos.x + m_Size.x; }
  169. coord_type GetBottom() const { return m_Pos.y + m_Size.y; }
  170. // Compatibility aliases
  171. coord_type GetLeft() const { return GetX(); }
  172. coord_type GetTop() const { return GetY(); }
  173. void MoveTopTo( coord_type aTop ) { m_Pos.y = aTop; }
  174. void MoveBottomTo( coord_type aBottom ) { m_Size.y = aBottom - m_Pos.y; }
  175. void MoveLeftTo( coord_type aLeft ) { m_Pos.x = aLeft; }
  176. void MoveRightTo( coord_type aRight ) { m_Size.x = aRight - m_Pos.x; }
  177. void SetOrigin( const Vec& pos ) { m_Pos = pos; }
  178. void SetOrigin( coord_type x, coord_type y ) { m_Pos.x = x; m_Pos.y = y; }
  179. void SetSize( const Vec& size ) { m_Size = size; }
  180. void SetSize( coord_type w, coord_type h ) { m_Size.x = w; m_Size.y = h; }
  181. void Offset( coord_type dx, coord_type dy ) { m_Pos.x += dx; m_Pos.y += dy; }
  182. void Offset( const Vec& offset )
  183. {
  184. m_Pos.x += offset.x; m_Pos.y +=
  185. offset.y;
  186. }
  187. void SetX( coord_type val ) { m_Pos.x = val; }
  188. void SetY( coord_type val ) { m_Pos.y = val; }
  189. void SetWidth( coord_type val ) { m_Size.x = val; }
  190. void SetHeight( coord_type val ) { m_Size.y = val; }
  191. void SetEnd( coord_type x, coord_type y ) { SetEnd( Vec( x, y ) ); }
  192. void SetEnd( const Vec& pos )
  193. {
  194. m_Size.x = pos.x - m_Pos.x; m_Size.y = pos.y - m_Pos.y;
  195. }
  196. /**
  197. * Function Intersects
  198. * @return bool - true if the argument rectangle intersects this rectangle.
  199. * (i.e. if the 2 rectangles have at least a common point)
  200. */
  201. bool Intersects( const BOX2<Vec>& aRect ) const
  202. {
  203. // this logic taken from wxWidgets' geometry.cpp file:
  204. bool rc;
  205. BOX2<Vec> me( *this );
  206. BOX2<Vec> rect( aRect );
  207. me.Normalize(); // ensure size is >= 0
  208. rect.Normalize(); // ensure size is >= 0
  209. // calculate the left common area coordinate:
  210. int left = std::max( me.m_Pos.x, rect.m_Pos.x );
  211. // calculate the right common area coordinate:
  212. int right = std::min( me.m_Pos.x + me.m_Size.x, rect.m_Pos.x + rect.m_Size.x );
  213. // calculate the upper common area coordinate:
  214. int top = std::max( me.m_Pos.y, aRect.m_Pos.y );
  215. // calculate the lower common area coordinate:
  216. int bottom = std::min( me.m_Pos.y + me.m_Size.y, rect.m_Pos.y + rect.m_Size.y );
  217. // if a common area exists, it must have a positive (null accepted) size
  218. if( left <= right && top <= bottom )
  219. rc = true;
  220. else
  221. rc = false;
  222. return rc;
  223. }
  224. /**
  225. * Function Intersect
  226. * Returns the intersection of this with another rectangle.
  227. */
  228. BOX2<Vec> Intersect( const BOX2<Vec>& aRect )
  229. {
  230. BOX2<Vec> me( *this );
  231. BOX2<Vec> rect( aRect );
  232. me.Normalize(); // ensure size is >= 0
  233. rect.Normalize(); // ensure size is >= 0
  234. Vec topLeft, bottomRight;
  235. topLeft.x = std::max( me.m_Pos.x, rect.m_Pos.x );
  236. bottomRight.x = std::min( me.m_Pos.x + me.m_Size.x, rect.m_Pos.x + rect.m_Size.x );
  237. topLeft.y = std::max( me.m_Pos.y, rect.m_Pos.y );
  238. bottomRight.y = std::min( me.m_Pos.y + me.m_Size.y, rect.m_Pos.y + rect.m_Size.y );
  239. if ( topLeft.x < bottomRight.x && topLeft.y < bottomRight.y )
  240. return BOX2<Vec>( topLeft, bottomRight - topLeft );
  241. else
  242. return BOX2<Vec>( Vec( 0, 0 ), Vec( 0, 0 ) );
  243. }
  244. const std::string Format() const
  245. {
  246. std::stringstream ss;
  247. ss << "( box corner " << m_Pos.Format() << " w " << m_Size.x << " h " << m_Size.y << " )";
  248. return ss.str();
  249. }
  250. /**
  251. * Function Inflate
  252. * inflates the rectangle horizontally by \a dx and vertically by \a dy. If \a dx
  253. * and/or \a dy is negative the rectangle is deflated.
  254. */
  255. BOX2<Vec>& Inflate( coord_type dx, coord_type dy )
  256. {
  257. if( m_Size.x >= 0 )
  258. {
  259. if( m_Size.x < -2 * dx )
  260. {
  261. // Don't allow deflate to eat more width than we have,
  262. m_Pos.x += m_Size.x / 2;
  263. m_Size.x = 0;
  264. }
  265. else
  266. {
  267. // The inflate is valid.
  268. m_Pos.x -= dx;
  269. m_Size.x += 2 * dx;
  270. }
  271. }
  272. else // size.x < 0:
  273. {
  274. if( m_Size.x > -2 * dx )
  275. {
  276. // Don't allow deflate to eat more width than we have,
  277. m_Pos.x -= m_Size.x / 2;
  278. m_Size.x = 0;
  279. }
  280. else
  281. {
  282. // The inflate is valid.
  283. m_Pos.x += dx;
  284. m_Size.x -= 2 * dx; // m_Size.x <0: inflate when dx > 0
  285. }
  286. }
  287. if( m_Size.y >= 0 )
  288. {
  289. if( m_Size.y < -2 * dy )
  290. {
  291. // Don't allow deflate to eat more height than we have,
  292. m_Pos.y += m_Size.y / 2;
  293. m_Size.y = 0;
  294. }
  295. else
  296. {
  297. // The inflate is valid.
  298. m_Pos.y -= dy;
  299. m_Size.y += 2 * dy;
  300. }
  301. }
  302. else // size.y < 0:
  303. {
  304. if( m_Size.y > 2 * dy )
  305. {
  306. // Don't allow deflate to eat more height than we have,
  307. m_Pos.y -= m_Size.y / 2;
  308. m_Size.y = 0;
  309. }
  310. else
  311. {
  312. // The inflate is valid.
  313. m_Pos.y += dy;
  314. m_Size.y -= 2 * dy; // m_Size.y <0: inflate when dy > 0
  315. }
  316. }
  317. return *this;
  318. }
  319. /**
  320. * Function Inflate
  321. * inflates the rectangle horizontally and vertically by \a aDelta. If \a aDelta
  322. * is negative the rectangle is deflated.
  323. */
  324. BOX2<Vec>& Inflate( int aDelta )
  325. {
  326. Inflate( aDelta, aDelta );
  327. return *this;
  328. }
  329. /**
  330. * Function Merge
  331. * modifies the position and size of the rectangle in order to contain \a aRect. It is
  332. * mainly used to calculate bounding boxes.
  333. * @param aRect The rectangle to merge with this rectangle.
  334. */
  335. BOX2<Vec>& Merge( const BOX2<Vec>& aRect )
  336. {
  337. Normalize(); // ensure width and height >= 0
  338. BOX2<Vec> rect = aRect;
  339. rect.Normalize(); // ensure width and height >= 0
  340. Vec end = GetEnd();
  341. Vec rect_end = rect.GetEnd();
  342. // Change origin and size in order to contain the given rect
  343. m_Pos.x = std::min( m_Pos.x, rect.m_Pos.x );
  344. m_Pos.y = std::min( m_Pos.y, rect.m_Pos.y );
  345. end.x = std::max( end.x, rect_end.x );
  346. end.y = std::max( end.y, rect_end.y );
  347. SetEnd( end );
  348. return *this;
  349. }
  350. /**
  351. * Function Merge
  352. * modifies the position and size of the rectangle in order to contain the given point.
  353. * @param aPoint The point to merge with the rectangle.
  354. */
  355. BOX2<Vec>& Merge( const Vec& aPoint )
  356. {
  357. Normalize(); // ensure width and height >= 0
  358. Vec end = GetEnd();
  359. // Change origin and size in order to contain the given rect
  360. m_Pos.x = std::min( m_Pos.x, aPoint.x );
  361. m_Pos.y = std::min( m_Pos.y, aPoint.y );
  362. end.x = std::max( end.x, aPoint.x );
  363. end.y = std::max( end.y, aPoint.y );
  364. SetEnd( end );
  365. return *this;
  366. }
  367. /**
  368. * Function GetArea
  369. * returns the area of the rectangle.
  370. * @return The area of the rectangle.
  371. */
  372. ecoord_type GetArea() const
  373. {
  374. return (ecoord_type) GetWidth() * (ecoord_type) GetHeight();
  375. }
  376. /**
  377. * Function GetArea
  378. * returns the length of the diagonal of the rectangle.
  379. * @return The area of the diagonal.
  380. */
  381. ecoord_type Diagonal() const
  382. {
  383. return m_Size.EuclideanNorm();
  384. }
  385. ecoord_type SquaredDistance( const Vec& aP ) const
  386. {
  387. ecoord_type x2 = m_Pos.x + m_Size.x;
  388. ecoord_type y2 = m_Pos.y + m_Size.y;
  389. ecoord_type xdiff = std::max( aP.x < m_Pos.x ? m_Pos.x - aP.x : m_Pos.x - x2, (ecoord_type) 0 );
  390. ecoord_type ydiff = std::max( aP.y < m_Pos.y ? m_Pos.y - aP.y : m_Pos.y - y2, (ecoord_type) 0 );
  391. return xdiff * xdiff + ydiff * ydiff;
  392. }
  393. ecoord_type Distance( const Vec& aP ) const
  394. {
  395. return sqrt( SquaredDistance( aP ) );
  396. }
  397. /**
  398. * Function SquaredDistance
  399. * returns the square of the minimum distance between self and box aBox
  400. * @param aBox: the other box
  401. * @return The distance, squared
  402. */
  403. ecoord_type SquaredDistance( const BOX2<Vec>& aBox ) const
  404. {
  405. ecoord_type s = 0;
  406. if( aBox.m_Pos.x + aBox.m_Size.x < m_Pos.x )
  407. {
  408. ecoord_type d = aBox.m_Pos.x + aBox.m_Size.x - m_Pos.x;
  409. s += d * d;
  410. }
  411. else if( aBox.m_Pos.x > m_Pos.x + m_Size.x )
  412. {
  413. ecoord_type d = aBox.m_Pos.x - m_Size.x - m_Pos.x;
  414. s += d * d;
  415. }
  416. if( aBox.m_Pos.y + aBox.m_Size.y < m_Pos.y )
  417. {
  418. ecoord_type d = aBox.m_Pos.y + aBox.m_Size.y - m_Pos.y;
  419. s += d * d;
  420. }
  421. else if( aBox.m_Pos.y > m_Pos.y + m_Size.y )
  422. {
  423. ecoord_type d = aBox.m_Pos.y - m_Size.y - m_Pos.y;
  424. s += d * d;
  425. }
  426. return s;
  427. }
  428. /**
  429. * Function Distance
  430. * returns the minimum distance between self and box aBox
  431. * @param aBox: the other box
  432. * @return The distance
  433. */
  434. ecoord_type Distance( const BOX2<Vec>& aBox ) const
  435. {
  436. return sqrt( SquaredDistance( aBox ) );
  437. }
  438. bool operator==( const BOX2<Vec>& aOther ) const
  439. {
  440. auto t1 ( *this );
  441. auto t2 ( aOther );
  442. t1.Normalize();
  443. t2.Normalize();
  444. return ( t1.m_Pos == t2.m_Pos && t1.m_Size == t2.m_Size );
  445. }
  446. bool operator!=( const BOX2<Vec>& aOther ) const
  447. {
  448. auto t1 ( *this );
  449. auto t2 ( aOther );
  450. t1.Normalize();
  451. t2.Normalize();
  452. return ( t1.m_Pos != t2.m_Pos || t1.m_Size != t2.m_Size );
  453. }
  454. };
  455. /* Default specializations */
  456. typedef BOX2<VECTOR2I> BOX2I;
  457. typedef BOX2<VECTOR2D> BOX2D;
  458. typedef OPT<BOX2I> OPT_BOX2I;
  459. // FIXME should be removed to avoid multiple typedefs for the same type
  460. typedef BOX2D DBOX;
  461. #endif