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.

1673 lines
47 KiB

4 years ago
4 years ago
4 years ago
4 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
  7. * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
  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. #include <bezier_curves.h>
  27. #include <base_units.h>
  28. #include <convert_basic_shapes_to_polygon.h>
  29. #include <eda_draw_frame.h>
  30. #include <geometry/shape_simple.h>
  31. #include <geometry/shape_segment.h>
  32. #include <geometry/shape_circle.h>
  33. #include <macros.h>
  34. #include <math/util.h> // for KiROUND
  35. #include <eda_shape.h>
  36. #include <plotters/plotter.h>
  37. EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) :
  38. m_endsSwapped( false ),
  39. m_shape( aType ),
  40. m_stroke( aLineWidth, PLOT_DASH_TYPE::DEFAULT, COLOR4D::UNSPECIFIED ),
  41. m_fill( aFill ),
  42. m_fillColor( COLOR4D::UNSPECIFIED ),
  43. m_editState( 0 ),
  44. m_annotationProxy( false )
  45. {
  46. }
  47. EDA_SHAPE::~EDA_SHAPE()
  48. {
  49. }
  50. wxString EDA_SHAPE::ShowShape() const
  51. {
  52. if( IsAnnotationProxy() )
  53. return _( "Number Box" );
  54. switch( m_shape )
  55. {
  56. case SHAPE_T::SEGMENT: return _( "Line" );
  57. case SHAPE_T::RECT: return _( "Rect" );
  58. case SHAPE_T::ARC: return _( "Arc" );
  59. case SHAPE_T::CIRCLE: return _( "Circle" );
  60. case SHAPE_T::BEZIER: return _( "Bezier Curve" );
  61. case SHAPE_T::POLY: return _( "Polygon" );
  62. default: return wxT( "??" );
  63. }
  64. }
  65. wxString EDA_SHAPE::SHAPE_T_asString() const
  66. {
  67. switch( m_shape )
  68. {
  69. case SHAPE_T::SEGMENT: return "S_SEGMENT";
  70. case SHAPE_T::RECT: return "S_RECT";
  71. case SHAPE_T::ARC: return "S_ARC";
  72. case SHAPE_T::CIRCLE: return "S_CIRCLE";
  73. case SHAPE_T::POLY: return "S_POLYGON";
  74. case SHAPE_T::BEZIER: return "S_CURVE";
  75. case SHAPE_T::LAST: return "!S_LAST!"; // Synthetic value, but if we come across it then
  76. // we're going to want to know.
  77. }
  78. return wxEmptyString; // Just to quiet GCC.
  79. }
  80. void EDA_SHAPE::setPosition( const VECTOR2I& aPos )
  81. {
  82. move( aPos - getPosition() );
  83. }
  84. VECTOR2I EDA_SHAPE::getPosition() const
  85. {
  86. if( m_shape == SHAPE_T::ARC )
  87. return getCenter();
  88. else if( m_shape == SHAPE_T::POLY )
  89. return m_poly.CVertex( 0 );
  90. else
  91. return m_start;
  92. }
  93. double EDA_SHAPE::GetLength() const
  94. {
  95. double length = 0.0;
  96. switch( m_shape )
  97. {
  98. case SHAPE_T::BEZIER:
  99. for( size_t ii = 1; ii < m_bezierPoints.size(); ++ii )
  100. length += GetLineLength( m_bezierPoints[ ii - 1], m_bezierPoints[ii] );
  101. return length;
  102. case SHAPE_T::SEGMENT:
  103. return GetLineLength( GetStart(), GetEnd() );
  104. case SHAPE_T::POLY:
  105. for( int ii = 0; ii < m_poly.COutline( 0 ).SegmentCount(); ii++ )
  106. length += m_poly.COutline( 0 ).CSegment( ii ).Length();
  107. return length;
  108. case SHAPE_T::ARC:
  109. return GetRadius() * GetArcAngle().AsRadians();
  110. default:
  111. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  112. return 0.0;
  113. }
  114. }
  115. void EDA_SHAPE::move( const VECTOR2I& aMoveVector )
  116. {
  117. switch ( m_shape )
  118. {
  119. case SHAPE_T::ARC:
  120. case SHAPE_T::SEGMENT:
  121. case SHAPE_T::RECT:
  122. case SHAPE_T::CIRCLE:
  123. m_start += aMoveVector;
  124. m_end += aMoveVector;
  125. m_arcCenter += aMoveVector;
  126. break;
  127. case SHAPE_T::POLY:
  128. m_poly.Move( VECTOR2I( aMoveVector ) );
  129. break;
  130. case SHAPE_T::BEZIER:
  131. m_start += aMoveVector;
  132. m_end += aMoveVector;
  133. m_bezierC1 += aMoveVector;
  134. m_bezierC2 += aMoveVector;
  135. for( VECTOR2I& pt : m_bezierPoints )
  136. pt += aMoveVector;
  137. break;
  138. default:
  139. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  140. break;
  141. }
  142. }
  143. void EDA_SHAPE::scale( double aScale )
  144. {
  145. auto scalePt = [&]( VECTOR2I& pt )
  146. {
  147. pt.x = KiROUND( pt.x * aScale );
  148. pt.y = KiROUND( pt.y * aScale );
  149. };
  150. switch( m_shape )
  151. {
  152. case SHAPE_T::ARC:
  153. case SHAPE_T::SEGMENT:
  154. case SHAPE_T::RECT:
  155. scalePt( m_start );
  156. scalePt( m_end );
  157. scalePt( m_arcCenter );
  158. break;
  159. case SHAPE_T::CIRCLE: // ring or circle
  160. scalePt( m_start );
  161. m_end.x = m_start.x + KiROUND( GetRadius() * aScale );
  162. m_end.y = m_start.y;
  163. break;
  164. case SHAPE_T::POLY: // polygon
  165. {
  166. std::vector<VECTOR2I> pts;
  167. for( int ii = 0; ii < m_poly.OutlineCount(); ++ ii )
  168. {
  169. for( const VECTOR2I& pt : m_poly.Outline( ii ).CPoints() )
  170. {
  171. pts.emplace_back( pt );
  172. scalePt( pts.back() );
  173. }
  174. }
  175. SetPolyPoints( pts );
  176. }
  177. break;
  178. case SHAPE_T::BEZIER:
  179. scalePt( m_start );
  180. scalePt( m_end );
  181. scalePt( m_bezierC1 );
  182. scalePt( m_bezierC2 );
  183. break;
  184. default:
  185. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  186. break;
  187. }
  188. }
  189. void EDA_SHAPE::rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  190. {
  191. switch( m_shape )
  192. {
  193. case SHAPE_T::SEGMENT:
  194. case SHAPE_T::CIRCLE:
  195. RotatePoint( m_start, aRotCentre, aAngle );
  196. RotatePoint( m_end, aRotCentre, aAngle );
  197. break;
  198. case SHAPE_T::ARC:
  199. RotatePoint( m_start, aRotCentre, aAngle );
  200. RotatePoint( m_end, aRotCentre, aAngle );
  201. RotatePoint( m_arcCenter, aRotCentre, aAngle );
  202. break;
  203. case SHAPE_T::RECT:
  204. if( aAngle.IsCardinal() )
  205. {
  206. RotatePoint( m_start, aRotCentre, aAngle );
  207. RotatePoint( m_end, aRotCentre, aAngle );
  208. break;
  209. }
  210. // Convert non-cardinally-rotated rect to a diamond
  211. m_shape = SHAPE_T::POLY;
  212. m_poly.RemoveAllContours();
  213. m_poly.NewOutline();
  214. m_poly.Append( m_start );
  215. m_poly.Append( m_end.x, m_start.y );
  216. m_poly.Append( m_end );
  217. m_poly.Append( m_start.x, m_end.y );
  218. KI_FALLTHROUGH;
  219. case SHAPE_T::POLY:
  220. m_poly.Rotate( aAngle, aRotCentre );
  221. break;
  222. case SHAPE_T::BEZIER:
  223. RotatePoint( m_start, aRotCentre, aAngle );
  224. RotatePoint( m_end, aRotCentre, aAngle );
  225. RotatePoint( m_bezierC1, aRotCentre, aAngle );
  226. RotatePoint( m_bezierC2, aRotCentre, aAngle );
  227. for( VECTOR2I& pt : m_bezierPoints )
  228. RotatePoint( pt, aRotCentre, aAngle);
  229. break;
  230. default:
  231. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  232. break;
  233. }
  234. }
  235. void EDA_SHAPE::flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
  236. {
  237. switch ( m_shape )
  238. {
  239. case SHAPE_T::SEGMENT:
  240. case SHAPE_T::RECT:
  241. if( aFlipLeftRight )
  242. {
  243. m_start.x = aCentre.x - ( m_start.x - aCentre.x );
  244. m_end.x = aCentre.x - ( m_end.x - aCentre.x );
  245. }
  246. else
  247. {
  248. m_start.y = aCentre.y - ( m_start.y - aCentre.y );
  249. m_end.y = aCentre.y - ( m_end.y - aCentre.y );
  250. }
  251. std::swap( m_start, m_end );
  252. break;
  253. case SHAPE_T::CIRCLE:
  254. if( aFlipLeftRight )
  255. {
  256. m_start.x = aCentre.x - ( m_start.x - aCentre.x );
  257. m_end.x = aCentre.x - ( m_end.x - aCentre.x );
  258. }
  259. else
  260. {
  261. m_start.y = aCentre.y - ( m_start.y - aCentre.y );
  262. m_end.y = aCentre.y - ( m_end.y - aCentre.y );
  263. }
  264. break;
  265. case SHAPE_T::ARC:
  266. if( aFlipLeftRight )
  267. {
  268. m_start.x = aCentre.x - ( m_start.x - aCentre.x );
  269. m_end.x = aCentre.x - ( m_end.x - aCentre.x );
  270. m_arcCenter.x = aCentre.x - ( m_arcCenter.x - aCentre.x );
  271. }
  272. else
  273. {
  274. m_start.y = aCentre.y - ( m_start.y - aCentre.y );
  275. m_end.y = aCentre.y - ( m_end.y - aCentre.y );
  276. m_arcCenter.y = aCentre.y - ( m_arcCenter.y - aCentre.y );
  277. }
  278. std::swap( m_start, m_end );
  279. break;
  280. case SHAPE_T::POLY:
  281. m_poly.Mirror( aFlipLeftRight, !aFlipLeftRight, aCentre );
  282. break;
  283. case SHAPE_T::BEZIER:
  284. if( aFlipLeftRight )
  285. {
  286. m_start.x = aCentre.x - ( m_start.x - aCentre.x );
  287. m_end.x = aCentre.x - ( m_end.x - aCentre.x );
  288. m_bezierC1.x = aCentre.x - ( m_bezierC1.x - aCentre.x );
  289. m_bezierC2.x = aCentre.x - ( m_bezierC2.x - aCentre.x );
  290. }
  291. else
  292. {
  293. m_start.y = aCentre.y - ( m_start.y - aCentre.y );
  294. m_end.y = aCentre.y - ( m_end.y - aCentre.y );
  295. m_bezierC1.y = aCentre.y - ( m_bezierC1.y - aCentre.y );
  296. m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y );
  297. }
  298. // Rebuild the poly points shape
  299. {
  300. std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
  301. BEZIER_POLY converter( ctrlPoints );
  302. converter.GetPoly( m_bezierPoints, m_stroke.GetWidth() );
  303. }
  304. break;
  305. default:
  306. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  307. break;
  308. }
  309. }
  310. void EDA_SHAPE::RebuildBezierToSegmentsPointsList( int aMinSegLen )
  311. {
  312. // Has meaning only for SHAPE_T::BEZIER
  313. if( m_shape != SHAPE_T::BEZIER )
  314. {
  315. m_bezierPoints.clear();
  316. return;
  317. }
  318. // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
  319. m_bezierPoints = buildBezierToSegmentsPointsList( aMinSegLen );
  320. }
  321. const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMinSegLen ) const
  322. {
  323. std::vector<VECTOR2I> bezierPoints;
  324. // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
  325. std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
  326. BEZIER_POLY converter( ctrlPoints );
  327. converter.GetPoly( bezierPoints, aMinSegLen );
  328. return bezierPoints;
  329. }
  330. VECTOR2I EDA_SHAPE::getCenter() const
  331. {
  332. switch( m_shape )
  333. {
  334. case SHAPE_T::ARC:
  335. return m_arcCenter;
  336. case SHAPE_T::CIRCLE:
  337. return m_start;
  338. case SHAPE_T::SEGMENT:
  339. // Midpoint of the line
  340. return ( m_start + m_end ) / 2;
  341. case SHAPE_T::POLY:
  342. case SHAPE_T::RECT:
  343. case SHAPE_T::BEZIER:
  344. return getBoundingBox().Centre();
  345. default:
  346. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  347. return VECTOR2I();
  348. }
  349. }
  350. void EDA_SHAPE::SetCenter( const VECTOR2I& aCenter )
  351. {
  352. switch( m_shape )
  353. {
  354. case SHAPE_T::ARC:
  355. m_arcCenter = aCenter;
  356. break;
  357. case SHAPE_T::CIRCLE:
  358. m_start = aCenter;
  359. break;
  360. default:
  361. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  362. }
  363. }
  364. VECTOR2I EDA_SHAPE::GetArcMid() const
  365. {
  366. // If none of the input data have changed since we loaded the arc,
  367. // keep the original mid point data to minimize churn
  368. if( m_arcMidData.start == m_start && m_arcMidData.end == m_end
  369. && m_arcMidData.center == m_arcCenter )
  370. return m_arcMidData.mid;
  371. VECTOR2I mid = m_start;
  372. RotatePoint( mid, m_arcCenter, -GetArcAngle() / 2.0 );
  373. return mid;
  374. }
  375. void EDA_SHAPE::CalcArcAngles( EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle ) const
  376. {
  377. VECTOR2D startRadial( GetStart() - getCenter() );
  378. VECTOR2D endRadial( GetEnd() - getCenter() );
  379. aStartAngle = EDA_ANGLE( startRadial );
  380. aEndAngle = EDA_ANGLE( endRadial );
  381. if( aEndAngle == aStartAngle )
  382. aEndAngle = aStartAngle + ANGLE_360; // ring, not null
  383. if( aStartAngle > aEndAngle )
  384. {
  385. if( aEndAngle < ANGLE_0 )
  386. aEndAngle.Normalize();
  387. else
  388. aStartAngle = aStartAngle.Normalize() - ANGLE_360;
  389. }
  390. }
  391. int EDA_SHAPE::GetRadius() const
  392. {
  393. double radius = 0.0;
  394. switch( m_shape )
  395. {
  396. case SHAPE_T::ARC:
  397. radius = GetLineLength( m_arcCenter, m_start );
  398. break;
  399. case SHAPE_T::CIRCLE:
  400. radius = GetLineLength( m_start, m_end );
  401. break;
  402. default:
  403. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  404. }
  405. // don't allow degenerate circles/arcs
  406. return std::max( 1, KiROUND( radius ) );
  407. }
  408. void EDA_SHAPE::SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCenter )
  409. {
  410. m_arcMidData.start = aStart;
  411. m_arcMidData.end = aEnd;
  412. m_arcMidData.center = aCenter;
  413. m_arcMidData.mid = aMid;
  414. }
  415. void EDA_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
  416. {
  417. m_arcMidData = {};
  418. m_start = aStart;
  419. m_end = aEnd;
  420. m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
  421. VECTOR2I new_mid = GetArcMid();
  422. m_endsSwapped = false;
  423. // Watch the ordering here. GetArcMid above needs to be called prior to initializing the
  424. // m_arcMidData structure in order to ensure we get the calculated variant, not the cached
  425. SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
  426. /*
  427. * If the input winding doesn't match our internal winding, the calculated midpoint will end
  428. * up on the other side of the arc. In this case, we need to flip the start/end points and
  429. * flag this change for the system.
  430. */
  431. VECTOR2D dist( new_mid - aMid );
  432. VECTOR2D dist2( new_mid - m_arcCenter );
  433. if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
  434. {
  435. std::swap( m_start, m_end );
  436. m_endsSwapped = true;
  437. }
  438. }
  439. EDA_ANGLE EDA_SHAPE::GetArcAngle() const
  440. {
  441. EDA_ANGLE startAngle;
  442. EDA_ANGLE endAngle;
  443. CalcArcAngles( startAngle, endAngle );
  444. return endAngle - startAngle;
  445. }
  446. void EDA_SHAPE::SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeAngle )
  447. {
  448. EDA_ANGLE angle( aAngle );
  449. m_end = m_start;
  450. RotatePoint( m_end, m_arcCenter, -angle.Normalize720() );
  451. if( aCheckNegativeAngle && aAngle < ANGLE_0 )
  452. {
  453. std::swap( m_start, m_end );
  454. m_endsSwapped = true;
  455. }
  456. }
  457. void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  458. {
  459. ORIGIN_TRANSFORMS originTransforms = aFrame->GetOriginTransforms();
  460. wxString msg;
  461. wxString shape = _( "Shape" );
  462. switch( m_shape )
  463. {
  464. case SHAPE_T::CIRCLE:
  465. aList.emplace_back( shape, _( "Circle" ) );
  466. aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
  467. break;
  468. case SHAPE_T::ARC:
  469. aList.emplace_back( shape, _( "Arc" ) );
  470. msg = EDA_UNIT_UTILS::UI::MessageTextFromValue( GetArcAngle() );
  471. aList.emplace_back( _( "Angle" ), msg );
  472. aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
  473. break;
  474. case SHAPE_T::BEZIER:
  475. aList.emplace_back( shape, _( "Curve" ) );
  476. aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
  477. break;
  478. case SHAPE_T::POLY:
  479. aList.emplace_back( shape, _( "Polygon" ) );
  480. msg.Printf( "%d", GetPolyShape().Outline(0).PointCount() );
  481. aList.emplace_back( _( "Points" ), msg );
  482. break;
  483. case SHAPE_T::RECT:
  484. if( IsAnnotationProxy() )
  485. aList.emplace_back( shape, _( "Pad Number Box" ) );
  486. else
  487. aList.emplace_back( shape, _( "Rectangle" ) );
  488. aList.emplace_back( _( "Width" ),
  489. aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
  490. aList.emplace_back( _( "Height" ),
  491. aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
  492. break;
  493. case SHAPE_T::SEGMENT:
  494. {
  495. aList.emplace_back( shape, _( "Segment" ) );
  496. aList.emplace_back( _( "Length" ),
  497. aFrame->MessageTextFromValue( GetLineLength( GetStart(), GetEnd() ) ));
  498. // angle counter-clockwise from 3'o-clock
  499. EDA_ANGLE angle( atan2( (double)( GetStart().y - GetEnd().y ),
  500. (double)( GetEnd().x - GetStart().x ) ), RADIANS_T );
  501. aList.emplace_back( _( "Angle" ), EDA_UNIT_UTILS::UI::MessageTextFromValue( angle ) );
  502. break;
  503. }
  504. default:
  505. aList.emplace_back( shape, _( "Unrecognized" ) );
  506. break;
  507. }
  508. m_stroke.GetMsgPanelInfo( aFrame, aList );
  509. }
  510. const BOX2I EDA_SHAPE::getBoundingBox() const
  511. {
  512. BOX2I bbox;
  513. switch( m_shape )
  514. {
  515. case SHAPE_T::RECT:
  516. for( VECTOR2I& pt : GetRectCorners() )
  517. bbox.Merge( pt );
  518. break;
  519. case SHAPE_T::SEGMENT:
  520. bbox.SetOrigin( GetStart() );
  521. bbox.SetEnd( GetEnd() );
  522. break;
  523. case SHAPE_T::CIRCLE:
  524. bbox.SetOrigin( GetStart() );
  525. bbox.Inflate( GetRadius() );
  526. break;
  527. case SHAPE_T::ARC:
  528. computeArcBBox( bbox );
  529. break;
  530. case SHAPE_T::POLY:
  531. if( m_poly.IsEmpty() )
  532. break;
  533. for( auto iter = m_poly.CIterate(); iter; iter++ )
  534. {
  535. VECTOR2I pt( iter->x, iter->y );
  536. RotatePoint( pt, getParentOrientation() );
  537. pt += getParentPosition();
  538. bbox.Merge( pt );
  539. }
  540. break;
  541. case SHAPE_T::BEZIER:
  542. bbox.SetOrigin( GetStart() );
  543. bbox.Merge( GetBezierC1() );
  544. bbox.Merge( GetBezierC2() );
  545. bbox.Merge( GetEnd() );
  546. break;
  547. default:
  548. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  549. break;
  550. }
  551. bbox.Inflate( std::max( 0, GetWidth() ) / 2 );
  552. bbox.Normalize();
  553. return bbox;
  554. }
  555. bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  556. {
  557. int maxdist = aAccuracy;
  558. if( GetWidth() > 0 )
  559. maxdist += GetWidth() / 2;
  560. switch( m_shape )
  561. {
  562. case SHAPE_T::CIRCLE:
  563. {
  564. int radius = GetRadius();
  565. VECTOR2I::extended_type dist = KiROUND<double, VECTOR2I::extended_type>(
  566. EuclideanNorm( aPosition - getCenter() ) );
  567. if( IsFilled() )
  568. return dist <= radius + maxdist; // Filled circle hit-test
  569. else
  570. return abs( radius - dist ) <= maxdist; // Ring hit-test
  571. }
  572. case SHAPE_T::ARC:
  573. {
  574. if( EuclideanNorm( aPosition - m_start ) <= maxdist )
  575. return true;
  576. if( EuclideanNorm( aPosition - m_end ) <= maxdist )
  577. return true;
  578. VECTOR2I relPos = aPosition - getCenter();
  579. int radius = GetRadius();
  580. VECTOR2I::extended_type dist =
  581. KiROUND<double, VECTOR2I::extended_type>( EuclideanNorm( relPos ) );
  582. if( IsFilled() )
  583. {
  584. // Check distance from arc center
  585. if( dist > radius + maxdist )
  586. return false;
  587. }
  588. else
  589. {
  590. // Check distance from arc circumference
  591. if( abs( radius - dist ) > maxdist )
  592. return false;
  593. }
  594. // Finally, check to see if it's within arc's swept angle.
  595. EDA_ANGLE startAngle;
  596. EDA_ANGLE endAngle;
  597. CalcArcAngles( startAngle, endAngle );
  598. EDA_ANGLE relPosAngle( relPos );
  599. startAngle.Normalize();
  600. endAngle.Normalize();
  601. relPosAngle.Normalize();
  602. if( endAngle > startAngle )
  603. return relPosAngle >= startAngle && relPosAngle <= endAngle;
  604. else
  605. return relPosAngle >= startAngle || relPosAngle <= endAngle;
  606. }
  607. case SHAPE_T::BEZIER:
  608. const_cast<EDA_SHAPE*>( this )->RebuildBezierToSegmentsPointsList( GetWidth() );
  609. for( unsigned int i= 1; i < m_bezierPoints.size(); i++)
  610. {
  611. if( TestSegmentHit( aPosition, m_bezierPoints[ i - 1], m_bezierPoints[i], maxdist ) )
  612. return true;
  613. }
  614. return false;
  615. case SHAPE_T::SEGMENT:
  616. return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
  617. case SHAPE_T::RECT:
  618. if( IsAnnotationProxy() || IsFilled() ) // Filled rect hit-test
  619. {
  620. SHAPE_POLY_SET poly;
  621. poly.NewOutline();
  622. for( const VECTOR2I& pt : GetRectCorners() )
  623. poly.Append( pt );
  624. return poly.Collide( aPosition, maxdist );
  625. }
  626. else // Open rect hit-test
  627. {
  628. std::vector<VECTOR2I> pts = GetRectCorners();
  629. return TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
  630. || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
  631. || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
  632. || TestSegmentHit( aPosition, pts[3], pts[0], maxdist );
  633. }
  634. case SHAPE_T::POLY:
  635. if( IsFilled() )
  636. return m_poly.Collide( aPosition, maxdist );
  637. else
  638. return m_poly.CollideEdge( aPosition, nullptr, maxdist );
  639. default:
  640. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  641. return false;
  642. }
  643. }
  644. bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  645. {
  646. BOX2I arect = aRect;
  647. arect.Normalize();
  648. arect.Inflate( aAccuracy );
  649. BOX2I bbox = getBoundingBox();
  650. switch( m_shape )
  651. {
  652. case SHAPE_T::CIRCLE:
  653. // Test if area intersects or contains the circle:
  654. if( aContained )
  655. {
  656. return arect.Contains( bbox );
  657. }
  658. else
  659. {
  660. // If the rectangle does not intersect the bounding box, this is a much quicker test
  661. if( !arect.Intersects( bbox ) )
  662. return false;
  663. else
  664. return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
  665. }
  666. case SHAPE_T::ARC:
  667. // Test for full containment of this arc in the rect
  668. if( aContained )
  669. {
  670. return arect.Contains( bbox );
  671. }
  672. // Test if the rect crosses the arc
  673. else
  674. {
  675. if( !arect.Intersects( bbox ) )
  676. return false;
  677. if( IsFilled() )
  678. {
  679. return ( arect.Intersects( getCenter(), GetStart() )
  680. || arect.Intersects( getCenter(), GetEnd() )
  681. || arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ) );
  682. }
  683. else
  684. {
  685. return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
  686. }
  687. }
  688. case SHAPE_T::RECT:
  689. if( aContained )
  690. {
  691. return arect.Contains( bbox );
  692. }
  693. else
  694. {
  695. std::vector<VECTOR2I> pts = GetRectCorners();
  696. // Account for the width of the lines
  697. arect.Inflate( GetWidth() / 2 );
  698. return ( arect.Intersects( pts[0], pts[1] )
  699. || arect.Intersects( pts[1], pts[2] )
  700. || arect.Intersects( pts[2], pts[3] )
  701. || arect.Intersects( pts[3], pts[0] ) );
  702. }
  703. case SHAPE_T::SEGMENT:
  704. if( aContained )
  705. {
  706. return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
  707. }
  708. else
  709. {
  710. // Account for the width of the line
  711. arect.Inflate( GetWidth() / 2 );
  712. return arect.Intersects( GetStart(), GetEnd() );
  713. }
  714. case SHAPE_T::POLY:
  715. if( aContained )
  716. {
  717. return arect.Contains( bbox );
  718. }
  719. else
  720. {
  721. // Fast test: if aRect is outside the polygon bounding box,
  722. // rectangles cannot intersect
  723. if( !arect.Intersects( bbox ) )
  724. return false;
  725. // Account for the width of the line
  726. arect.Inflate( GetWidth() / 2 );
  727. // Polygons in footprints use coordinates relative to the footprint.
  728. // Therefore, instead of using m_poly, we make a copy which is translated
  729. // to the actual location in the board.
  730. VECTOR2I offset = getParentPosition();
  731. for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
  732. {
  733. SHAPE_LINE_CHAIN poly = m_poly.Outline( ii );
  734. poly.Rotate( getParentOrientation() );
  735. poly.Move( offset );
  736. int count = poly.GetPointCount();
  737. for( int jj = 0; jj < count; jj++ )
  738. {
  739. VECTOR2I vertex = poly.GetPoint( jj );
  740. // Test if the point is within aRect
  741. if( arect.Contains( vertex ) )
  742. return true;
  743. if( jj + 1 < count )
  744. {
  745. VECTOR2I vertexNext = poly.GetPoint( jj + 1 );
  746. // Test if this edge intersects aRect
  747. if( arect.Intersects( vertex, vertexNext ) )
  748. return true;
  749. }
  750. else if( poly.IsClosed() )
  751. {
  752. VECTOR2I vertexNext = poly.GetPoint( 0 );
  753. // Test if this edge intersects aRect
  754. if( arect.Intersects( vertex, vertexNext ) )
  755. return true;
  756. }
  757. }
  758. }
  759. return false;
  760. }
  761. case SHAPE_T::BEZIER:
  762. if( aContained )
  763. {
  764. return arect.Contains( bbox );
  765. }
  766. else
  767. {
  768. // Fast test: if aRect is outside the polygon bounding box,
  769. // rectangles cannot intersect
  770. if( !arect.Intersects( bbox ) )
  771. return false;
  772. // Account for the width of the line
  773. arect.Inflate( GetWidth() / 2 );
  774. unsigned count = m_bezierPoints.size();
  775. for( unsigned ii = 1; ii < count; ii++ )
  776. {
  777. VECTOR2I vertex = m_bezierPoints[ii - 1];
  778. VECTOR2I vertexNext = m_bezierPoints[ii];
  779. // Test if the point is within aRect
  780. if( arect.Contains( vertex ) )
  781. return true;
  782. // Test if this edge intersects aRect
  783. if( arect.Intersects( vertex, vertexNext ) )
  784. return true;
  785. }
  786. return false;
  787. }
  788. default:
  789. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  790. return false;
  791. }
  792. }
  793. std::vector<VECTOR2I> EDA_SHAPE::GetRectCorners() const
  794. {
  795. std::vector<VECTOR2I> pts;
  796. VECTOR2I topLeft = GetStart();
  797. VECTOR2I botRight = GetEnd();
  798. // Un-rotate rect topLeft and botRight
  799. if( !getParentOrientation().IsCardinal() )
  800. {
  801. topLeft -= getParentPosition();
  802. RotatePoint( topLeft, -getParentOrientation() );
  803. botRight -= getParentPosition();
  804. RotatePoint( botRight, -getParentOrientation() );
  805. }
  806. // Set up the un-rotated 4 corners
  807. pts.emplace_back( topLeft );
  808. pts.emplace_back( botRight.x, topLeft.y );
  809. pts.emplace_back( botRight );
  810. pts.emplace_back( topLeft.x, botRight.y );
  811. // Now re-rotate the 4 corners to get a diamond
  812. if( !getParentOrientation().IsCardinal() )
  813. {
  814. for( VECTOR2I& pt : pts )
  815. {
  816. RotatePoint( pt, getParentOrientation() );
  817. pt += getParentPosition();
  818. }
  819. }
  820. return pts;
  821. }
  822. void EDA_SHAPE::computeArcBBox( BOX2I& aBBox ) const
  823. {
  824. // Start, end, and each inflection point the arc crosses will enclose the entire arc.
  825. // Only include the center when filled; it's not necessarily inside the BB of an unfilled
  826. // arc with a small included angle.
  827. aBBox.SetOrigin( m_start );
  828. aBBox.Merge( m_end );
  829. if( IsFilled() )
  830. aBBox.Merge( m_arcCenter );
  831. int radius = GetRadius();
  832. EDA_ANGLE t1, t2;
  833. CalcArcAngles( t1, t2 );
  834. t1.Normalize();
  835. t2.Normalize();
  836. if( t2 > t1 )
  837. {
  838. if( t1 < ANGLE_0 && t2 > ANGLE_0 )
  839. aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
  840. if( t1 < ANGLE_90 && t2 > ANGLE_90 )
  841. aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
  842. if( t1 < ANGLE_180 && t2 > ANGLE_180 )
  843. aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
  844. if( t1 < ANGLE_270 && t2 > ANGLE_270 )
  845. aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
  846. }
  847. else
  848. {
  849. if( t1 < ANGLE_0 || t2 > ANGLE_0 )
  850. aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
  851. if( t1 < ANGLE_90 || t2 > ANGLE_90 )
  852. aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
  853. if( t1 < ANGLE_180 || t2 > ANGLE_180 )
  854. aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
  855. if( t1 < ANGLE_270 || t2 > ANGLE_270 )
  856. aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
  857. }
  858. }
  859. void EDA_SHAPE::SetPolyPoints( const std::vector<VECTOR2I>& aPoints )
  860. {
  861. m_poly.RemoveAllContours();
  862. m_poly.NewOutline();
  863. for( const VECTOR2I& p : aPoints )
  864. m_poly.Append( p.x, p.y );
  865. }
  866. std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly ) const
  867. {
  868. std::vector<SHAPE*> effectiveShapes;
  869. int width = GetEffectiveWidth();
  870. switch( m_shape )
  871. {
  872. case SHAPE_T::ARC:
  873. effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle(), width ) );
  874. break;
  875. case SHAPE_T::SEGMENT:
  876. effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, width ) );
  877. break;
  878. case SHAPE_T::RECT:
  879. {
  880. std::vector<VECTOR2I> pts = GetRectCorners();
  881. if( ( IsFilled() || IsAnnotationProxy() ) && !aEdgeOnly )
  882. effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
  883. if( width > 0 || !IsFilled() || aEdgeOnly )
  884. {
  885. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
  886. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
  887. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
  888. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
  889. }
  890. }
  891. break;
  892. case SHAPE_T::CIRCLE:
  893. {
  894. if( IsFilled() && !aEdgeOnly )
  895. effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
  896. if( width > 0 || !IsFilled() || aEdgeOnly )
  897. effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetEnd(), ANGLE_360, width ) );
  898. break;
  899. }
  900. case SHAPE_T::BEZIER:
  901. {
  902. std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width );
  903. VECTOR2I start_pt = bezierPoints[0];
  904. for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
  905. {
  906. VECTOR2I end_pt = bezierPoints[jj];
  907. effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, width ) );
  908. start_pt = end_pt;
  909. }
  910. break;
  911. }
  912. case SHAPE_T::POLY:
  913. {
  914. if( GetPolyShape().OutlineCount() == 0 ) // malformed/empty polygon
  915. break;
  916. for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
  917. {
  918. SHAPE_LINE_CHAIN l = GetPolyShape().COutline( ii );
  919. if( aLineChainOnly )
  920. l.SetClosed( false );
  921. l.Rotate( getParentOrientation() );
  922. l.Move( getParentPosition() );
  923. if( IsFilled() && !aEdgeOnly )
  924. effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
  925. if( width > 0 || !IsFilled() || aEdgeOnly )
  926. {
  927. for( int jj = 0; jj < l.SegmentCount(); jj++ )
  928. effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( jj ), width ) );
  929. }
  930. }
  931. }
  932. break;
  933. default:
  934. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  935. break;
  936. }
  937. return effectiveShapes;
  938. }
  939. void EDA_SHAPE::DupPolyPointsList( std::vector<VECTOR2I>& aBuffer ) const
  940. {
  941. for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
  942. {
  943. int pointCount = m_poly.COutline( ii ).PointCount();
  944. if( pointCount )
  945. {
  946. aBuffer.reserve( pointCount );
  947. for ( auto iter = m_poly.CIterate(); iter; iter++ )
  948. aBuffer.emplace_back( iter->x, iter->y );
  949. }
  950. }
  951. }
  952. bool EDA_SHAPE::IsPolyShapeValid() const
  953. {
  954. // return true if the polygonal shape is valid (has more than 2 points)
  955. if( GetPolyShape().OutlineCount() == 0 )
  956. return false;
  957. const SHAPE_LINE_CHAIN& outline = static_cast<const SHAPE_POLY_SET&>( GetPolyShape() ).Outline( 0 );
  958. return outline.PointCount() > 2;
  959. }
  960. int EDA_SHAPE::GetPointCount() const
  961. {
  962. // return the number of corners of the polygonal shape
  963. // this shape is expected to be only one polygon without hole
  964. if( GetPolyShape().OutlineCount() )
  965. return GetPolyShape().VertexCount( 0 );
  966. return 0;
  967. }
  968. void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
  969. {
  970. switch( GetShape() )
  971. {
  972. case SHAPE_T::SEGMENT:
  973. case SHAPE_T::CIRCLE:
  974. case SHAPE_T::RECT:
  975. SetStart( aPosition );
  976. SetEnd( aPosition );
  977. break;
  978. case SHAPE_T::ARC:
  979. SetArcGeometry( aPosition, aPosition, aPosition );
  980. m_editState = 1;
  981. break;
  982. case SHAPE_T::POLY:
  983. m_poly.NewOutline();
  984. m_poly.Outline( 0 ).SetClosed( false );
  985. // Start and end of the first segment (co-located for now)
  986. m_poly.Outline( 0 ).Append( aPosition );
  987. m_poly.Outline( 0 ).Append( aPosition, true );
  988. break;
  989. default:
  990. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  991. }
  992. }
  993. bool EDA_SHAPE::continueEdit( const VECTOR2I& aPosition )
  994. {
  995. switch( GetShape() )
  996. {
  997. case SHAPE_T::ARC:
  998. case SHAPE_T::SEGMENT:
  999. case SHAPE_T::CIRCLE:
  1000. case SHAPE_T::RECT:
  1001. return false;
  1002. case SHAPE_T::POLY:
  1003. {
  1004. SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
  1005. // do not add zero-length segments
  1006. if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
  1007. poly.Append( aPosition, true );
  1008. }
  1009. return true;
  1010. default:
  1011. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  1012. return false;
  1013. }
  1014. }
  1015. void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
  1016. {
  1017. #define sq( x ) pow( x, 2 )
  1018. switch( GetShape() )
  1019. {
  1020. case SHAPE_T::SEGMENT:
  1021. case SHAPE_T::CIRCLE:
  1022. case SHAPE_T::RECT:
  1023. SetEnd( aPosition );
  1024. break;
  1025. case SHAPE_T::ARC:
  1026. {
  1027. int radius = GetRadius();
  1028. // Edit state 0: drawing: place start
  1029. // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
  1030. // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
  1031. // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
  1032. // Edit state 4: point edit: move center
  1033. // Edit state 5: point edit: move arc-mid-point
  1034. switch( m_editState )
  1035. {
  1036. case 0:
  1037. SetArcGeometry( aPosition, aPosition, aPosition );
  1038. return;
  1039. case 1:
  1040. m_end = aPosition;
  1041. radius = KiROUND( sqrt( sq( GetLineLength( m_start, m_end ) ) / 2.0 ) );
  1042. break;
  1043. case 2:
  1044. case 3:
  1045. {
  1046. VECTOR2I v = m_start - m_end;
  1047. double chordBefore = sq( v.x ) + sq( v.y );
  1048. if( m_editState == 2 )
  1049. m_start = aPosition;
  1050. else
  1051. m_end = aPosition;
  1052. v = m_start - m_end;
  1053. double chordAfter = sq( v.x ) + sq( v.y );
  1054. double ratio = chordAfter / chordBefore;
  1055. if( ratio != 0 )
  1056. {
  1057. radius = std::max( int( sqrt( sq( radius ) * ratio ) ) + 1,
  1058. int( sqrt( chordAfter ) / 2 ) + 1 );
  1059. }
  1060. }
  1061. break;
  1062. case 4:
  1063. {
  1064. double radialA = GetLineLength( m_start, aPosition );
  1065. double radialB = GetLineLength( m_end, aPosition );
  1066. radius = int( ( radialA + radialB ) / 2.0 ) + 1;
  1067. }
  1068. break;
  1069. case 5:
  1070. SetArcGeometry( GetStart(), aPosition, GetEnd() );
  1071. return;
  1072. }
  1073. // Calculate center based on start, end, and radius
  1074. //
  1075. // Let 'l' be the length of the chord and 'm' the middle point of the chord
  1076. double l = GetLineLength( m_start, m_end );
  1077. VECTOR2I m = ( m_start + m_end ) / 2;
  1078. // Calculate 'd', the vector from the chord midpoint to the center
  1079. VECTOR2I d;
  1080. d.x = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_start.y - m_end.y ) / l );
  1081. d.y = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_end.x - m_start.x ) / l );
  1082. VECTOR2I c1 = m + d;
  1083. VECTOR2I c2 = m - d;
  1084. // Solution gives us 2 centers; we need to pick one:
  1085. switch( m_editState )
  1086. {
  1087. case 1:
  1088. // Keep arc clockwise while drawing i.e. arc angle = 90 deg.
  1089. // it can be 90 or 270 deg depending on the arc center choice (c1 or c2)
  1090. m_arcCenter = c1; // first trial
  1091. if( GetArcAngle() > ANGLE_180 )
  1092. m_arcCenter = c2;
  1093. break;
  1094. case 2:
  1095. case 3:
  1096. // Pick the one of c1, c2 to keep arc <= 180 deg
  1097. m_arcCenter = c1; // first trial
  1098. if( GetArcAngle() > ANGLE_180 )
  1099. m_arcCenter = c2;
  1100. break;
  1101. case 4:
  1102. // Pick the one closer to the mouse position
  1103. m_arcCenter = GetLineLength( c1, aPosition ) < GetLineLength( c2, aPosition ) ? c1 : c2;
  1104. // keep arc angle <= 180 deg
  1105. if( GetArcAngle() > ANGLE_180 )
  1106. std::swap( m_start, m_end );
  1107. break;
  1108. }
  1109. }
  1110. break;
  1111. case SHAPE_T::POLY:
  1112. m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
  1113. break;
  1114. default:
  1115. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  1116. }
  1117. }
  1118. void EDA_SHAPE::endEdit( bool aClosed )
  1119. {
  1120. switch( GetShape() )
  1121. {
  1122. case SHAPE_T::ARC:
  1123. case SHAPE_T::SEGMENT:
  1124. case SHAPE_T::CIRCLE:
  1125. case SHAPE_T::RECT:
  1126. break;
  1127. case SHAPE_T::POLY:
  1128. {
  1129. SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
  1130. // do not include last point twice
  1131. if( poly.GetPointCount() > 2 )
  1132. {
  1133. if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
  1134. {
  1135. poly.SetClosed( aClosed );
  1136. poly.Remove( poly.GetPointCount() - 1 );
  1137. }
  1138. }
  1139. }
  1140. break;
  1141. default:
  1142. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  1143. }
  1144. }
  1145. void EDA_SHAPE::SwapShape( EDA_SHAPE* aImage )
  1146. {
  1147. EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
  1148. assert( image );
  1149. #define SWAPITEM( x ) std::swap( x, image->x )
  1150. SWAPITEM( m_stroke );
  1151. SWAPITEM( m_start );
  1152. SWAPITEM( m_end );
  1153. SWAPITEM( m_arcCenter );
  1154. SWAPITEM( m_shape );
  1155. SWAPITEM( m_bezierC1 );
  1156. SWAPITEM( m_bezierC2 );
  1157. SWAPITEM( m_bezierPoints );
  1158. SWAPITEM( m_poly );
  1159. SWAPITEM( m_fill );
  1160. SWAPITEM( m_fillColor );
  1161. SWAPITEM( m_editState );
  1162. SWAPITEM( m_endsSwapped );
  1163. #undef SWAPITEM
  1164. }
  1165. int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
  1166. {
  1167. #define EPSILON 2 // Should be enough for rounding errors on calculated items
  1168. #define TEST( a, b ) { if( a != b ) return a - b; }
  1169. #define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
  1170. #define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
  1171. TEST_PT( m_start, aOther->m_start );
  1172. TEST_PT( m_end, aOther->m_end );
  1173. TEST( (int) m_shape, (int) aOther->m_shape );
  1174. if( m_shape == SHAPE_T::ARC )
  1175. {
  1176. TEST_PT( m_arcCenter, aOther->m_arcCenter );
  1177. }
  1178. else if( m_shape == SHAPE_T::BEZIER )
  1179. {
  1180. TEST_PT( m_bezierC1, aOther->m_bezierC1 );
  1181. TEST_PT( m_bezierC2, aOther->m_bezierC2 );
  1182. }
  1183. else if( m_shape == SHAPE_T::POLY )
  1184. {
  1185. TEST( m_poly.TotalVertices(), aOther->m_poly.TotalVertices() );
  1186. }
  1187. for( size_t ii = 0; ii < m_bezierPoints.size(); ++ii )
  1188. TEST_PT( m_bezierPoints[ii], aOther->m_bezierPoints[ii] );
  1189. for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
  1190. TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
  1191. TEST_E( m_stroke.GetWidth(), aOther->m_stroke.GetWidth() );
  1192. TEST( (int) m_stroke.GetPlotStyle(), (int) aOther->m_stroke.GetPlotStyle() );
  1193. TEST( (int) m_fill, (int) aOther->m_fill );
  1194. return 0;
  1195. }
  1196. void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
  1197. ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const
  1198. {
  1199. int width = ignoreLineWidth ? 0 : GetWidth();
  1200. width += 2 * aClearance;
  1201. switch( m_shape )
  1202. {
  1203. case SHAPE_T::CIRCLE:
  1204. {
  1205. int r = GetRadius();
  1206. if( IsFilled() )
  1207. TransformCircleToPolygon( aBuffer, getCenter(), r + width / 2, aError, aErrorLoc );
  1208. else
  1209. TransformRingToPolygon( aBuffer, getCenter(), r, width, aError, aErrorLoc );
  1210. break;
  1211. }
  1212. case SHAPE_T::RECT:
  1213. {
  1214. std::vector<VECTOR2I> pts = GetRectCorners();
  1215. if( IsFilled() || IsAnnotationProxy() )
  1216. {
  1217. aBuffer.NewOutline();
  1218. for( const VECTOR2I& pt : pts )
  1219. aBuffer.Append( pt );
  1220. }
  1221. if( width > 0 || !IsFilled() )
  1222. {
  1223. // Add in segments
  1224. TransformOvalToPolygon( aBuffer, pts[0], pts[1], width, aError, aErrorLoc );
  1225. TransformOvalToPolygon( aBuffer, pts[1], pts[2], width, aError, aErrorLoc );
  1226. TransformOvalToPolygon( aBuffer, pts[2], pts[3], width, aError, aErrorLoc );
  1227. TransformOvalToPolygon( aBuffer, pts[3], pts[0], width, aError, aErrorLoc );
  1228. }
  1229. break;
  1230. }
  1231. case SHAPE_T::ARC:
  1232. TransformArcToPolygon( aBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError, aErrorLoc );
  1233. break;
  1234. case SHAPE_T::SEGMENT:
  1235. TransformOvalToPolygon( aBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
  1236. break;
  1237. case SHAPE_T::POLY:
  1238. {
  1239. if( !IsPolyShapeValid() )
  1240. break;
  1241. // The polygon is expected to be a simple polygon; not self intersecting, no hole.
  1242. EDA_ANGLE orientation = getParentOrientation();
  1243. VECTOR2I offset = getParentPosition();
  1244. // Build the polygon with the actual position and orientation:
  1245. std::vector<VECTOR2I> poly;
  1246. DupPolyPointsList( poly );
  1247. for( VECTOR2I& point : poly )
  1248. {
  1249. RotatePoint( point, orientation );
  1250. point += offset;
  1251. }
  1252. if( IsFilled() )
  1253. {
  1254. aBuffer.NewOutline();
  1255. for( const VECTOR2I& point : poly )
  1256. aBuffer.Append( point.x, point.y );
  1257. }
  1258. if( width > 0 || !IsFilled() )
  1259. {
  1260. VECTOR2I pt1( poly[poly.size() - 1] );
  1261. for( const VECTOR2I& pt2 : poly )
  1262. {
  1263. if( pt2 != pt1 )
  1264. TransformOvalToPolygon( aBuffer, pt1, pt2, width, aError, aErrorLoc );
  1265. pt1 = pt2;
  1266. }
  1267. }
  1268. break;
  1269. }
  1270. case SHAPE_T::BEZIER:
  1271. {
  1272. std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
  1273. BEZIER_POLY converter( ctrlPts );
  1274. std::vector<VECTOR2I> poly;
  1275. converter.GetPoly( poly, GetWidth() );
  1276. for( unsigned ii = 1; ii < poly.size(); ii++ )
  1277. TransformOvalToPolygon( aBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );
  1278. break;
  1279. }
  1280. default:
  1281. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  1282. break;
  1283. }
  1284. }
  1285. ENUM_TO_WXANY( SHAPE_T )
  1286. ENUM_TO_WXANY( PLOT_DASH_TYPE )
  1287. static struct EDA_SHAPE_DESC
  1288. {
  1289. EDA_SHAPE_DESC()
  1290. {
  1291. ENUM_MAP<SHAPE_T>::Instance()
  1292. .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
  1293. .Map( SHAPE_T::RECT, _HKI( "Rectangle" ) )
  1294. .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
  1295. .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
  1296. .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
  1297. .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
  1298. ENUM_MAP<PLOT_DASH_TYPE>::Instance()
  1299. .Map( PLOT_DASH_TYPE::DEFAULT, _HKI( "Default" ) )
  1300. .Map( PLOT_DASH_TYPE::SOLID, _HKI( "Solid" ) )
  1301. .Map( PLOT_DASH_TYPE::DASH, _HKI( "Dashed" ) )
  1302. .Map( PLOT_DASH_TYPE::DOT, _HKI( "Dotted" ) )
  1303. .Map( PLOT_DASH_TYPE::DASHDOT, _HKI( "Dash-Dot" ) )
  1304. .Map( PLOT_DASH_TYPE::DASHDOTDOT, _HKI( "Dash-Dot-Dot" ) );
  1305. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1306. REGISTER_TYPE( EDA_SHAPE );
  1307. auto shape = new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
  1308. NO_SETTER( EDA_SHAPE, SHAPE_T ), &EDA_SHAPE::GetShape );
  1309. propMgr.AddProperty( shape );
  1310. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
  1311. &EDA_SHAPE::SetStartX, &EDA_SHAPE::GetStartX, PROPERTY_DISPLAY::PT_COORD,
  1312. ORIGIN_TRANSFORMS::ABS_X_COORD ) );
  1313. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
  1314. &EDA_SHAPE::SetStartY, &EDA_SHAPE::GetStartY, PROPERTY_DISPLAY::PT_COORD,
  1315. ORIGIN_TRANSFORMS::ABS_Y_COORD ) );
  1316. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
  1317. &EDA_SHAPE::SetEndX, &EDA_SHAPE::GetEndX, PROPERTY_DISPLAY::PT_COORD,
  1318. ORIGIN_TRANSFORMS::ABS_X_COORD ) );
  1319. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
  1320. &EDA_SHAPE::SetEndY, &EDA_SHAPE::GetEndY, PROPERTY_DISPLAY::PT_COORD,
  1321. ORIGIN_TRANSFORMS::ABS_Y_COORD ) );
  1322. // TODO: m_arcCenter, m_bezierC1, m_bezierC2, m_poly
  1323. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
  1324. &EDA_SHAPE::SetWidth, &EDA_SHAPE::GetWidth, PROPERTY_DISPLAY::PT_SIZE ) );
  1325. auto angle = new PROPERTY<EDA_SHAPE, EDA_ANGLE>( _HKI( "Angle" ),
  1326. NO_SETTER( EDA_SHAPE, EDA_ANGLE ), &EDA_SHAPE::GetArcAngle,
  1327. PROPERTY_DISPLAY::PT_DECIDEGREE );
  1328. angle->SetAvailableFunc(
  1329. [=]( INSPECTABLE* aItem ) -> bool
  1330. {
  1331. return aItem->Get<SHAPE_T>( shape ) == SHAPE_T::ARC;
  1332. } );
  1333. propMgr.AddProperty( angle );
  1334. }
  1335. } _EDA_SHAPE_DESC;