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.

742 lines
21 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-2022 Kicad Developers, see AUTHORS.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 <limits>
  29. #include <algorithm>
  30. #include <optional>
  31. #include <math/vector2d.h>
  32. #include <geometry/eda_angle.h>
  33. #include <trigo.h>
  34. /**
  35. * A 2D bounding box built on top of an origin point and size vector.
  36. */
  37. template <class Vec>
  38. class BOX2
  39. {
  40. public:
  41. typedef typename Vec::coord_type coord_type;
  42. typedef typename Vec::extended_type ecoord_type;
  43. typedef std::numeric_limits<coord_type> coord_limits;
  44. BOX2() :
  45. m_Pos( 0, 0 ),
  46. m_Size( 0, 0 ),
  47. m_init( false )
  48. {};
  49. BOX2( const Vec& aPos, const Vec& aSize = Vec(0, 0) ) :
  50. m_Pos( aPos ),
  51. m_Size( aSize ),
  52. m_init( true )
  53. {
  54. Normalize();
  55. }
  56. void SetMaximum()
  57. {
  58. m_Pos.x = m_Pos.y = coord_limits::lowest() / 2 + coord_limits::epsilon();
  59. m_Size.x = m_Size.y = coord_limits::max() - coord_limits::epsilon();
  60. m_init = true;
  61. }
  62. Vec Centre() const
  63. {
  64. return Vec( m_Pos.x + ( m_Size.x / 2 ),
  65. m_Pos.y + ( m_Size.y / 2 ) );
  66. }
  67. /**
  68. * Compute the bounding box from a given list of points.
  69. *
  70. * @param aPointList is the list points of the object.
  71. */
  72. template <class Container>
  73. void Compute( const Container& aPointList )
  74. {
  75. Vec vmin, vmax;
  76. typename Container::const_iterator i;
  77. if( !aPointList.size() )
  78. return;
  79. vmin = vmax = aPointList[0];
  80. for( i = aPointList.begin(); i != aPointList.end(); ++i )
  81. {
  82. Vec p( *i );
  83. vmin.x = std::min( vmin.x, p.x );
  84. vmin.y = std::min( vmin.y, p.y );
  85. vmax.x = std::max( vmax.x, p.x );
  86. vmax.y = std::max( vmax.y, p.y );
  87. }
  88. SetOrigin( vmin );
  89. SetSize( vmax - vmin );
  90. }
  91. /**
  92. * Move the rectangle by the \a aMoveVector.
  93. *
  94. * @param aMoveVector is a point that is the value to move this rectangle.
  95. */
  96. void Move( const Vec& aMoveVector )
  97. {
  98. m_Pos += aMoveVector;
  99. }
  100. /**
  101. * Ensure that the height ant width are positive.
  102. */
  103. BOX2<Vec>& Normalize()
  104. {
  105. if( m_Size.y < 0 )
  106. {
  107. m_Size.y = -m_Size.y;
  108. m_Pos.y -= m_Size.y;
  109. }
  110. if( m_Size.x < 0 )
  111. {
  112. m_Size.x = -m_Size.x;
  113. m_Pos.x -= m_Size.x;
  114. }
  115. return *this;
  116. }
  117. /**
  118. * @param aPoint is the point to test.
  119. *
  120. * @return true if \a aPoint is inside the boundary box. A point on a edge is seen as inside.
  121. */
  122. bool Contains( const Vec& aPoint ) const
  123. {
  124. Vec rel_pos = aPoint - m_Pos;
  125. Vec size = m_Size;
  126. if( size.x < 0 )
  127. {
  128. size.x = -size.x;
  129. rel_pos.x += size.x;
  130. }
  131. if( size.y < 0 )
  132. {
  133. size.y = -size.y;
  134. rel_pos.y += size.y;
  135. }
  136. return ( rel_pos.x >= 0 ) && ( rel_pos.y >= 0 ) && ( rel_pos.y <= size.y) &&
  137. ( rel_pos.x <= size.x);
  138. }
  139. /**
  140. * @param x is the x coordinate of the point to test.
  141. * @param y is the x coordinate of the point to test.
  142. * @return true if point is inside the boundary box. A point on a edge is seen as inside.
  143. */
  144. bool Contains( coord_type x, coord_type y ) const { return Contains( Vec( x, y ) ); }
  145. /**
  146. * @param aRect is the the area to test.
  147. *
  148. * @return true if \a aRect is contained. A common edge is seen as contained.
  149. */
  150. bool Contains( const BOX2<Vec>& aRect ) const
  151. {
  152. return Contains( aRect.GetOrigin() ) && Contains( aRect.GetEnd() );
  153. }
  154. const Vec& GetSize() const { return m_Size; }
  155. coord_type GetX() const { return m_Pos.x; }
  156. coord_type GetY() const { return m_Pos.y; }
  157. const Vec& GetOrigin() const { return m_Pos; }
  158. const Vec& GetPosition() const { return m_Pos; }
  159. const Vec GetEnd() const { return Vec( GetRight(), GetBottom() ); }
  160. coord_type GetWidth() const { return m_Size.x; }
  161. coord_type GetHeight() const { return m_Size.y; }
  162. coord_type GetRight() const { return m_Pos.x + m_Size.x; }
  163. coord_type GetBottom() const { return m_Pos.y + m_Size.y; }
  164. // Compatibility aliases
  165. coord_type GetLeft() const { return GetX(); }
  166. coord_type GetTop() const { return GetY(); }
  167. const Vec GetCenter() const { return Centre(); }
  168. /**
  169. * @return the width or height, whichever is greater.
  170. */
  171. int GetSizeMax() const { return ( m_Size.x > m_Size.y ) ? m_Size.x : m_Size.y; }
  172. void SetOrigin( const Vec& pos )
  173. {
  174. m_Pos = pos;
  175. m_init = true;
  176. }
  177. void SetOrigin( coord_type x, coord_type y )
  178. {
  179. SetOrigin( Vec( x, y ) );
  180. }
  181. void SetSize( const Vec& size )
  182. {
  183. m_Size = size;
  184. m_init = true;
  185. }
  186. void SetSize( coord_type w, coord_type h )
  187. {
  188. SetSize( Vec( w, h ) );
  189. }
  190. void Offset( coord_type dx, coord_type dy )
  191. {
  192. m_Pos.x += dx;
  193. m_Pos.y += dy;
  194. }
  195. void Offset( const Vec& offset )
  196. {
  197. Offset( offset.x, offset.y );
  198. }
  199. void SetX( coord_type val )
  200. {
  201. SetOrigin( val, m_Pos.y );
  202. }
  203. void SetY( coord_type val )
  204. {
  205. SetOrigin( m_Pos.x, val );
  206. }
  207. void SetWidth( coord_type val )
  208. {
  209. SetSize( val, m_Size.y );
  210. }
  211. void SetHeight( coord_type val )
  212. {
  213. SetSize( m_Size.x, val );
  214. }
  215. void SetEnd( coord_type x, coord_type y )
  216. {
  217. SetEnd( Vec( x, y ) );
  218. }
  219. void SetEnd( const Vec& pos )
  220. {
  221. SetSize( pos - m_Pos );
  222. }
  223. /**
  224. * @return true if the argument rectangle intersects this rectangle.
  225. * (i.e. if the 2 rectangles have at least a common point)
  226. */
  227. bool Intersects( const BOX2<Vec>& aRect ) const
  228. {
  229. // this logic taken from wxWidgets' geometry.cpp file:
  230. bool rc;
  231. BOX2<Vec> me( *this );
  232. BOX2<Vec> rect( aRect );
  233. me.Normalize(); // ensure size is >= 0
  234. rect.Normalize(); // ensure size is >= 0
  235. // calculate the left common area coordinate:
  236. int left = std::max( me.m_Pos.x, rect.m_Pos.x );
  237. // calculate the right common area coordinate:
  238. int right = std::min( me.m_Pos.x + me.m_Size.x, rect.m_Pos.x + rect.m_Size.x );
  239. // calculate the upper common area coordinate:
  240. int top = std::max( me.m_Pos.y, aRect.m_Pos.y );
  241. // calculate the lower common area coordinate:
  242. int bottom = std::min( me.m_Pos.y + me.m_Size.y, rect.m_Pos.y + rect.m_Size.y );
  243. // if a common area exists, it must have a positive (null accepted) size
  244. if( left <= right && top <= bottom )
  245. rc = true;
  246. else
  247. rc = false;
  248. return rc;
  249. }
  250. /**
  251. * @return true if this rectangle intersects \a aRect.
  252. */
  253. BOX2<Vec> Intersect( const BOX2<Vec>& aRect )
  254. {
  255. BOX2<Vec> me( *this );
  256. BOX2<Vec> rect( aRect );
  257. me.Normalize(); // ensure size is >= 0
  258. rect.Normalize(); // ensure size is >= 0
  259. Vec topLeft, bottomRight;
  260. topLeft.x = std::max( me.m_Pos.x, rect.m_Pos.x );
  261. bottomRight.x = std::min( me.m_Pos.x + me.m_Size.x, rect.m_Pos.x + rect.m_Size.x );
  262. topLeft.y = std::max( me.m_Pos.y, rect.m_Pos.y );
  263. bottomRight.y = std::min( me.m_Pos.y + me.m_Size.y, rect.m_Pos.y + rect.m_Size.y );
  264. if ( topLeft.x < bottomRight.x && topLeft.y < bottomRight.y )
  265. return BOX2<Vec>( topLeft, bottomRight - topLeft );
  266. else
  267. return BOX2<Vec>( Vec( 0, 0 ), Vec( 0, 0 ) );
  268. }
  269. /**
  270. * @return true if this rectangle intersects a line from \a aPoint1 to \a aPoint2
  271. */
  272. bool Intersects( const Vec& aPoint1, const Vec& aPoint2 ) const
  273. {
  274. Vec point2, point4;
  275. if( Contains( aPoint1 ) || Contains( aPoint2 ) )
  276. return true;
  277. point2.x = GetEnd().x;
  278. point2.y = GetOrigin().y;
  279. point4.x = GetOrigin().x;
  280. point4.y = GetEnd().y;
  281. //Only need to test 3 sides since a straight line can't enter and exit on same side
  282. if( SegmentIntersectsSegment( aPoint1, aPoint2, GetOrigin(), point2 ) )
  283. return true;
  284. if( SegmentIntersectsSegment( aPoint1, aPoint2, point2, GetEnd() ) )
  285. return true;
  286. if( SegmentIntersectsSegment( aPoint1, aPoint2, GetEnd(), point4 ) )
  287. return true;
  288. return false;
  289. }
  290. /**
  291. * @return true if this rectangle intersects the circle defined by \a aCenter and \a aRadius.
  292. */
  293. bool IntersectsCircle( const Vec& aCenter, const int aRadius ) const
  294. {
  295. if( !m_init )
  296. return false;
  297. Vec closest = ClosestPointTo( aCenter );
  298. double dx = static_cast<double>( aCenter.x ) - closest.x;
  299. double dy = static_cast<double>( aCenter.y ) - closest.y;
  300. double r = static_cast<double>( aRadius );
  301. return ( dx * dx + dy * dy ) <= ( r * r );
  302. }
  303. /**
  304. * @return true if this rectangle intersects the edge of a circle defined by \a aCenter
  305. * and \a aRadius.
  306. */
  307. bool IntersectsCircleEdge( const Vec& aCenter, const int aRadius, const int aWidth ) const
  308. {
  309. if( !m_init )
  310. return false;
  311. BOX2<Vec> me( *this );
  312. me.Normalize(); // ensure size is >= 0
  313. // Test if the circle intersects at all
  314. if( !IntersectsCircle( aCenter, aRadius + aWidth / 2 ) )
  315. return false;
  316. Vec farpt = FarthestPointTo( aCenter );
  317. // Farthest point must be further than the inside of the line
  318. double fx = (double) farpt.x;
  319. double fy = (double) farpt.y;
  320. double r = (double) aRadius - (double) aWidth / 2;
  321. return ( fx * fx + fy * fy ) > ( r * r );
  322. }
  323. const std::string Format() const
  324. {
  325. std::stringstream ss;
  326. ss << "( box corner " << m_Pos.Format() << " w " << m_Size.x << " h " << m_Size.y << " )";
  327. return ss.str();
  328. }
  329. /**
  330. * Inflates the rectangle horizontally by \a dx and vertically by \a dy. If \a dx
  331. * and/or \a dy is negative the rectangle is deflated.
  332. */
  333. BOX2<Vec>& Inflate( coord_type dx, coord_type dy )
  334. {
  335. if( m_Size.x >= 0 )
  336. {
  337. if( m_Size.x < -2 * dx )
  338. {
  339. // Don't allow deflate to eat more width than we have,
  340. m_Pos.x += m_Size.x / 2;
  341. m_Size.x = 0;
  342. }
  343. else
  344. {
  345. // The inflate is valid.
  346. m_Pos.x -= dx;
  347. m_Size.x += 2 * dx;
  348. }
  349. }
  350. else // size.x < 0:
  351. {
  352. if( m_Size.x > -2 * dx )
  353. {
  354. // Don't allow deflate to eat more width than we have,
  355. m_Pos.x -= m_Size.x / 2;
  356. m_Size.x = 0;
  357. }
  358. else
  359. {
  360. // The inflate is valid.
  361. m_Pos.x += dx;
  362. m_Size.x -= 2 * dx; // m_Size.x <0: inflate when dx > 0
  363. }
  364. }
  365. if( m_Size.y >= 0 )
  366. {
  367. if( m_Size.y < -2 * dy )
  368. {
  369. // Don't allow deflate to eat more height than we have,
  370. m_Pos.y += m_Size.y / 2;
  371. m_Size.y = 0;
  372. }
  373. else
  374. {
  375. // The inflate is valid.
  376. m_Pos.y -= dy;
  377. m_Size.y += 2 * dy;
  378. }
  379. }
  380. else // size.y < 0:
  381. {
  382. if( m_Size.y > 2 * dy )
  383. {
  384. // Don't allow deflate to eat more height than we have,
  385. m_Pos.y -= m_Size.y / 2;
  386. m_Size.y = 0;
  387. }
  388. else
  389. {
  390. // The inflate is valid.
  391. m_Pos.y += dy;
  392. m_Size.y -= 2 * dy; // m_Size.y <0: inflate when dy > 0
  393. }
  394. }
  395. return *this;
  396. }
  397. /**
  398. * Inflate the rectangle horizontally and vertically by \a aDelta. If \a aDelta
  399. * is negative the rectangle is deflated.
  400. */
  401. BOX2<Vec>& Inflate( int aDelta )
  402. {
  403. Inflate( aDelta, aDelta );
  404. return *this;
  405. }
  406. /**
  407. * Modify the position and size of the rectangle in order to contain \a aRect.
  408. *
  409. * @param aRect is the rectangle to merge with this rectangle.
  410. */
  411. BOX2<Vec>& Merge( const BOX2<Vec>& aRect )
  412. {
  413. if( !m_init )
  414. {
  415. if( aRect.m_init )
  416. {
  417. m_Pos = aRect.GetPosition();
  418. m_Size = aRect.GetSize();
  419. m_init = true;
  420. }
  421. return *this;
  422. }
  423. Normalize(); // ensure width and height >= 0
  424. BOX2<Vec> rect = aRect;
  425. rect.Normalize(); // ensure width and height >= 0
  426. Vec end = GetEnd();
  427. Vec rect_end = rect.GetEnd();
  428. // Change origin and size in order to contain the given rect
  429. m_Pos.x = std::min( m_Pos.x, rect.m_Pos.x );
  430. m_Pos.y = std::min( m_Pos.y, rect.m_Pos.y );
  431. end.x = std::max( end.x, rect_end.x );
  432. end.y = std::max( end.y, rect_end.y );
  433. SetEnd( end );
  434. return *this;
  435. }
  436. /**
  437. * Modify the position and size of the rectangle in order to contain the given point.
  438. *
  439. * @param aPoint is the point to merge with the rectangle.
  440. */
  441. BOX2<Vec>& Merge( const Vec& aPoint )
  442. {
  443. if( !m_init )
  444. {
  445. m_Pos = aPoint;
  446. m_Size = VECTOR2I( 0, 0 );
  447. m_init = true;
  448. return *this;
  449. }
  450. Normalize(); // ensure width and height >= 0
  451. Vec end = GetEnd();
  452. // Change origin and size in order to contain the given rectangle.
  453. m_Pos.x = std::min( m_Pos.x, aPoint.x );
  454. m_Pos.y = std::min( m_Pos.y, aPoint.y );
  455. end.x = std::max( end.x, aPoint.x );
  456. end.y = std::max( end.y, aPoint.y );
  457. SetEnd( end );
  458. return *this;
  459. }
  460. /**
  461. * Useful to calculate bounding box of rotated items, when rotation is not cardinal.
  462. *
  463. * @return the bounding box of this, after rotation.
  464. */
  465. const BOX2<Vec> GetBoundingBoxRotated( const VECTOR2I& aRotCenter,
  466. const EDA_ANGLE& aAngle ) const
  467. {
  468. VECTOR2I corners[4];
  469. // Build the corners list
  470. corners[0] = GetOrigin();
  471. corners[2] = GetEnd();
  472. corners[1].x = corners[0].x;
  473. corners[1].y = corners[2].y;
  474. corners[3].x = corners[2].x;
  475. corners[3].y = corners[0].y;
  476. // Rotate all corners, to find the bounding box
  477. for( int ii = 0; ii < 4; ii++ )
  478. RotatePoint( corners[ii], aRotCenter, aAngle );
  479. // Find the corners bounding box
  480. VECTOR2I start = corners[0];
  481. VECTOR2I end = corners[0];
  482. for( int ii = 1; ii < 4; ii++ )
  483. {
  484. start.x = std::min( start.x, corners[ii].x );
  485. start.y = std::min( start.y, corners[ii].y );
  486. end.x = std::max( end.x, corners[ii].x );
  487. end.y = std::max( end.y, corners[ii].y );
  488. }
  489. BOX2<Vec> bbox;
  490. bbox.SetOrigin( start );
  491. bbox.SetEnd( end );
  492. return bbox;
  493. }
  494. /**
  495. * Mirror the rectangle from the X axis (negate Y pos and size).
  496. */
  497. void RevertYAxis()
  498. {
  499. m_Pos.y = -m_Pos.y;
  500. m_Size.y = -m_Size.y;
  501. Normalize();
  502. }
  503. /**
  504. * Return the area of the rectangle.
  505. *
  506. * @return The area of the rectangle.
  507. */
  508. ecoord_type GetArea() const
  509. {
  510. return (ecoord_type) GetWidth() * (ecoord_type) GetHeight();
  511. }
  512. /**
  513. * Return the length of the diagonal of the rectangle.
  514. *
  515. * @return The length of the rectangle diagonal.
  516. */
  517. ecoord_type Diagonal() const
  518. {
  519. return m_Size.EuclideanNorm();
  520. }
  521. ecoord_type SquaredDistance( const Vec& aP ) const
  522. {
  523. ecoord_type x2 = m_Pos.x + m_Size.x;
  524. ecoord_type y2 = m_Pos.y + m_Size.y;
  525. ecoord_type xdiff = std::max( aP.x < m_Pos.x ? m_Pos.x - aP.x : m_Pos.x - x2,
  526. (ecoord_type) 0 );
  527. ecoord_type ydiff = std::max( aP.y < m_Pos.y ? m_Pos.y - aP.y : m_Pos.y - y2,
  528. (ecoord_type) 0 );
  529. return xdiff * xdiff + ydiff * ydiff;
  530. }
  531. ecoord_type Distance( const Vec& aP ) const
  532. {
  533. return sqrt( SquaredDistance( aP ) );
  534. }
  535. /**
  536. * Return the square of the minimum distance between self and box \a aBox
  537. *
  538. * @param aBox is the other box.
  539. * @return The distance squared from \a aBox.
  540. */
  541. ecoord_type SquaredDistance( const BOX2<Vec>& aBox ) const
  542. {
  543. ecoord_type s = 0;
  544. if( aBox.m_Pos.x + aBox.m_Size.x < m_Pos.x )
  545. {
  546. ecoord_type d = aBox.m_Pos.x + aBox.m_Size.x - m_Pos.x;
  547. s += d * d;
  548. }
  549. else if( aBox.m_Pos.x > m_Pos.x + m_Size.x )
  550. {
  551. ecoord_type d = aBox.m_Pos.x - m_Size.x - m_Pos.x;
  552. s += d * d;
  553. }
  554. if( aBox.m_Pos.y + aBox.m_Size.y < m_Pos.y )
  555. {
  556. ecoord_type d = aBox.m_Pos.y + aBox.m_Size.y - m_Pos.y;
  557. s += d * d;
  558. }
  559. else if( aBox.m_Pos.y > m_Pos.y + m_Size.y )
  560. {
  561. ecoord_type d = aBox.m_Pos.y - m_Size.y - m_Pos.y;
  562. s += d * d;
  563. }
  564. return s;
  565. }
  566. /**
  567. * Return the minimum distance between self and \a aBox.
  568. *
  569. * @param aBox is the other box to get the distance from.
  570. * @return The distance from \a aBox.
  571. */
  572. ecoord_type Distance( const BOX2<Vec>& aBox ) const
  573. {
  574. return sqrt( SquaredDistance( aBox ) );
  575. }
  576. /**
  577. * Return the point in this rect that is closest to the provided point
  578. */
  579. const Vec ClosestPointTo( const Vec& aPoint ) const
  580. {
  581. BOX2<Vec> me( *this );
  582. me.Normalize(); // ensure size is >= 0
  583. // Determine closest point to the circle centre within this rect
  584. coord_type nx = std::max( me.GetLeft(), std::min( aPoint.x, me.GetRight() ) );
  585. coord_type ny = std::max( me.GetTop(), std::min( aPoint.y, me.GetBottom() ) );
  586. return Vec( nx, ny );
  587. }
  588. /**
  589. * Return the point in this rect that is farthest from the provided point
  590. */
  591. const Vec FarthestPointTo( const Vec& aPoint ) const
  592. {
  593. BOX2<Vec> me( *this );
  594. me.Normalize(); // ensure size is >= 0
  595. coord_type fx = std::max( std::abs( aPoint.x - me.GetLeft() ), std::abs( aPoint.x - me.GetRight() ) );
  596. coord_type fy = std::max( std::abs( aPoint.y - me.GetTop() ), std::abs( aPoint.y - me.GetBottom() ) );
  597. return Vec( fx, fy );
  598. }
  599. bool operator==( const BOX2<Vec>& aOther ) const
  600. {
  601. auto t1 ( *this );
  602. auto t2 ( aOther );
  603. t1.Normalize();
  604. t2.Normalize();
  605. return ( t1.m_Pos == t2.m_Pos && t1.m_Size == t2.m_Size );
  606. }
  607. bool operator!=( const BOX2<Vec>& aOther ) const
  608. {
  609. auto t1 ( *this );
  610. auto t2 ( aOther );
  611. t1.Normalize();
  612. t2.Normalize();
  613. return ( t1.m_Pos != t2.m_Pos || t1.m_Size != t2.m_Size );
  614. }
  615. private:
  616. Vec m_Pos; // Rectangle Origin
  617. Vec m_Size; // Rectangle Size
  618. bool m_init; // Is the rectangle initialized
  619. };
  620. /* Default specializations */
  621. typedef BOX2<VECTOR2I> BOX2I;
  622. typedef BOX2<VECTOR2D> BOX2D;
  623. typedef std::optional<BOX2I> OPT_BOX2I;
  624. // FIXME should be removed to avoid multiple typedefs for the same type
  625. typedef BOX2D DBOX;
  626. #endif