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.

543 lines
14 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com
  5. * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @brief Implementation of EDA_RECT base class for KiCad.
  26. */
  27. #include <algorithm>
  28. #include <deque>
  29. #include <eda_rect.h>
  30. #include <trigo.h>
  31. void EDA_RECT::Normalize()
  32. {
  33. if( m_Size.y < 0 )
  34. {
  35. m_Size.y = -m_Size.y;
  36. m_Pos.y -= m_Size.y;
  37. }
  38. if( m_Size.x < 0 )
  39. {
  40. m_Size.x = -m_Size.x;
  41. m_Pos.x -= m_Size.x;
  42. }
  43. }
  44. void EDA_RECT::Move( const wxPoint& aMoveVector )
  45. {
  46. m_Pos += aMoveVector;
  47. }
  48. bool EDA_RECT::Contains( const wxPoint& aPoint ) const
  49. {
  50. wxPoint rel_pos = aPoint - m_Pos;
  51. wxSize size = m_Size;
  52. if( size.x < 0 )
  53. {
  54. size.x = -size.x;
  55. rel_pos.x += size.x;
  56. }
  57. if( size.y < 0 )
  58. {
  59. size.y = -size.y;
  60. rel_pos.y += size.y;
  61. }
  62. return ( rel_pos.x >= 0 ) && ( rel_pos.y >= 0 ) && ( rel_pos.y <= size.y )
  63. && ( rel_pos.x <= size.x );
  64. }
  65. bool EDA_RECT::Contains( const EDA_RECT& aRect ) const
  66. {
  67. return Contains( aRect.GetOrigin() ) && Contains( aRect.GetEnd() );
  68. }
  69. bool EDA_RECT::Intersects( const wxPoint& aPoint1, const wxPoint& aPoint2 ) const
  70. {
  71. wxPoint point2, point4;
  72. if( Contains( aPoint1 ) || Contains( aPoint2 ) )
  73. return true;
  74. point2.x = GetEnd().x;
  75. point2.y = GetOrigin().y;
  76. point4.x = GetOrigin().x;
  77. point4.y = GetEnd().y;
  78. //Only need to test 3 sides since a straight line cant enter and exit on same side
  79. if( SegmentIntersectsSegment( aPoint1, aPoint2, GetOrigin(), point2 ) )
  80. return true;
  81. if( SegmentIntersectsSegment( aPoint1, aPoint2, point2, GetEnd() ) )
  82. return true;
  83. if( SegmentIntersectsSegment( aPoint1, aPoint2, GetEnd(), point4 ) )
  84. return true;
  85. return false;
  86. }
  87. bool EDA_RECT::Intersects( const wxPoint& aPoint1, const wxPoint& aPoint2, wxPoint* aIntersection1,
  88. wxPoint* aIntersection2 ) const
  89. {
  90. wxPoint point2, point4;
  91. point2.x = GetEnd().x;
  92. point2.y = GetOrigin().y;
  93. point4.x = GetOrigin().x;
  94. point4.y = GetEnd().y;
  95. bool intersects = false;
  96. wxPoint* aPointToFill = aIntersection1;
  97. if( SegmentIntersectsSegment( aPoint1, aPoint2, GetOrigin(), point2, aPointToFill ) )
  98. intersects = true;
  99. if( intersects )
  100. aPointToFill = aIntersection2;
  101. if( SegmentIntersectsSegment( aPoint1, aPoint2, point2, GetEnd(), aPointToFill ) )
  102. intersects = true;
  103. if( intersects )
  104. aPointToFill = aIntersection2;
  105. if( SegmentIntersectsSegment( aPoint1, aPoint2, GetEnd(), point4, aPointToFill ) )
  106. intersects = true;
  107. if( intersects )
  108. aPointToFill = aIntersection2;
  109. if( SegmentIntersectsSegment( aPoint1, aPoint2, point4, GetOrigin(), aPointToFill ) )
  110. intersects = true;
  111. return intersects;
  112. }
  113. bool EDA_RECT::Intersects( const EDA_RECT& aRect ) const
  114. {
  115. if( !m_init )
  116. return false;
  117. // this logic taken from wxWidgets' geometry.cpp file:
  118. bool rc;
  119. EDA_RECT me( *this );
  120. EDA_RECT rect( aRect );
  121. me.Normalize(); // ensure size is >= 0
  122. rect.Normalize(); // ensure size is >= 0
  123. // calculate the left common area coordinate:
  124. int left = std::max( me.m_Pos.x, rect.m_Pos.x );
  125. // calculate the right common area coordinate:
  126. int right = std::min( me.m_Pos.x + me.m_Size.x, rect.m_Pos.x + rect.m_Size.x );
  127. // calculate the upper common area coordinate:
  128. int top = std::max( me.m_Pos.y, aRect.m_Pos.y );
  129. // calculate the lower common area coordinate:
  130. int bottom = std::min( me.m_Pos.y + me.m_Size.y, rect.m_Pos.y + rect.m_Size.y );
  131. // if a common area exists, it must have a positive (null accepted) size
  132. if( left <= right && top <= bottom )
  133. rc = true;
  134. else
  135. rc = false;
  136. return rc;
  137. }
  138. bool EDA_RECT::Intersects( const EDA_RECT& aRect, double aRot ) const
  139. {
  140. if( !m_init )
  141. return false;
  142. /* Most rectangles will be axis aligned.
  143. * It is quicker to check for this case and pass the rect
  144. * to the simpler intersection test
  145. */
  146. // Prevent floating point comparison errors
  147. static const double ROT_EPS = 0.000000001;
  148. static const double ROT_PARALLEL[] = { -3600, -1800, 0, 1800, 3600 };
  149. static const double ROT_PERPENDICULAR[] = { -2700, -900, 0, 900, 2700 };
  150. NORMALIZE_ANGLE_POS<double>( aRot );
  151. // Test for non-rotated rectangle
  152. for( int ii = 0; ii < 5; ii++ )
  153. {
  154. if( std::fabs( aRot - ROT_PARALLEL[ii] ) < ROT_EPS )
  155. {
  156. return Intersects( aRect );
  157. }
  158. }
  159. // Test for rectangle rotated by multiple of 90 degrees
  160. for( int jj = 0; jj < 4; jj++ )
  161. {
  162. if( std::fabs( aRot - ROT_PERPENDICULAR[jj] ) < ROT_EPS )
  163. {
  164. EDA_RECT rotRect;
  165. // Rotate the supplied rect by 90 degrees
  166. rotRect.SetOrigin( aRect.Centre() );
  167. rotRect.Inflate( aRect.GetHeight(), aRect.GetWidth() );
  168. return Intersects( rotRect );
  169. }
  170. }
  171. /* There is some non-orthogonal rotation.
  172. * There are three cases to test:
  173. * A) One point of this rect is inside the rotated rect
  174. * B) One point of the rotated rect is inside this rect
  175. * C) One of the sides of the rotated rect intersect this
  176. */
  177. wxPoint corners[4];
  178. /* Test A : Any corners exist in rotated rect? */
  179. corners[0] = m_Pos;
  180. corners[1] = m_Pos + wxPoint( m_Size.x, 0 );
  181. corners[2] = m_Pos + wxPoint( m_Size.x, m_Size.y );
  182. corners[3] = m_Pos + wxPoint( 0, m_Size.y );
  183. wxPoint rCentre = aRect.Centre();
  184. for( int i = 0; i < 4; i++ )
  185. {
  186. wxPoint delta = corners[i] - rCentre;
  187. RotatePoint( &delta, -aRot );
  188. delta += rCentre;
  189. if( aRect.Contains( delta ) )
  190. {
  191. return true;
  192. }
  193. }
  194. /* Test B : Any corners of rotated rect exist in this one? */
  195. int w = aRect.GetWidth() / 2;
  196. int h = aRect.GetHeight() / 2;
  197. // Construct corners around center of shape
  198. corners[0] = wxPoint( -w, -h );
  199. corners[1] = wxPoint( w, -h );
  200. corners[2] = wxPoint( w, h );
  201. corners[3] = wxPoint( -w, h );
  202. // Rotate and test each corner
  203. for( int j = 0; j < 4; j++ )
  204. {
  205. RotatePoint( &corners[j], aRot );
  206. corners[j] += rCentre;
  207. if( Contains( corners[j] ) )
  208. {
  209. return true;
  210. }
  211. }
  212. /* Test C : Any sides of rotated rect intersect this */
  213. if( Intersects( corners[0], corners[1] ) || Intersects( corners[1], corners[2] )
  214. || Intersects( corners[2], corners[3] ) || Intersects( corners[3], corners[0] ) )
  215. {
  216. return true;
  217. }
  218. return false;
  219. }
  220. const wxPoint EDA_RECT::ClosestPointTo( const wxPoint& aPoint ) const
  221. {
  222. EDA_RECT me( *this );
  223. me.Normalize(); // ensure size is >= 0
  224. // Determine closest point to the circle centre within this rect
  225. int nx = std::max( me.GetLeft(), std::min( aPoint.x, me.GetRight() ) );
  226. int ny = std::max( me.GetTop(), std::min( aPoint.y, me.GetBottom() ) );
  227. return wxPoint( nx, ny );
  228. }
  229. const wxPoint EDA_RECT::FarthestPointTo( const wxPoint& aPoint ) const
  230. {
  231. EDA_RECT me( *this );
  232. me.Normalize(); // ensure size is >= 0
  233. int fx = std::max( std::abs( aPoint.x - me.GetLeft() ), std::abs( aPoint.x - me.GetRight() ) );
  234. int fy = std::max( std::abs( aPoint.y - me.GetTop() ), std::abs( aPoint.y - me.GetBottom() ) );
  235. return wxPoint( fx, fy );
  236. }
  237. bool EDA_RECT::IntersectsCircle( const wxPoint& aCenter, const int aRadius ) const
  238. {
  239. if( !m_init )
  240. return false;
  241. wxPoint closest = ClosestPointTo( aCenter );
  242. double dx = static_cast<double>( aCenter.x ) - closest.x;
  243. double dy = static_cast<double>( aCenter.y ) - closest.y;
  244. double r = static_cast<double>( aRadius );
  245. return ( dx * dx + dy * dy ) <= ( r * r );
  246. }
  247. bool EDA_RECT::IntersectsCircleEdge(
  248. const wxPoint& aCenter, const int aRadius, const int aWidth ) const
  249. {
  250. if( !m_init )
  251. return false;
  252. EDA_RECT me( *this );
  253. me.Normalize(); // ensure size is >= 0
  254. // Test if the circle intersects at all
  255. if( !IntersectsCircle( aCenter, aRadius + aWidth / 2 ) )
  256. {
  257. return false;
  258. }
  259. wxPoint farpt = FarthestPointTo( aCenter );
  260. // Farthest point must be further than the inside of the line
  261. double fx = (double) farpt.x;
  262. double fy = (double) farpt.y;
  263. double r = (double) aRadius - (double) aWidth / 2;
  264. return ( fx * fx + fy * fy ) > ( r * r );
  265. }
  266. EDA_RECT& EDA_RECT::Inflate( int aDelta )
  267. {
  268. Inflate( aDelta, aDelta );
  269. return *this;
  270. }
  271. EDA_RECT& EDA_RECT::Inflate( wxCoord dx, wxCoord dy )
  272. {
  273. if( m_Size.x >= 0 )
  274. {
  275. if( m_Size.x < -2 * dx )
  276. {
  277. // Don't allow deflate to eat more width than we have,
  278. m_Pos.x += m_Size.x / 2;
  279. m_Size.x = 0;
  280. }
  281. else
  282. {
  283. // The inflate is valid.
  284. m_Pos.x -= dx;
  285. m_Size.x += 2 * dx;
  286. }
  287. }
  288. else // size.x < 0:
  289. {
  290. if( m_Size.x > -2 * dx )
  291. {
  292. // Don't allow deflate to eat more width than we have,
  293. m_Pos.x -= m_Size.x / 2;
  294. m_Size.x = 0;
  295. }
  296. else
  297. {
  298. // The inflate is valid.
  299. m_Pos.x += dx;
  300. m_Size.x -= 2 * dx; // m_Size.x <0: inflate when dx > 0
  301. }
  302. }
  303. if( m_Size.y >= 0 )
  304. {
  305. if( m_Size.y < -2 * dy )
  306. {
  307. // Don't allow deflate to eat more height than we have,
  308. m_Pos.y += m_Size.y / 2;
  309. m_Size.y = 0;
  310. }
  311. else
  312. {
  313. // The inflate is valid.
  314. m_Pos.y -= dy;
  315. m_Size.y += 2 * dy;
  316. }
  317. }
  318. else // size.y < 0:
  319. {
  320. if( m_Size.y > 2 * dy )
  321. {
  322. // Don't allow deflate to eat more height than we have,
  323. m_Pos.y -= m_Size.y / 2;
  324. m_Size.y = 0;
  325. }
  326. else
  327. {
  328. // The inflate is valid.
  329. m_Pos.y += dy;
  330. m_Size.y -= 2 * dy; // m_Size.y <0: inflate when dy > 0
  331. }
  332. }
  333. return *this;
  334. }
  335. void EDA_RECT::Merge( const EDA_RECT& aRect )
  336. {
  337. if( !m_init )
  338. {
  339. if( aRect.IsValid() )
  340. {
  341. m_Pos = aRect.GetPosition();
  342. m_Size = aRect.GetSize();
  343. m_init = true;
  344. }
  345. return;
  346. }
  347. Normalize(); // ensure width and height >= 0
  348. EDA_RECT rect = aRect;
  349. rect.Normalize(); // ensure width and height >= 0
  350. wxPoint end = GetEnd();
  351. wxPoint rect_end = rect.GetEnd();
  352. // Change origin and size in order to contain the given rect
  353. m_Pos.x = std::min( m_Pos.x, rect.m_Pos.x );
  354. m_Pos.y = std::min( m_Pos.y, rect.m_Pos.y );
  355. end.x = std::max( end.x, rect_end.x );
  356. end.y = std::max( end.y, rect_end.y );
  357. SetEnd( end );
  358. }
  359. void EDA_RECT::Merge( const wxPoint& aPoint )
  360. {
  361. if( !m_init )
  362. {
  363. m_Pos = aPoint;
  364. m_Size = wxSize( 0, 0 );
  365. m_init = true;
  366. return;
  367. }
  368. Normalize(); // ensure width and height >= 0
  369. wxPoint end = GetEnd();
  370. // Change origin and size in order to contain the given rect
  371. m_Pos.x = std::min( m_Pos.x, aPoint.x );
  372. m_Pos.y = std::min( m_Pos.y, aPoint.y );
  373. end.x = std::max( end.x, aPoint.x );
  374. end.y = std::max( end.y, aPoint.y );
  375. SetEnd( end );
  376. }
  377. double EDA_RECT::GetArea() const
  378. {
  379. return (double) GetWidth() * (double) GetHeight();
  380. }
  381. EDA_RECT EDA_RECT::Common( const EDA_RECT& aRect ) const
  382. {
  383. EDA_RECT r;
  384. if( Intersects( aRect ) )
  385. {
  386. wxPoint originA(
  387. std::min( GetOrigin().x, GetEnd().x ), std::min( GetOrigin().y, GetEnd().y ) );
  388. wxPoint originB( std::min( aRect.GetOrigin().x, aRect.GetEnd().x ),
  389. std::min( aRect.GetOrigin().y, aRect.GetEnd().y ) );
  390. wxPoint endA(
  391. std::max( GetOrigin().x, GetEnd().x ), std::max( GetOrigin().y, GetEnd().y ) );
  392. wxPoint endB( std::max( aRect.GetOrigin().x, aRect.GetEnd().x ),
  393. std::max( aRect.GetOrigin().y, aRect.GetEnd().y ) );
  394. r.SetOrigin(
  395. wxPoint( std::max( originA.x, originB.x ), std::max( originA.y, originB.y ) ) );
  396. r.SetEnd( wxPoint( std::min( endA.x, endB.x ), std::min( endA.y, endB.y ) ) );
  397. }
  398. return r;
  399. }
  400. const EDA_RECT EDA_RECT::GetBoundingBoxRotated( wxPoint aRotCenter, double aAngle ) const
  401. {
  402. wxPoint corners[4];
  403. // Build the corners list
  404. corners[0] = GetOrigin();
  405. corners[2] = GetEnd();
  406. corners[1].x = corners[0].x;
  407. corners[1].y = corners[2].y;
  408. corners[3].x = corners[2].x;
  409. corners[3].y = corners[0].y;
  410. // Rotate all corners, to find the bounding box
  411. for( int ii = 0; ii < 4; ii++ )
  412. RotatePoint( &corners[ii], aRotCenter, aAngle );
  413. // Find the corners bounding box
  414. wxPoint start = corners[0];
  415. wxPoint end = corners[0];
  416. for( int ii = 1; ii < 4; ii++ )
  417. {
  418. start.x = std::min( start.x, corners[ii].x );
  419. start.y = std::min( start.y, corners[ii].y );
  420. end.x = std::max( end.x, corners[ii].x );
  421. end.y = std::max( end.y, corners[ii].y );
  422. }
  423. EDA_RECT bbox;
  424. bbox.SetOrigin( start );
  425. bbox.SetEnd( end );
  426. return bbox;
  427. }