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.

719 lines
17 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2013-2017 CERN
  5. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  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. #include <algorithm>
  25. #include <geometry/shape_line_chain.h>
  26. #include <geometry/shape_circle.h>
  27. #include <trigo.h>
  28. #include "clipper.hpp"
  29. ClipperLib::Path SHAPE_LINE_CHAIN::convertToClipper( bool aRequiredOrientation ) const
  30. {
  31. ClipperLib::Path c_path;
  32. for( int i = 0; i < PointCount(); i++ )
  33. {
  34. const VECTOR2I& vertex = CPoint( i );
  35. c_path.push_back( ClipperLib::IntPoint( vertex.x, vertex.y ) );
  36. }
  37. if( Orientation( c_path ) != aRequiredOrientation )
  38. ReversePath( c_path );
  39. return c_path;
  40. }
  41. bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance ) const
  42. {
  43. // fixme: ugly!
  44. SEG s( aP, aP );
  45. return this->Collide( s, aClearance );
  46. }
  47. void SHAPE_LINE_CHAIN::Rotate( double aAngle, const VECTOR2I& aCenter )
  48. {
  49. for( std::vector<VECTOR2I>::iterator i = m_points.begin(); i != m_points.end(); ++i )
  50. {
  51. (*i) -= aCenter;
  52. (*i) = (*i).Rotate( aAngle );
  53. (*i) += aCenter;
  54. }
  55. }
  56. bool SHAPE_LINE_CHAIN::Collide( const SEG& aSeg, int aClearance ) const
  57. {
  58. BOX2I box_a( aSeg.A, aSeg.B - aSeg.A );
  59. BOX2I::ecoord_type dist_sq = (BOX2I::ecoord_type) aClearance * aClearance;
  60. for( int i = 0; i < SegmentCount(); i++ )
  61. {
  62. const SEG& s = CSegment( i );
  63. BOX2I box_b( s.A, s.B - s.A );
  64. BOX2I::ecoord_type d = box_a.SquaredDistance( box_b );
  65. if( d < dist_sq )
  66. {
  67. if( s.Collide( aSeg, aClearance ) )
  68. return true;
  69. }
  70. }
  71. return false;
  72. }
  73. const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Reverse() const
  74. {
  75. SHAPE_LINE_CHAIN a( *this );
  76. reverse( a.m_points.begin(), a.m_points.end() );
  77. a.m_closed = m_closed;
  78. return a;
  79. }
  80. int SHAPE_LINE_CHAIN::Length() const
  81. {
  82. int l = 0;
  83. for( int i = 0; i < SegmentCount(); i++ )
  84. l += CSegment( i ).Length();
  85. return l;
  86. }
  87. void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const VECTOR2I& aP )
  88. {
  89. if( aEndIndex < 0 )
  90. aEndIndex += PointCount();
  91. if( aStartIndex < 0 )
  92. aStartIndex += PointCount();
  93. if( aStartIndex == aEndIndex )
  94. m_points[aStartIndex] = aP;
  95. else
  96. {
  97. m_points.erase( m_points.begin() + aStartIndex + 1, m_points.begin() + aEndIndex + 1 );
  98. m_points[aStartIndex] = aP;
  99. }
  100. }
  101. void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const SHAPE_LINE_CHAIN& aLine )
  102. {
  103. if( aEndIndex < 0 )
  104. aEndIndex += PointCount();
  105. if( aStartIndex < 0 )
  106. aStartIndex += PointCount();
  107. m_points.erase( m_points.begin() + aStartIndex, m_points.begin() + aEndIndex + 1 );
  108. m_points.insert( m_points.begin() + aStartIndex, aLine.m_points.begin(), aLine.m_points.end() );
  109. }
  110. void SHAPE_LINE_CHAIN::Remove( int aStartIndex, int aEndIndex )
  111. {
  112. if( aEndIndex < 0 )
  113. aEndIndex += PointCount();
  114. if( aStartIndex < 0 )
  115. aStartIndex += PointCount();
  116. m_points.erase( m_points.begin() + aStartIndex, m_points.begin() + aEndIndex + 1 );
  117. }
  118. int SHAPE_LINE_CHAIN::Distance( const VECTOR2I& aP, bool aOutlineOnly ) const
  119. {
  120. int d = INT_MAX;
  121. if( IsClosed() && PointInside( aP ) && !aOutlineOnly )
  122. return 0;
  123. for( int s = 0; s < SegmentCount(); s++ )
  124. d = std::min( d, CSegment( s ).Distance( aP ) );
  125. return d;
  126. }
  127. int SHAPE_LINE_CHAIN::Split( const VECTOR2I& aP )
  128. {
  129. int ii = -1;
  130. int min_dist = 2;
  131. int found_index = Find( aP );
  132. for( int s = 0; s < SegmentCount(); s++ )
  133. {
  134. const SEG seg = CSegment( s );
  135. int dist = seg.Distance( aP );
  136. // make sure we are not producing a 'slightly concave' primitive. This might happen
  137. // if aP lies very close to one of already existing points.
  138. if( dist < min_dist && seg.A != aP && seg.B != aP )
  139. {
  140. min_dist = dist;
  141. if( found_index < 0 )
  142. ii = s;
  143. else if( s < found_index )
  144. ii = s;
  145. }
  146. }
  147. if( ii < 0 )
  148. ii = found_index;
  149. if( ii >= 0 )
  150. {
  151. m_points.insert( m_points.begin() + ii + 1, aP );
  152. return ii + 1;
  153. }
  154. return -1;
  155. }
  156. int SHAPE_LINE_CHAIN::Find( const VECTOR2I& aP ) const
  157. {
  158. for( int s = 0; s < PointCount(); s++ )
  159. if( CPoint( s ) == aP )
  160. return s;
  161. return -1;
  162. }
  163. int SHAPE_LINE_CHAIN::FindSegment( const VECTOR2I& aP ) const
  164. {
  165. for( int s = 0; s < SegmentCount(); s++ )
  166. if( CSegment( s ).Distance( aP ) <= 1 )
  167. return s;
  168. return -1;
  169. }
  170. const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex ) const
  171. {
  172. SHAPE_LINE_CHAIN rv;
  173. if( aEndIndex < 0 )
  174. aEndIndex += PointCount();
  175. if( aStartIndex < 0 )
  176. aStartIndex += PointCount();
  177. for( int i = aStartIndex; i <= aEndIndex; i++ )
  178. rv.Append( m_points[i] );
  179. return rv;
  180. }
  181. struct compareOriginDistance
  182. {
  183. compareOriginDistance( VECTOR2I& aOrigin ) :
  184. m_origin( aOrigin ) {};
  185. bool operator()( const SHAPE_LINE_CHAIN::INTERSECTION& aA,
  186. const SHAPE_LINE_CHAIN::INTERSECTION& aB )
  187. {
  188. return ( m_origin - aA.p ).EuclideanNorm() < ( m_origin - aB.p ).EuclideanNorm();
  189. }
  190. VECTOR2I m_origin;
  191. };
  192. int SHAPE_LINE_CHAIN::Intersect( const SEG& aSeg, INTERSECTIONS& aIp ) const
  193. {
  194. for( int s = 0; s < SegmentCount(); s++ )
  195. {
  196. OPT_VECTOR2I p = CSegment( s ).Intersect( aSeg );
  197. if( p )
  198. {
  199. INTERSECTION is;
  200. is.our = CSegment( s );
  201. is.their = aSeg;
  202. is.p = *p;
  203. aIp.push_back( is );
  204. }
  205. }
  206. compareOriginDistance comp( aSeg.A );
  207. sort( aIp.begin(), aIp.end(), comp );
  208. return aIp.size();
  209. }
  210. int SHAPE_LINE_CHAIN::Intersect( const SHAPE_LINE_CHAIN& aChain, INTERSECTIONS& aIp ) const
  211. {
  212. BOX2I bb_other = aChain.BBox();
  213. for( int s1 = 0; s1 < SegmentCount(); s1++ )
  214. {
  215. const SEG& a = CSegment( s1 );
  216. const BOX2I bb_cur( a.A, a.B - a.A );
  217. if( !bb_other.Intersects( bb_cur ) )
  218. continue;
  219. for( int s2 = 0; s2 < aChain.SegmentCount(); s2++ )
  220. {
  221. const SEG& b = aChain.CSegment( s2 );
  222. INTERSECTION is;
  223. if( a.Collinear( b ) )
  224. {
  225. is.our = a;
  226. is.their = b;
  227. if( a.Contains( b.A ) ) { is.p = b.A; aIp.push_back( is ); }
  228. if( a.Contains( b.B ) ) { is.p = b.B; aIp.push_back( is ); }
  229. if( b.Contains( a.A ) ) { is.p = a.A; aIp.push_back( is ); }
  230. if( b.Contains( a.B ) ) { is.p = a.B; aIp.push_back( is ); }
  231. }
  232. else
  233. {
  234. OPT_VECTOR2I p = a.Intersect( b );
  235. if( p )
  236. {
  237. is.p = *p;
  238. is.our = a;
  239. is.their = b;
  240. aIp.push_back( is );
  241. }
  242. }
  243. }
  244. }
  245. return aIp.size();
  246. }
  247. int SHAPE_LINE_CHAIN::PathLength( const VECTOR2I& aP ) const
  248. {
  249. int sum = 0;
  250. for( int i = 0; i < SegmentCount(); i++ )
  251. {
  252. const SEG seg = CSegment( i );
  253. int d = seg.Distance( aP );
  254. if( d <= 1 )
  255. {
  256. sum += ( aP - seg.A ).EuclideanNorm();
  257. return sum;
  258. }
  259. else
  260. sum += seg.Length();
  261. }
  262. return -1;
  263. }
  264. bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aPt, int aAccuracy, bool aUseBBoxCache ) const
  265. {
  266. /*
  267. * Don't check the bounding box unless it's cached. Building it is about the same speed as
  268. * the rigorous test below and so just slows things down by doing potentially two tests.
  269. */
  270. if( aUseBBoxCache && !m_bbox.Contains( aPt ) )
  271. return false;
  272. if( !m_closed || PointCount() < 3 )
  273. return false;
  274. bool inside = false;
  275. /**
  276. * To check for interior points, we draw a line in the positive x direction from
  277. * the point. If it intersects an even number of segments, the point is outside the
  278. * line chain (it had to first enter and then exit). Otherwise, it is inside the chain.
  279. *
  280. * Note: slope might be denormal here in the case of a horizontal line but we require our
  281. * y to move from above to below the point (or vice versa)
  282. *
  283. * Note: we open-code CPoint() here so that we don't end up calculating the size of the
  284. * vector number-of-points times. This has a non-trivial impact on zone fill times.
  285. */
  286. const std::vector<VECTOR2I>& points = CPoints();
  287. int pointCount = points.size();
  288. for( int i = 0; i < pointCount; )
  289. {
  290. const auto p1 = points[ i++ ];
  291. const auto p2 = points[ i == pointCount ? 0 : i ];
  292. const auto diff = p2 - p1;
  293. if( diff.y != 0 )
  294. {
  295. const int d = rescale( diff.x, ( aPt.y - p1.y ), diff.y );
  296. if( ( ( p1.y > aPt.y ) != ( p2.y > aPt.y ) ) && ( aPt.x - p1.x < d ) )
  297. inside = !inside;
  298. }
  299. }
  300. // If aAccuracy is > 0 then by definition we don't care whether or not the point is
  301. // *exactly* on the edge -- which saves us considerable processing time
  302. return inside && ( aAccuracy > 0 || !PointOnEdge( aPt ) );
  303. }
  304. bool SHAPE_LINE_CHAIN::PointOnEdge( const VECTOR2I& aPt, int aAccuracy ) const
  305. {
  306. return EdgeContainingPoint( aPt, aAccuracy ) >= 0;
  307. }
  308. int SHAPE_LINE_CHAIN::EdgeContainingPoint( const VECTOR2I& aPt, int aAccuracy ) const
  309. {
  310. if( !PointCount() )
  311. return -1;
  312. else if( PointCount() == 1 )
  313. {
  314. VECTOR2I dist = m_points[0] - aPt;
  315. return ( hypot( dist.x, dist.y ) <= aAccuracy + 1 ) ? 0 : -1;
  316. }
  317. for( int i = 0; i < SegmentCount(); i++ )
  318. {
  319. const SEG s = CSegment( i );
  320. if( s.A == aPt || s.B == aPt )
  321. return i;
  322. if( s.Distance( aPt ) <= aAccuracy + 1 )
  323. return i;
  324. }
  325. return -1;
  326. }
  327. bool SHAPE_LINE_CHAIN::CheckClearance( const VECTOR2I& aP, const int aDist) const
  328. {
  329. if( !PointCount() )
  330. return false;
  331. else if( PointCount() == 1 )
  332. return m_points[0] == aP;
  333. for( int i = 0; i < SegmentCount(); i++ )
  334. {
  335. const SEG s = CSegment( i );
  336. if( s.A == aP || s.B == aP )
  337. return true;
  338. if( s.Distance( aP ) <= aDist )
  339. return true;
  340. }
  341. return false;
  342. }
  343. const OPT<SHAPE_LINE_CHAIN::INTERSECTION> SHAPE_LINE_CHAIN::SelfIntersecting() const
  344. {
  345. for( int s1 = 0; s1 < SegmentCount(); s1++ )
  346. {
  347. for( int s2 = s1 + 1; s2 < SegmentCount(); s2++ )
  348. {
  349. const VECTOR2I s2a = CSegment( s2 ).A, s2b = CSegment( s2 ).B;
  350. if( s1 + 1 != s2 && CSegment( s1 ).Contains( s2a ) )
  351. {
  352. INTERSECTION is;
  353. is.our = CSegment( s1 );
  354. is.their = CSegment( s2 );
  355. is.p = s2a;
  356. return is;
  357. }
  358. else if( CSegment( s1 ).Contains( s2b ) &&
  359. // for closed polylines, the ending point of the
  360. // last segment == starting point of the first segment
  361. // this is a normal case, not self intersecting case
  362. !( IsClosed() && s1 == 0 && s2 == SegmentCount()-1 ) )
  363. {
  364. INTERSECTION is;
  365. is.our = CSegment( s1 );
  366. is.their = CSegment( s2 );
  367. is.p = s2b;
  368. return is;
  369. }
  370. else
  371. {
  372. OPT_VECTOR2I p = CSegment( s1 ).Intersect( CSegment( s2 ), true );
  373. if( p )
  374. {
  375. INTERSECTION is;
  376. is.our = CSegment( s1 );
  377. is.their = CSegment( s2 );
  378. is.p = *p;
  379. return is;
  380. }
  381. }
  382. }
  383. }
  384. return OPT<SHAPE_LINE_CHAIN::INTERSECTION>();
  385. }
  386. SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify()
  387. {
  388. std::vector<VECTOR2I> pts_unique;
  389. if( PointCount() < 2 )
  390. {
  391. return *this;
  392. }
  393. else if( PointCount() == 2 )
  394. {
  395. if( m_points[0] == m_points[1] )
  396. m_points.pop_back();
  397. return *this;
  398. }
  399. int i = 0;
  400. int np = PointCount();
  401. // stage 1: eliminate duplicate vertices
  402. while( i < np )
  403. {
  404. int j = i + 1;
  405. while( j < np && CPoint( i ) == CPoint( j ) )
  406. j++;
  407. pts_unique.push_back( CPoint( i ) );
  408. i = j;
  409. }
  410. m_points.clear();
  411. np = pts_unique.size();
  412. i = 0;
  413. // stage 1: eliminate collinear segments
  414. while( i < np - 2 )
  415. {
  416. const VECTOR2I p0 = pts_unique[i];
  417. const VECTOR2I p1 = pts_unique[i + 1];
  418. int n = i;
  419. while( n < np - 2 && SEG( p0, p1 ).LineDistance( pts_unique[n + 2] ) <= 1 )
  420. n++;
  421. m_points.push_back( p0 );
  422. if( n > i )
  423. i = n;
  424. if( n == np )
  425. {
  426. m_points.push_back( pts_unique[n - 1] );
  427. return *this;
  428. }
  429. i++;
  430. }
  431. if( np > 1 )
  432. m_points.push_back( pts_unique[np - 2] );
  433. m_points.push_back( pts_unique[np - 1] );
  434. return *this;
  435. }
  436. const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint( const VECTOR2I& aP ) const
  437. {
  438. int min_d = INT_MAX;
  439. int nearest = 0;
  440. for( int i = 0; i < SegmentCount(); i++ )
  441. {
  442. int d = CSegment( i ).Distance( aP );
  443. if( d < min_d )
  444. {
  445. min_d = d;
  446. nearest = i;
  447. }
  448. }
  449. return CSegment( nearest ).NearestPoint( aP );
  450. }
  451. const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint( const SEG& aSeg, int& dist ) const
  452. {
  453. int nearest = 0;
  454. dist = INT_MAX;
  455. for( int i = 0; i < PointCount(); i++ )
  456. {
  457. int d = aSeg.LineDistance( CPoint( i ) );
  458. if( d < dist )
  459. {
  460. dist = d;
  461. nearest = i;
  462. }
  463. }
  464. return CPoint( nearest );
  465. }
  466. const std::string SHAPE_LINE_CHAIN::Format() const
  467. {
  468. std::stringstream ss;
  469. ss << m_points.size() << " " << ( m_closed ? 1 : 0 ) << " ";
  470. for( int i = 0; i < PointCount(); i++ )
  471. ss << m_points[i].x << " " << m_points[i].y << " "; // Format() << " ";
  472. return ss.str();
  473. }
  474. bool SHAPE_LINE_CHAIN::CompareGeometry ( const SHAPE_LINE_CHAIN & aOther ) const
  475. {
  476. SHAPE_LINE_CHAIN a(*this), b( aOther );
  477. a.Simplify();
  478. b.Simplify();
  479. if( a.m_points.size() != b.m_points.size() )
  480. return false;
  481. for( int i = 0; i < a.PointCount(); i++)
  482. if( a.CPoint( i ) != b.CPoint( i ) )
  483. return false;
  484. return true;
  485. }
  486. bool SHAPE_LINE_CHAIN::Intersects( const SHAPE_LINE_CHAIN& aChain ) const
  487. {
  488. INTERSECTIONS dummy;
  489. return Intersect( aChain, dummy ) != 0;
  490. }
  491. SHAPE* SHAPE_LINE_CHAIN::Clone() const
  492. {
  493. return new SHAPE_LINE_CHAIN( *this );
  494. }
  495. bool SHAPE_LINE_CHAIN::Parse( std::stringstream& aStream )
  496. {
  497. int n_pts;
  498. m_points.clear();
  499. aStream >> n_pts;
  500. // Rough sanity check, just make sure the loop bounds aren't absolutely outlandish
  501. if( n_pts < 0 || n_pts > int( aStream.str().size() ) )
  502. return false;
  503. aStream >> m_closed;
  504. for( int i = 0; i < n_pts; i++ )
  505. {
  506. int x, y;
  507. aStream >> x;
  508. aStream >> y;
  509. m_points.push_back( VECTOR2I( x, y ) );
  510. }
  511. return true;
  512. }
  513. const VECTOR2I SHAPE_LINE_CHAIN::PointAlong( int aPathLength ) const
  514. {
  515. int total = 0;
  516. if( aPathLength == 0 )
  517. return CPoint( 0 );
  518. for( int i = 0; i < SegmentCount(); i++ )
  519. {
  520. const SEG& s = CSegment( i );
  521. int l = s.Length();
  522. if( total + l >= aPathLength )
  523. {
  524. VECTOR2I d( s.B - s.A );
  525. return s.A + d.Resize( aPathLength - total );
  526. }
  527. total += l;
  528. }
  529. return CPoint( -1 );
  530. }
  531. double SHAPE_LINE_CHAIN::Area() const
  532. {
  533. // see https://www.mathopenref.com/coordpolygonarea2.html
  534. if( !m_closed )
  535. return 0.0;
  536. double area = 0.0;
  537. int size = m_points.size();
  538. for( int i = 0, j = size - 1; i < size; ++i )
  539. {
  540. area += ( (double) m_points[j].x + m_points[i].x ) * ( (double) m_points[j].y - m_points[i].y );
  541. j = i;
  542. }
  543. return -area * 0.5;
  544. }