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.

2356 lines
66 KiB

4 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
4 years ago
3 years ago
4 years ago
2 years ago
2 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) 2023 CERN
  8. * Copyright (C) 1992-2024 KiCad Developers, see AUTHORS.txt for contributors.
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, you may find one here:
  22. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  23. * or you may search the http://www.gnu.org website for the version 2 license,
  24. * or you may write to the Free Software Foundation, Inc.,
  25. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  26. */
  27. #include <eda_shape.h>
  28. #include <bezier_curves.h>
  29. #include <convert_basic_shapes_to_polygon.h>
  30. #include <eda_draw_frame.h>
  31. #include <geometry/shape_arc.h>
  32. #include <geometry/shape_circle.h>
  33. #include <geometry/shape_simple.h>
  34. #include <geometry/shape_segment.h>
  35. #include <geometry/shape_rect.h>
  36. #include <macros.h>
  37. #include <math/util.h> // for KiROUND
  38. #include <eda_item.h>
  39. #include <plotters/plotter.h>
  40. #include <api/api_enums.h>
  41. #include <api/api_utils.h>
  42. #include <api/common/types/base_types.pb.h>
  43. EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) :
  44. m_endsSwapped( false ),
  45. m_shape( aType ),
  46. m_stroke( aLineWidth, LINE_STYLE::DEFAULT, COLOR4D::UNSPECIFIED ),
  47. m_fill( aFill ),
  48. m_fillColor( COLOR4D::UNSPECIFIED ),
  49. m_rectangleHeight( 0 ),
  50. m_rectangleWidth( 0 ),
  51. m_segmentLength( 0 ),
  52. m_editState( 0 ),
  53. m_proxyItem( false )
  54. {
  55. }
  56. EDA_SHAPE::~EDA_SHAPE()
  57. {
  58. }
  59. EDA_SHAPE::EDA_SHAPE( const SHAPE& aShape ) :
  60. m_endsSwapped( false ),
  61. m_stroke( 0, LINE_STYLE::DEFAULT, COLOR4D::UNSPECIFIED ),
  62. m_fill(),
  63. m_rectangleHeight( 0 ),
  64. m_rectangleWidth( 0 ),
  65. m_segmentLength( 0 ),
  66. m_editState( 0 ),
  67. m_proxyItem( false )
  68. {
  69. switch( aShape.Type() )
  70. {
  71. case SH_RECT:
  72. {
  73. auto rect = static_cast<const SHAPE_RECT&>( aShape );
  74. m_shape = SHAPE_T::RECTANGLE;
  75. SetStart( rect.GetPosition() );
  76. SetEnd( rect.GetPosition() + rect.GetSize() );
  77. break;
  78. }
  79. case SH_SEGMENT:
  80. {
  81. auto seg = static_cast<const SHAPE_SEGMENT&>( aShape );
  82. m_shape = SHAPE_T::SEGMENT;
  83. SetStart( seg.GetSeg().A );
  84. SetEnd( seg.GetSeg().B );
  85. SetWidth( seg.GetWidth() );
  86. break;
  87. }
  88. case SH_LINE_CHAIN:
  89. {
  90. auto line = static_cast<const SHAPE_LINE_CHAIN&>( aShape );
  91. m_shape = SHAPE_T::POLY;
  92. m_poly = SHAPE_POLY_SET();
  93. m_poly.AddOutline( line );
  94. SetWidth( line.Width() );
  95. break;
  96. }
  97. case SH_CIRCLE:
  98. {
  99. auto circle = static_cast<const SHAPE_CIRCLE&>( aShape );
  100. m_shape = SHAPE_T::CIRCLE;
  101. SetStart( circle.GetCenter() );
  102. SetEnd( circle.GetCenter() + circle.GetRadius() );
  103. break;
  104. }
  105. case SH_ARC:
  106. {
  107. auto arc = static_cast<const SHAPE_ARC&>( aShape );
  108. m_shape = SHAPE_T::ARC;
  109. SetArcGeometry( arc.GetP0(), arc.GetArcMid(), arc.GetP1() );
  110. SetWidth( arc.GetWidth() );
  111. break;
  112. }
  113. case SH_SIMPLE:
  114. {
  115. auto poly = static_cast<const SHAPE_SIMPLE&>( aShape );
  116. m_shape = SHAPE_T::POLY;
  117. poly.TransformToPolygon( m_poly, 0, ERROR_INSIDE );
  118. break;
  119. }
  120. // currently unhandled
  121. case SH_POLY_SET:
  122. case SH_COMPOUND:
  123. case SH_NULL:
  124. case SH_POLY_SET_TRIANGLE:
  125. default:
  126. m_shape = SHAPE_T::UNDEFINED;
  127. break;
  128. }
  129. }
  130. void EDA_SHAPE::Serialize( google::protobuf::Any &aContainer ) const
  131. {
  132. using namespace kiapi::common;
  133. types::GraphicShape shape;
  134. types::StrokeAttributes* stroke = shape.mutable_attributes()->mutable_stroke();
  135. types::GraphicFillAttributes* fill = shape.mutable_attributes()->mutable_fill();
  136. stroke->mutable_width()->set_value_nm( GetWidth() );
  137. switch( GetLineStyle() )
  138. {
  139. case LINE_STYLE::DEFAULT: stroke->set_style( types::SLS_DEFAULT ); break;
  140. case LINE_STYLE::SOLID: stroke->set_style( types::SLS_SOLID ); break;
  141. case LINE_STYLE::DASH: stroke->set_style( types::SLS_DASH ); break;
  142. case LINE_STYLE::DOT: stroke->set_style( types::SLS_DOT ); break;
  143. case LINE_STYLE::DASHDOT: stroke->set_style( types::SLS_DASHDOT ); break;
  144. case LINE_STYLE::DASHDOTDOT: stroke->set_style( types::SLS_DASHDOTDOT ); break;
  145. default: break;
  146. }
  147. switch( GetFillMode() )
  148. {
  149. case FILL_T::FILLED_SHAPE: fill->set_fill_type( types::GFT_FILLED ); break;
  150. default: fill->set_fill_type( types::GFT_UNFILLED ); break;
  151. }
  152. switch( GetShape() )
  153. {
  154. case SHAPE_T::SEGMENT:
  155. {
  156. types::GraphicSegmentAttributes* segment = shape.mutable_segment();
  157. PackVector2( *segment->mutable_start(), GetStart() );
  158. PackVector2( *segment->mutable_end(), GetEnd() );
  159. break;
  160. }
  161. case SHAPE_T::RECTANGLE:
  162. {
  163. types::GraphicRectangleAttributes* rectangle = shape.mutable_rectangle();
  164. PackVector2( *rectangle->mutable_top_left(), GetStart() );
  165. PackVector2( *rectangle->mutable_bottom_right(), GetEnd() );
  166. break;
  167. }
  168. case SHAPE_T::ARC:
  169. {
  170. types::GraphicArcAttributes* arc = shape.mutable_arc();
  171. PackVector2( *arc->mutable_start(), GetStart() );
  172. PackVector2( *arc->mutable_mid(), GetArcMid() );
  173. PackVector2( *arc->mutable_end(), GetEnd() );
  174. break;
  175. }
  176. case SHAPE_T::CIRCLE:
  177. {
  178. types::GraphicCircleAttributes* circle = shape.mutable_circle();
  179. PackVector2( *circle->mutable_center(), GetStart() );
  180. PackVector2( *circle->mutable_radius_point(), GetEnd() );
  181. break;
  182. }
  183. case SHAPE_T::POLY:
  184. {
  185. PackPolySet( *shape.mutable_polygon(), GetPolyShape() );
  186. break;
  187. }
  188. case SHAPE_T::BEZIER:
  189. {
  190. types::GraphicBezierAttributes* bezier = shape.mutable_bezier();
  191. PackVector2( *bezier->mutable_start(), GetStart() );
  192. PackVector2( *bezier->mutable_control1(), GetBezierC1() );
  193. PackVector2( *bezier->mutable_control2(), GetBezierC2() );
  194. PackVector2( *bezier->mutable_end(), GetEnd() );
  195. break;
  196. }
  197. default:
  198. wxASSERT_MSG( false, "Unhandled shape in PCB_SHAPE::Serialize" );
  199. }
  200. // TODO m_hasSolderMask and m_solderMaskMargin
  201. aContainer.PackFrom( shape );
  202. }
  203. bool EDA_SHAPE::Deserialize( const google::protobuf::Any &aContainer )
  204. {
  205. using namespace kiapi::common;
  206. types::GraphicShape shape;
  207. if( !aContainer.UnpackTo( &shape ) )
  208. return false;
  209. // Initialize everything to a known state that doesn't get touched by every
  210. // codepath below, to make sure the equality operator is consistent
  211. m_start = {};
  212. m_end = {};
  213. m_arcCenter = {};
  214. m_arcMidData = {};
  215. m_bezierC1 = {};
  216. m_bezierC2 = {};
  217. m_editState = 0;
  218. m_proxyItem = false;
  219. m_endsSwapped = false;
  220. SetFilled( shape.attributes().fill().fill_type() == types::GFT_FILLED );
  221. SetWidth( shape.attributes().stroke().width().value_nm() );
  222. switch( shape.attributes().stroke().style() )
  223. {
  224. case types::SLS_DEFAULT: SetLineStyle( LINE_STYLE::DEFAULT ); break;
  225. case types::SLS_SOLID: SetLineStyle( LINE_STYLE::SOLID ); break;
  226. case types::SLS_DASH: SetLineStyle( LINE_STYLE::DASH ); break;
  227. case types::SLS_DOT: SetLineStyle( LINE_STYLE::DOT ); break;
  228. case types::SLS_DASHDOT: SetLineStyle( LINE_STYLE::DASHDOT ); break;
  229. case types::SLS_DASHDOTDOT: SetLineStyle( LINE_STYLE::DASHDOTDOT ); break;
  230. default: break;
  231. }
  232. if( shape.has_segment() )
  233. {
  234. SetShape( SHAPE_T::SEGMENT );
  235. SetStart( UnpackVector2( shape.segment().start() ) );
  236. SetEnd( UnpackVector2( shape.segment().end() ) );
  237. }
  238. else if( shape.has_rectangle() )
  239. {
  240. SetShape( SHAPE_T::RECTANGLE );
  241. SetStart( UnpackVector2( shape.rectangle().top_left() ) );
  242. SetEnd( UnpackVector2( shape.rectangle().bottom_right() ) );
  243. }
  244. else if( shape.has_arc() )
  245. {
  246. SetShape( SHAPE_T::ARC );
  247. SetArcGeometry( UnpackVector2( shape.arc().start() ),
  248. UnpackVector2( shape.arc().mid() ),
  249. UnpackVector2( shape.arc().end() ) );
  250. }
  251. else if( shape.has_circle() )
  252. {
  253. SetShape( SHAPE_T::CIRCLE );
  254. SetStart( UnpackVector2( shape.circle().center() ) );
  255. SetEnd( UnpackVector2( shape.circle().radius_point() ) );
  256. }
  257. else if( shape.has_polygon() )
  258. {
  259. SetShape( SHAPE_T::POLY );
  260. SetPolyShape( UnpackPolySet( shape.polygon() ) );
  261. }
  262. else if( shape.has_bezier() )
  263. {
  264. SetShape( SHAPE_T::BEZIER );
  265. SetStart( UnpackVector2( shape.bezier().start() ) );
  266. SetBezierC1( UnpackVector2( shape.bezier().control1() ) );
  267. SetBezierC2( UnpackVector2( shape.bezier().control2() ) );
  268. SetEnd( UnpackVector2( shape.bezier().end() ) );
  269. RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
  270. }
  271. return true;
  272. }
  273. wxString EDA_SHAPE::ShowShape() const
  274. {
  275. if( IsProxyItem() )
  276. {
  277. switch( m_shape )
  278. {
  279. case SHAPE_T::SEGMENT: return _( "Thermal Spoke" );
  280. case SHAPE_T::RECTANGLE: return _( "Number Box" );
  281. default: return wxT( "??" );
  282. }
  283. }
  284. else
  285. {
  286. switch( m_shape )
  287. {
  288. case SHAPE_T::SEGMENT: return _( "Line" );
  289. case SHAPE_T::RECTANGLE: return _( "Rect" );
  290. case SHAPE_T::ARC: return _( "Arc" );
  291. case SHAPE_T::CIRCLE: return _( "Circle" );
  292. case SHAPE_T::BEZIER: return _( "Bezier Curve" );
  293. case SHAPE_T::POLY: return _( "Polygon" );
  294. default: return wxT( "??" );
  295. }
  296. }
  297. }
  298. wxString EDA_SHAPE::SHAPE_T_asString() const
  299. {
  300. switch( m_shape )
  301. {
  302. case SHAPE_T::SEGMENT: return wxS( "S_SEGMENT" );
  303. case SHAPE_T::RECTANGLE: return wxS( "S_RECT" );
  304. case SHAPE_T::ARC: return wxS( "S_ARC" );
  305. case SHAPE_T::CIRCLE: return wxS( "S_CIRCLE" );
  306. case SHAPE_T::POLY: return wxS( "S_POLYGON" );
  307. case SHAPE_T::BEZIER: return wxS( "S_CURVE" );
  308. case SHAPE_T::UNDEFINED: return wxS( "UNDEFINED" );
  309. }
  310. return wxEmptyString; // Just to quiet GCC.
  311. }
  312. void EDA_SHAPE::setPosition( const VECTOR2I& aPos )
  313. {
  314. move( aPos - getPosition() );
  315. }
  316. VECTOR2I EDA_SHAPE::getPosition() const
  317. {
  318. if( m_shape == SHAPE_T::ARC )
  319. return getCenter();
  320. else if( m_shape == SHAPE_T::POLY )
  321. return m_poly.CVertex( 0 );
  322. else
  323. return m_start;
  324. }
  325. double EDA_SHAPE::GetLength() const
  326. {
  327. double length = 0.0;
  328. switch( m_shape )
  329. {
  330. case SHAPE_T::BEZIER:
  331. for( size_t ii = 1; ii < m_bezierPoints.size(); ++ii )
  332. length += m_bezierPoints[ ii - 1].Distance( m_bezierPoints[ii] );
  333. return length;
  334. case SHAPE_T::SEGMENT:
  335. return GetStart().Distance( GetEnd() );
  336. case SHAPE_T::POLY:
  337. for( int ii = 0; ii < m_poly.COutline( 0 ).SegmentCount(); ii++ )
  338. length += m_poly.COutline( 0 ).CSegment( ii ).Length();
  339. return length;
  340. case SHAPE_T::ARC:
  341. return GetRadius() * GetArcAngle().AsRadians();
  342. default:
  343. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  344. return 0.0;
  345. }
  346. }
  347. int EDA_SHAPE::GetRectangleHeight() const
  348. {
  349. switch( m_shape )
  350. {
  351. case SHAPE_T::RECTANGLE:
  352. return GetEndY() - GetStartY();
  353. default:
  354. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  355. return 0;
  356. }
  357. }
  358. int EDA_SHAPE::GetRectangleWidth() const
  359. {
  360. switch( m_shape )
  361. {
  362. case SHAPE_T::RECTANGLE:
  363. return GetEndX() - GetStartX();
  364. default:
  365. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  366. return 0;
  367. }
  368. }
  369. void EDA_SHAPE::SetLength( const double& aLength )
  370. {
  371. switch( m_shape )
  372. {
  373. case SHAPE_T::SEGMENT:
  374. m_segmentLength = aLength; break;
  375. default:
  376. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  377. }
  378. }
  379. void EDA_SHAPE::SetRectangleHeight( const int& aHeight )
  380. {
  381. switch ( m_shape )
  382. {
  383. case SHAPE_T::RECTANGLE:
  384. m_rectangleHeight = aHeight;
  385. SetEndY( GetStartY() + m_rectangleHeight );
  386. break;
  387. default:
  388. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  389. }
  390. }
  391. void EDA_SHAPE::SetRectangleWidth( const int& aWidth )
  392. {
  393. switch ( m_shape )
  394. {
  395. case SHAPE_T::RECTANGLE:
  396. m_rectangleWidth = aWidth;
  397. SetEndX( GetStartX() + m_rectangleWidth );
  398. break;
  399. default:
  400. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  401. }
  402. }
  403. void EDA_SHAPE::SetRectangle( const long long int& aHeight, const long long int& aWidth )
  404. {
  405. switch ( m_shape )
  406. {
  407. case SHAPE_T::RECTANGLE:
  408. m_rectangleHeight = aHeight;
  409. m_rectangleWidth = aWidth;
  410. break;
  411. default:
  412. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  413. }
  414. }
  415. void EDA_SHAPE::SetSegmentAngle( const EDA_ANGLE& aAngle )
  416. {
  417. switch( m_shape )
  418. {
  419. case SHAPE_T::SEGMENT:
  420. m_segmentAngle = aAngle;
  421. break;
  422. default:
  423. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  424. }
  425. }
  426. bool EDA_SHAPE::IsClosed() const
  427. {
  428. switch( m_shape )
  429. {
  430. case SHAPE_T::CIRCLE:
  431. case SHAPE_T::RECTANGLE:
  432. return true;
  433. case SHAPE_T::ARC:
  434. case SHAPE_T::SEGMENT:
  435. return false;
  436. case SHAPE_T::POLY:
  437. if( m_poly.IsEmpty() )
  438. return false;
  439. else
  440. return m_poly.Outline( 0 ).IsClosed();
  441. case SHAPE_T::BEZIER:
  442. if( m_bezierPoints.size() < 3 )
  443. return false;
  444. else
  445. return m_bezierPoints[0] == m_bezierPoints[ m_bezierPoints.size() - 1 ];
  446. default:
  447. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  448. return false;
  449. }
  450. }
  451. void EDA_SHAPE::move( const VECTOR2I& aMoveVector )
  452. {
  453. switch ( m_shape )
  454. {
  455. case SHAPE_T::ARC:
  456. m_arcCenter += aMoveVector;
  457. KI_FALLTHROUGH;
  458. case SHAPE_T::SEGMENT:
  459. case SHAPE_T::RECTANGLE:
  460. case SHAPE_T::CIRCLE:
  461. m_start += aMoveVector;
  462. m_end += aMoveVector;
  463. break;
  464. case SHAPE_T::POLY:
  465. m_poly.Move( aMoveVector );
  466. break;
  467. case SHAPE_T::BEZIER:
  468. m_start += aMoveVector;
  469. m_end += aMoveVector;
  470. m_bezierC1 += aMoveVector;
  471. m_bezierC2 += aMoveVector;
  472. for( VECTOR2I& pt : m_bezierPoints )
  473. pt += aMoveVector;
  474. break;
  475. default:
  476. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  477. break;
  478. }
  479. }
  480. void EDA_SHAPE::scale( double aScale )
  481. {
  482. auto scalePt = [&]( VECTOR2I& pt )
  483. {
  484. pt.x = KiROUND( pt.x * aScale );
  485. pt.y = KiROUND( pt.y * aScale );
  486. };
  487. switch( m_shape )
  488. {
  489. case SHAPE_T::ARC:
  490. scalePt( m_arcCenter );
  491. KI_FALLTHROUGH;
  492. case SHAPE_T::SEGMENT:
  493. case SHAPE_T::RECTANGLE:
  494. scalePt( m_start );
  495. scalePt( m_end );
  496. break;
  497. case SHAPE_T::CIRCLE: // ring or circle
  498. scalePt( m_start );
  499. m_end.x = m_start.x + KiROUND( GetRadius() * aScale );
  500. m_end.y = m_start.y;
  501. break;
  502. case SHAPE_T::POLY: // polygon
  503. {
  504. std::vector<VECTOR2I> pts;
  505. for( int ii = 0; ii < m_poly.OutlineCount(); ++ ii )
  506. {
  507. for( const VECTOR2I& pt : m_poly.Outline( ii ).CPoints() )
  508. {
  509. pts.emplace_back( pt );
  510. scalePt( pts.back() );
  511. }
  512. }
  513. SetPolyPoints( pts );
  514. }
  515. break;
  516. case SHAPE_T::BEZIER:
  517. scalePt( m_start );
  518. scalePt( m_end );
  519. scalePt( m_bezierC1 );
  520. scalePt( m_bezierC2 );
  521. RebuildBezierToSegmentsPointsList( m_stroke.GetWidth() / 2 );
  522. break;
  523. default:
  524. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  525. break;
  526. }
  527. }
  528. void EDA_SHAPE::rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  529. {
  530. switch( m_shape )
  531. {
  532. case SHAPE_T::SEGMENT:
  533. case SHAPE_T::CIRCLE:
  534. RotatePoint( m_start, aRotCentre, aAngle );
  535. RotatePoint( m_end, aRotCentre, aAngle );
  536. break;
  537. case SHAPE_T::ARC:
  538. RotatePoint( m_start, aRotCentre, aAngle );
  539. RotatePoint( m_end, aRotCentre, aAngle );
  540. RotatePoint( m_arcCenter, aRotCentre, aAngle );
  541. break;
  542. case SHAPE_T::RECTANGLE:
  543. if( aAngle.IsCardinal() )
  544. {
  545. RotatePoint( m_start, aRotCentre, aAngle );
  546. RotatePoint( m_end, aRotCentre, aAngle );
  547. break;
  548. }
  549. // Convert non-cardinally-rotated rect to a diamond
  550. m_shape = SHAPE_T::POLY;
  551. m_poly.RemoveAllContours();
  552. m_poly.NewOutline();
  553. m_poly.Append( m_start );
  554. m_poly.Append( m_end.x, m_start.y );
  555. m_poly.Append( m_end );
  556. m_poly.Append( m_start.x, m_end.y );
  557. KI_FALLTHROUGH;
  558. case SHAPE_T::POLY:
  559. m_poly.Rotate( aAngle, aRotCentre );
  560. break;
  561. case SHAPE_T::BEZIER:
  562. RotatePoint( m_start, aRotCentre, aAngle );
  563. RotatePoint( m_end, aRotCentre, aAngle );
  564. RotatePoint( m_bezierC1, aRotCentre, aAngle );
  565. RotatePoint( m_bezierC2, aRotCentre, aAngle );
  566. for( VECTOR2I& pt : m_bezierPoints )
  567. RotatePoint( pt, aRotCentre, aAngle);
  568. break;
  569. default:
  570. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  571. break;
  572. }
  573. }
  574. void EDA_SHAPE::flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  575. {
  576. switch ( m_shape )
  577. {
  578. case SHAPE_T::SEGMENT:
  579. case SHAPE_T::RECTANGLE:
  580. MIRROR( m_start, aCentre, aFlipDirection );
  581. MIRROR( m_end, aCentre, aFlipDirection );
  582. break;
  583. case SHAPE_T::CIRCLE:
  584. MIRROR( m_start, aCentre, aFlipDirection );
  585. MIRROR( m_end, aCentre, aFlipDirection );
  586. break;
  587. case SHAPE_T::ARC:
  588. MIRROR( m_start, aCentre, aFlipDirection );
  589. MIRROR( m_end, aCentre, aFlipDirection );
  590. MIRROR( m_arcCenter, aCentre, aFlipDirection );
  591. std::swap( m_start, m_end );
  592. break;
  593. case SHAPE_T::POLY:
  594. m_poly.Mirror( aCentre, aFlipDirection );
  595. break;
  596. case SHAPE_T::BEZIER:
  597. MIRROR( m_start, aCentre, aFlipDirection );
  598. MIRROR( m_end, aCentre, aFlipDirection );
  599. MIRROR( m_bezierC1, aCentre, aFlipDirection );
  600. MIRROR( m_bezierC2, aCentre, aFlipDirection );
  601. RebuildBezierToSegmentsPointsList( m_stroke.GetWidth() / 2 );
  602. break;
  603. default:
  604. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  605. break;
  606. }
  607. }
  608. void EDA_SHAPE::RebuildBezierToSegmentsPointsList( int aMaxError )
  609. {
  610. // Has meaning only for SHAPE_T::BEZIER
  611. if( m_shape != SHAPE_T::BEZIER )
  612. {
  613. m_bezierPoints.clear();
  614. return;
  615. }
  616. // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
  617. m_bezierPoints = buildBezierToSegmentsPointsList( aMaxError );
  618. }
  619. const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMaxError ) const
  620. {
  621. std::vector<VECTOR2I> bezierPoints;
  622. // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
  623. std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
  624. BEZIER_POLY converter( ctrlPoints );
  625. converter.GetPoly( bezierPoints, aMaxError );
  626. return bezierPoints;
  627. }
  628. VECTOR2I EDA_SHAPE::getCenter() const
  629. {
  630. switch( m_shape )
  631. {
  632. case SHAPE_T::ARC:
  633. return m_arcCenter;
  634. case SHAPE_T::CIRCLE:
  635. return m_start;
  636. case SHAPE_T::SEGMENT:
  637. // Midpoint of the line
  638. return ( m_start + m_end ) / 2;
  639. case SHAPE_T::POLY:
  640. case SHAPE_T::RECTANGLE:
  641. case SHAPE_T::BEZIER:
  642. return getBoundingBox().Centre();
  643. default:
  644. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  645. return VECTOR2I();
  646. }
  647. }
  648. void EDA_SHAPE::SetCenter( const VECTOR2I& aCenter )
  649. {
  650. switch( m_shape )
  651. {
  652. case SHAPE_T::ARC:
  653. m_arcCenter = aCenter;
  654. break;
  655. case SHAPE_T::CIRCLE:
  656. m_start = aCenter;
  657. break;
  658. default:
  659. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  660. }
  661. }
  662. VECTOR2I EDA_SHAPE::GetArcMid() const
  663. {
  664. // If none of the input data have changed since we loaded the arc,
  665. // keep the original mid point data to minimize churn
  666. if( m_arcMidData.start == m_start && m_arcMidData.end == m_end
  667. && m_arcMidData.center == m_arcCenter )
  668. return m_arcMidData.mid;
  669. VECTOR2I mid = m_start;
  670. RotatePoint( mid, m_arcCenter, -GetArcAngle() / 2.0 );
  671. return mid;
  672. }
  673. void EDA_SHAPE::CalcArcAngles( EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle ) const
  674. {
  675. VECTOR2D startRadial( GetStart() - getCenter() );
  676. VECTOR2D endRadial( GetEnd() - getCenter() );
  677. aStartAngle = EDA_ANGLE( startRadial );
  678. aEndAngle = EDA_ANGLE( endRadial );
  679. if( aEndAngle == aStartAngle )
  680. aEndAngle = aStartAngle + ANGLE_360; // ring, not null
  681. while( aEndAngle < aStartAngle )
  682. aEndAngle += ANGLE_360;
  683. }
  684. int EDA_SHAPE::GetRadius() const
  685. {
  686. double radius = 0.0;
  687. switch( m_shape )
  688. {
  689. case SHAPE_T::ARC:
  690. radius = m_arcCenter.Distance( m_start );
  691. break;
  692. case SHAPE_T::CIRCLE:
  693. radius = m_start.Distance( m_end );
  694. break;
  695. default:
  696. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  697. }
  698. // don't allow degenerate circles/arcs
  699. return std::max( 1, KiROUND( radius ) );
  700. }
  701. void EDA_SHAPE::SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCenter )
  702. {
  703. m_arcMidData.start = aStart;
  704. m_arcMidData.end = aEnd;
  705. m_arcMidData.center = aCenter;
  706. m_arcMidData.mid = aMid;
  707. }
  708. void EDA_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
  709. {
  710. m_arcMidData = {};
  711. m_start = aStart;
  712. m_end = aEnd;
  713. m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
  714. VECTOR2I new_mid = GetArcMid();
  715. m_endsSwapped = false;
  716. // Watch the ordering here. GetArcMid above needs to be called prior to initializing the
  717. // m_arcMidData structure in order to ensure we get the calculated variant, not the cached
  718. SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
  719. /*
  720. * If the input winding doesn't match our internal winding, the calculated midpoint will end
  721. * up on the other side of the arc. In this case, we need to flip the start/end points and
  722. * flag this change for the system.
  723. */
  724. VECTOR2D dist( new_mid - aMid );
  725. VECTOR2D dist2( new_mid - m_arcCenter );
  726. if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
  727. {
  728. std::swap( m_start, m_end );
  729. m_endsSwapped = true;
  730. }
  731. }
  732. EDA_ANGLE EDA_SHAPE::GetSegmentAngle() const
  733. {
  734. EDA_ANGLE angle( atan2( static_cast<double>( GetStart().y - GetEnd().y ),
  735. static_cast<double>( GetEnd().x - GetStart().x ) ), RADIANS_T );
  736. return angle;
  737. }
  738. EDA_ANGLE EDA_SHAPE::GetArcAngle() const
  739. {
  740. EDA_ANGLE startAngle;
  741. EDA_ANGLE endAngle;
  742. CalcArcAngles( startAngle, endAngle );
  743. return endAngle - startAngle;
  744. }
  745. bool EDA_SHAPE::IsClockwiseArc() const
  746. {
  747. if( m_shape == SHAPE_T::ARC )
  748. {
  749. VECTOR2D mid = GetArcMid();
  750. double orient = ( mid.x - m_start.x ) * ( m_end.y - m_start.y )
  751. - ( mid.y - m_start.y ) * ( m_end.x - m_start.x );
  752. return orient < 0;
  753. }
  754. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  755. return false;
  756. }
  757. void EDA_SHAPE::SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeAngle )
  758. {
  759. EDA_ANGLE angle( aAngle );
  760. m_end = m_start;
  761. RotatePoint( m_end, m_arcCenter, -angle.Normalize720() );
  762. if( aCheckNegativeAngle && aAngle < ANGLE_0 )
  763. {
  764. std::swap( m_start, m_end );
  765. m_endsSwapped = true;
  766. }
  767. }
  768. wxString EDA_SHAPE::getFriendlyName() const
  769. {
  770. if( IsProxyItem() )
  771. {
  772. switch( m_shape )
  773. {
  774. case SHAPE_T::RECTANGLE: return _( "Pad Number Box" );
  775. case SHAPE_T::SEGMENT: return _( "Thermal Spoke Template" );
  776. default: return _( "Unrecognized" );
  777. }
  778. }
  779. else
  780. {
  781. switch( m_shape )
  782. {
  783. case SHAPE_T::CIRCLE: return _( "Circle" );
  784. case SHAPE_T::ARC: return _( "Arc" );
  785. case SHAPE_T::BEZIER: return _( "Curve" );
  786. case SHAPE_T::POLY: return _( "Polygon" );
  787. case SHAPE_T::RECTANGLE: return _( "Rectangle" );
  788. case SHAPE_T::SEGMENT: return _( "Segment" );
  789. default: return _( "Unrecognized" );
  790. }
  791. }
  792. }
  793. void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  794. {
  795. wxString msg;
  796. wxString shape = _( "Shape" );
  797. aList.emplace_back( shape, getFriendlyName() );
  798. switch( m_shape )
  799. {
  800. case SHAPE_T::CIRCLE:
  801. aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
  802. break;
  803. case SHAPE_T::ARC:
  804. msg = EDA_UNIT_UTILS::UI::MessageTextFromValue( GetArcAngle() );
  805. aList.emplace_back( _( "Angle" ), msg );
  806. aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
  807. break;
  808. case SHAPE_T::BEZIER:
  809. aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
  810. break;
  811. case SHAPE_T::POLY:
  812. msg.Printf( wxS( "%d" ), GetPolyShape().Outline(0).PointCount() );
  813. aList.emplace_back( _( "Points" ), msg );
  814. break;
  815. case SHAPE_T::RECTANGLE:
  816. aList.emplace_back( _( "Width" ),
  817. aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
  818. aList.emplace_back( _( "Height" ),
  819. aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
  820. break;
  821. case SHAPE_T::SEGMENT:
  822. {
  823. aList.emplace_back( _( "Length" ),
  824. aFrame->MessageTextFromValue( GetStart().Distance( GetEnd() ) ));
  825. // angle counter-clockwise from 3'o-clock
  826. EDA_ANGLE angle( atan2( (double)( GetStart().y - GetEnd().y ),
  827. (double)( GetEnd().x - GetStart().x ) ), RADIANS_T );
  828. aList.emplace_back( _( "Angle" ), EDA_UNIT_UTILS::UI::MessageTextFromValue( angle ) );
  829. break;
  830. }
  831. default:
  832. break;
  833. }
  834. m_stroke.GetMsgPanelInfo( aFrame, aList );
  835. }
  836. const BOX2I EDA_SHAPE::getBoundingBox() const
  837. {
  838. BOX2I bbox;
  839. switch( m_shape )
  840. {
  841. case SHAPE_T::RECTANGLE:
  842. for( VECTOR2I& pt : GetRectCorners() )
  843. bbox.Merge( pt );
  844. break;
  845. case SHAPE_T::SEGMENT:
  846. bbox.SetOrigin( GetStart() );
  847. bbox.SetEnd( GetEnd() );
  848. break;
  849. case SHAPE_T::CIRCLE:
  850. bbox.SetOrigin( GetStart() );
  851. bbox.Inflate( GetRadius() );
  852. break;
  853. case SHAPE_T::ARC:
  854. computeArcBBox( bbox );
  855. break;
  856. case SHAPE_T::POLY:
  857. if( m_poly.IsEmpty() )
  858. break;
  859. for( auto iter = m_poly.CIterate(); iter; iter++ )
  860. bbox.Merge( *iter );
  861. break;
  862. case SHAPE_T::BEZIER:
  863. // Bezier BBoxes are not trivial to compute, so we approximate it by
  864. // using the bounding box of the curve (not control!) points.
  865. for( const VECTOR2I& pt : m_bezierPoints )
  866. bbox.Merge( pt );
  867. break;
  868. default:
  869. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  870. break;
  871. }
  872. bbox.Inflate( std::max( 0, GetWidth() ) / 2 );
  873. bbox.Normalize();
  874. return bbox;
  875. }
  876. bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  877. {
  878. double maxdist = aAccuracy;
  879. if( GetWidth() > 0 )
  880. maxdist += GetWidth() / 2.0;
  881. switch( m_shape )
  882. {
  883. case SHAPE_T::CIRCLE:
  884. {
  885. double radius = GetRadius();
  886. double dist = aPosition.Distance( getCenter() );
  887. if( IsFilledForHitTesting() )
  888. return dist <= radius + maxdist; // Filled circle hit-test
  889. else
  890. return abs( radius - dist ) <= maxdist; // Ring hit-test
  891. }
  892. case SHAPE_T::ARC:
  893. {
  894. if( aPosition.Distance( m_start ) <= maxdist )
  895. return true;
  896. if( aPosition.Distance( m_end ) <= maxdist )
  897. return true;
  898. double radius = GetRadius();
  899. VECTOR2D relPos( VECTOR2D( aPosition ) - getCenter() );
  900. double dist = relPos.EuclideanNorm();
  901. if( IsFilledForHitTesting() )
  902. {
  903. // Check distance from arc center
  904. if( dist > radius + maxdist )
  905. return false;
  906. }
  907. else
  908. {
  909. // Check distance from arc circumference
  910. if( abs( radius - dist ) > maxdist )
  911. return false;
  912. }
  913. // Finally, check to see if it's within arc's swept angle.
  914. EDA_ANGLE startAngle;
  915. EDA_ANGLE endAngle;
  916. CalcArcAngles( startAngle, endAngle );
  917. EDA_ANGLE relPosAngle( relPos );
  918. startAngle.Normalize();
  919. endAngle.Normalize();
  920. relPosAngle.Normalize();
  921. if( endAngle > startAngle )
  922. return relPosAngle >= startAngle && relPosAngle <= endAngle;
  923. else
  924. return relPosAngle >= startAngle || relPosAngle <= endAngle;
  925. }
  926. case SHAPE_T::BEZIER:
  927. {
  928. const std::vector<VECTOR2I>* pts = &m_bezierPoints;
  929. std::vector<VECTOR2I> updatedBezierPoints;
  930. if( m_bezierPoints.empty() )
  931. {
  932. BEZIER_POLY converter( m_start, m_bezierC1, m_bezierC2, m_end );
  933. converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
  934. pts = &updatedBezierPoints;
  935. }
  936. for( unsigned int i = 1; i < pts->size(); i++ )
  937. {
  938. if( TestSegmentHit( aPosition, ( *pts )[i - 1], ( *pts )[i], maxdist ) )
  939. return true;
  940. }
  941. return false;
  942. }
  943. case SHAPE_T::SEGMENT:
  944. return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
  945. case SHAPE_T::RECTANGLE:
  946. if( IsProxyItem() || IsFilledForHitTesting() ) // Filled rect hit-test
  947. {
  948. SHAPE_POLY_SET poly;
  949. poly.NewOutline();
  950. for( const VECTOR2I& pt : GetRectCorners() )
  951. poly.Append( pt );
  952. return poly.Collide( aPosition, maxdist );
  953. }
  954. else // Open rect hit-test
  955. {
  956. std::vector<VECTOR2I> pts = GetRectCorners();
  957. return TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
  958. || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
  959. || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
  960. || TestSegmentHit( aPosition, pts[3], pts[0], maxdist );
  961. }
  962. case SHAPE_T::POLY:
  963. if( IsFilledForHitTesting() )
  964. {
  965. if( !m_poly.COutline( 0 ).IsClosed() )
  966. {
  967. // Only one outline is expected
  968. SHAPE_LINE_CHAIN copy( m_poly.COutline( 0 ) );
  969. copy.SetClosed( true );
  970. return copy.Collide( aPosition, maxdist );
  971. }
  972. else
  973. {
  974. return m_poly.Collide( aPosition, maxdist );
  975. }
  976. }
  977. else
  978. {
  979. return m_poly.CollideEdge( aPosition, nullptr, maxdist );
  980. }
  981. default:
  982. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  983. return false;
  984. }
  985. }
  986. bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  987. {
  988. BOX2I arect = aRect;
  989. arect.Normalize();
  990. arect.Inflate( aAccuracy );
  991. BOX2I bbox = getBoundingBox();
  992. switch( m_shape )
  993. {
  994. case SHAPE_T::CIRCLE:
  995. // Test if area intersects or contains the circle:
  996. if( aContained )
  997. {
  998. return arect.Contains( bbox );
  999. }
  1000. else
  1001. {
  1002. // If the rectangle does not intersect the bounding box, this is a much quicker test
  1003. if( !arect.Intersects( bbox ) )
  1004. return false;
  1005. else
  1006. return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
  1007. }
  1008. case SHAPE_T::ARC:
  1009. // Test for full containment of this arc in the rect
  1010. if( aContained )
  1011. {
  1012. return arect.Contains( bbox );
  1013. }
  1014. // Test if the rect crosses the arc
  1015. else
  1016. {
  1017. if( !arect.Intersects( bbox ) )
  1018. return false;
  1019. if( IsFilled() )
  1020. {
  1021. return ( arect.Intersects( getCenter(), GetStart() )
  1022. || arect.Intersects( getCenter(), GetEnd() )
  1023. || arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ) );
  1024. }
  1025. else
  1026. {
  1027. return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
  1028. }
  1029. }
  1030. case SHAPE_T::RECTANGLE:
  1031. if( aContained )
  1032. {
  1033. return arect.Contains( bbox );
  1034. }
  1035. else
  1036. {
  1037. std::vector<VECTOR2I> pts = GetRectCorners();
  1038. // Account for the width of the lines
  1039. arect.Inflate( GetWidth() / 2 );
  1040. return ( arect.Intersects( pts[0], pts[1] )
  1041. || arect.Intersects( pts[1], pts[2] )
  1042. || arect.Intersects( pts[2], pts[3] )
  1043. || arect.Intersects( pts[3], pts[0] ) );
  1044. }
  1045. case SHAPE_T::SEGMENT:
  1046. if( aContained )
  1047. {
  1048. return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
  1049. }
  1050. else
  1051. {
  1052. // Account for the width of the line
  1053. arect.Inflate( GetWidth() / 2 );
  1054. return arect.Intersects( GetStart(), GetEnd() );
  1055. }
  1056. case SHAPE_T::POLY:
  1057. if( aContained )
  1058. {
  1059. return arect.Contains( bbox );
  1060. }
  1061. else
  1062. {
  1063. // Fast test: if aRect is outside the polygon bounding box,
  1064. // rectangles cannot intersect
  1065. if( !arect.Intersects( bbox ) )
  1066. return false;
  1067. // Account for the width of the line
  1068. arect.Inflate( GetWidth() / 2 );
  1069. for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
  1070. {
  1071. const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
  1072. int count = poly.GetPointCount();
  1073. for( int jj = 0; jj < count; jj++ )
  1074. {
  1075. VECTOR2I vertex = poly.GetPoint( jj );
  1076. // Test if the point is within aRect
  1077. if( arect.Contains( vertex ) )
  1078. return true;
  1079. if( jj + 1 < count )
  1080. {
  1081. VECTOR2I vertexNext = poly.GetPoint( jj + 1 );
  1082. // Test if this edge intersects aRect
  1083. if( arect.Intersects( vertex, vertexNext ) )
  1084. return true;
  1085. }
  1086. else if( poly.IsClosed() )
  1087. {
  1088. VECTOR2I vertexNext = poly.GetPoint( 0 );
  1089. // Test if this edge intersects aRect
  1090. if( arect.Intersects( vertex, vertexNext ) )
  1091. return true;
  1092. }
  1093. }
  1094. }
  1095. return false;
  1096. }
  1097. case SHAPE_T::BEZIER:
  1098. if( aContained )
  1099. {
  1100. return arect.Contains( bbox );
  1101. }
  1102. else
  1103. {
  1104. // Fast test: if aRect is outside the polygon bounding box,
  1105. // rectangles cannot intersect
  1106. if( !arect.Intersects( bbox ) )
  1107. return false;
  1108. // Account for the width of the line
  1109. arect.Inflate( GetWidth() / 2 );
  1110. const std::vector<VECTOR2I>* pts = &m_bezierPoints;
  1111. std::vector<VECTOR2I> updatedBezierPoints;
  1112. if( m_bezierPoints.empty() )
  1113. {
  1114. BEZIER_POLY converter( m_start, m_bezierC1, m_bezierC2, m_end );
  1115. converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
  1116. pts = &updatedBezierPoints;
  1117. }
  1118. for( unsigned ii = 1; ii < pts->size(); ii++ )
  1119. {
  1120. VECTOR2I vertex = ( *pts )[ii - 1];
  1121. VECTOR2I vertexNext = ( *pts )[ii];
  1122. // Test if the point is within aRect
  1123. if( arect.Contains( vertex ) )
  1124. return true;
  1125. // Test if this edge intersects aRect
  1126. if( arect.Intersects( vertex, vertexNext ) )
  1127. return true;
  1128. }
  1129. return false;
  1130. }
  1131. default:
  1132. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  1133. return false;
  1134. }
  1135. }
  1136. std::vector<VECTOR2I> EDA_SHAPE::GetRectCorners() const
  1137. {
  1138. std::vector<VECTOR2I> pts;
  1139. VECTOR2I topLeft = GetStart();
  1140. VECTOR2I botRight = GetEnd();
  1141. pts.emplace_back( topLeft );
  1142. pts.emplace_back( botRight.x, topLeft.y );
  1143. pts.emplace_back( botRight );
  1144. pts.emplace_back( topLeft.x, botRight.y );
  1145. return pts;
  1146. }
  1147. void EDA_SHAPE::computeArcBBox( BOX2I& aBBox ) const
  1148. {
  1149. // Start, end, and each inflection point the arc crosses will enclose the entire arc.
  1150. // Only include the center when filled; it's not necessarily inside the BB of an unfilled
  1151. // arc with a small included angle.
  1152. aBBox.SetOrigin( m_start );
  1153. aBBox.Merge( m_end );
  1154. if( IsFilled() )
  1155. aBBox.Merge( m_arcCenter );
  1156. int radius = GetRadius();
  1157. EDA_ANGLE t1, t2;
  1158. CalcArcAngles( t1, t2 );
  1159. t1.Normalize();
  1160. t2.Normalize();
  1161. if( t2 > t1 )
  1162. {
  1163. if( t1 < ANGLE_0 && t2 > ANGLE_0 )
  1164. aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
  1165. if( t1 < ANGLE_90 && t2 > ANGLE_90 )
  1166. aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
  1167. if( t1 < ANGLE_180 && t2 > ANGLE_180 )
  1168. aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
  1169. if( t1 < ANGLE_270 && t2 > ANGLE_270 )
  1170. aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
  1171. }
  1172. else
  1173. {
  1174. if( t1 < ANGLE_0 || t2 > ANGLE_0 )
  1175. aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
  1176. if( t1 < ANGLE_90 || t2 > ANGLE_90 )
  1177. aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
  1178. if( t1 < ANGLE_180 || t2 > ANGLE_180 )
  1179. aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
  1180. if( t1 < ANGLE_270 || t2 > ANGLE_270 )
  1181. aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
  1182. }
  1183. }
  1184. void EDA_SHAPE::SetPolyPoints( const std::vector<VECTOR2I>& aPoints )
  1185. {
  1186. m_poly.RemoveAllContours();
  1187. m_poly.NewOutline();
  1188. for( const VECTOR2I& p : aPoints )
  1189. m_poly.Append( p.x, p.y );
  1190. }
  1191. std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly ) const
  1192. {
  1193. std::vector<SHAPE*> effectiveShapes;
  1194. int width = GetEffectiveWidth();
  1195. switch( m_shape )
  1196. {
  1197. case SHAPE_T::ARC:
  1198. effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle(), width ) );
  1199. break;
  1200. case SHAPE_T::SEGMENT:
  1201. effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, width ) );
  1202. break;
  1203. case SHAPE_T::RECTANGLE:
  1204. {
  1205. std::vector<VECTOR2I> pts = GetRectCorners();
  1206. if( ( IsFilled() || IsProxyItem() ) && !aEdgeOnly )
  1207. effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
  1208. if( width > 0 || !IsFilled() || aEdgeOnly )
  1209. {
  1210. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
  1211. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
  1212. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
  1213. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
  1214. }
  1215. }
  1216. break;
  1217. case SHAPE_T::CIRCLE:
  1218. {
  1219. if( IsFilled() && !aEdgeOnly )
  1220. effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
  1221. if( width > 0 || !IsFilled() || aEdgeOnly )
  1222. effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetEnd(), ANGLE_360, width ) );
  1223. break;
  1224. }
  1225. case SHAPE_T::BEZIER:
  1226. {
  1227. std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width / 2 );
  1228. VECTOR2I start_pt = bezierPoints[0];
  1229. for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
  1230. {
  1231. VECTOR2I end_pt = bezierPoints[jj];
  1232. effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, width ) );
  1233. start_pt = end_pt;
  1234. }
  1235. break;
  1236. }
  1237. case SHAPE_T::POLY:
  1238. {
  1239. if( GetPolyShape().OutlineCount() == 0 ) // malformed/empty polygon
  1240. break;
  1241. for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
  1242. {
  1243. const SHAPE_LINE_CHAIN& l = GetPolyShape().COutline( ii );
  1244. if( IsFilled() && !aEdgeOnly )
  1245. effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
  1246. if( width > 0 || !IsFilled() || aEdgeOnly )
  1247. {
  1248. int segCount = l.SegmentCount();
  1249. if( aLineChainOnly && l.IsClosed() )
  1250. segCount--; // Treat closed chain as open
  1251. for( int jj = 0; jj < segCount; jj++ )
  1252. effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.CSegment( jj ), width ) );
  1253. }
  1254. }
  1255. }
  1256. break;
  1257. default:
  1258. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  1259. break;
  1260. }
  1261. return effectiveShapes;
  1262. }
  1263. void EDA_SHAPE::DupPolyPointsList( std::vector<VECTOR2I>& aBuffer ) const
  1264. {
  1265. for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
  1266. {
  1267. int pointCount = m_poly.COutline( ii ).PointCount();
  1268. if( pointCount )
  1269. {
  1270. aBuffer.reserve( pointCount );
  1271. for ( auto iter = m_poly.CIterate(); iter; iter++ )
  1272. aBuffer.emplace_back( iter->x, iter->y );
  1273. }
  1274. }
  1275. }
  1276. bool EDA_SHAPE::IsPolyShapeValid() const
  1277. {
  1278. // return true if the polygonal shape is valid (has more than 2 points)
  1279. return GetPolyShape().OutlineCount() > 0 && GetPolyShape().Outline( 0 ).PointCount() > 2;
  1280. }
  1281. int EDA_SHAPE::GetPointCount() const
  1282. {
  1283. // return the number of corners of the polygonal shape
  1284. // this shape is expected to be only one polygon without hole
  1285. return GetPolyShape().OutlineCount() ? GetPolyShape().VertexCount( 0 ) : 0;
  1286. }
  1287. void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
  1288. {
  1289. switch( GetShape() )
  1290. {
  1291. case SHAPE_T::SEGMENT:
  1292. case SHAPE_T::CIRCLE:
  1293. case SHAPE_T::RECTANGLE:
  1294. SetStart( aPosition );
  1295. SetEnd( aPosition );
  1296. break;
  1297. case SHAPE_T::ARC:
  1298. SetArcGeometry( aPosition, aPosition, aPosition );
  1299. m_editState = 1;
  1300. break;
  1301. case SHAPE_T::BEZIER:
  1302. SetStart( aPosition );
  1303. SetEnd( aPosition );
  1304. SetBezierC1( aPosition );
  1305. SetBezierC2( aPosition );
  1306. m_editState = 1;
  1307. RebuildBezierToSegmentsPointsList( GetWidth() / 2 );
  1308. break;
  1309. case SHAPE_T::POLY:
  1310. m_poly.NewOutline();
  1311. m_poly.Outline( 0 ).SetClosed( false );
  1312. // Start and end of the first segment (co-located for now)
  1313. m_poly.Outline( 0 ).Append( aPosition );
  1314. m_poly.Outline( 0 ).Append( aPosition, true );
  1315. break;
  1316. default:
  1317. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  1318. }
  1319. }
  1320. bool EDA_SHAPE::continueEdit( const VECTOR2I& aPosition )
  1321. {
  1322. switch( GetShape() )
  1323. {
  1324. case SHAPE_T::ARC:
  1325. case SHAPE_T::SEGMENT:
  1326. case SHAPE_T::CIRCLE:
  1327. case SHAPE_T::RECTANGLE:
  1328. return false;
  1329. case SHAPE_T::BEZIER:
  1330. if( m_editState == 3 )
  1331. return false;
  1332. m_editState++;
  1333. return true;
  1334. case SHAPE_T::POLY:
  1335. {
  1336. SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
  1337. // do not add zero-length segments
  1338. if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
  1339. poly.Append( aPosition, true );
  1340. }
  1341. return true;
  1342. default:
  1343. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  1344. return false;
  1345. }
  1346. }
  1347. void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
  1348. {
  1349. #define sq( x ) pow( x, 2 )
  1350. switch( GetShape() )
  1351. {
  1352. case SHAPE_T::SEGMENT:
  1353. case SHAPE_T::CIRCLE:
  1354. case SHAPE_T::RECTANGLE:
  1355. SetEnd( aPosition );
  1356. break;
  1357. case SHAPE_T::BEZIER:
  1358. {
  1359. switch( m_editState )
  1360. {
  1361. case 0:
  1362. SetStart( aPosition );
  1363. SetEnd( aPosition );
  1364. SetBezierC1( aPosition );
  1365. SetBezierC2( aPosition );
  1366. break;
  1367. case 1:
  1368. SetBezierC2( aPosition );
  1369. SetEnd( aPosition );
  1370. break;
  1371. case 2: SetBezierC1( aPosition ); break;
  1372. case 3: SetBezierC2( aPosition ); break;
  1373. }
  1374. RebuildBezierToSegmentsPointsList( GetWidth() / 2 );
  1375. }
  1376. break;
  1377. case SHAPE_T::ARC:
  1378. {
  1379. double radius = GetRadius();
  1380. EDA_ANGLE lastAngle = GetArcAngle();
  1381. // Edit state 0: drawing: place start
  1382. // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
  1383. // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
  1384. // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
  1385. // Edit state 4: point edit: move center
  1386. // Edit state 5: point edit: move arc-mid-point
  1387. switch( m_editState )
  1388. {
  1389. case 0:
  1390. SetArcGeometry( aPosition, aPosition, aPosition );
  1391. return;
  1392. case 1:
  1393. m_end = aPosition;
  1394. radius = m_start.Distance( m_end ) * M_SQRT1_2;
  1395. break;
  1396. case 2:
  1397. case 3:
  1398. {
  1399. VECTOR2I v = m_start - m_end;
  1400. double chordBefore = v.SquaredEuclideanNorm();
  1401. if( m_editState == 2 )
  1402. m_start = aPosition;
  1403. else
  1404. m_end = aPosition;
  1405. v = m_start - m_end;
  1406. double chordAfter = v.SquaredEuclideanNorm();
  1407. double ratio = 0.0;
  1408. if( chordBefore > 0 )
  1409. ratio = chordAfter / chordBefore;
  1410. if( ratio != 0 )
  1411. radius = std::max( sqrt( sq( radius ) * ratio ), sqrt( chordAfter ) / 2 );
  1412. }
  1413. break;
  1414. case 4:
  1415. {
  1416. double radialA = m_start.Distance( aPosition );
  1417. double radialB = m_end.Distance( aPosition );
  1418. radius = ( radialA + radialB ) / 2.0;
  1419. }
  1420. break;
  1421. case 5:
  1422. SetArcGeometry( GetStart(), aPosition, GetEnd() );
  1423. return;
  1424. }
  1425. // Calculate center based on start, end, and radius
  1426. //
  1427. // Let 'l' be the length of the chord and 'm' the middle point of the chord
  1428. double l = m_start.Distance( m_end );
  1429. VECTOR2D m = ( m_start + m_end ) / 2;
  1430. double sqRadDiff = ( radius * radius ) - ( l * l ) / 4.0;
  1431. // Calculate 'd', the vector from the chord midpoint to the center
  1432. VECTOR2D d;
  1433. if( l > 0 && sqRadDiff >= 0 )
  1434. {
  1435. d.x = sqrt( sqRadDiff ) * ( m_start.y - m_end.y ) / l;
  1436. d.y = sqrt( sqRadDiff ) * ( m_end.x - m_start.x ) / l;
  1437. }
  1438. VECTOR2I c1 = KiROUND( m + d );
  1439. VECTOR2I c2 = KiROUND( m - d );
  1440. // Solution gives us 2 centers; we need to pick one:
  1441. switch( m_editState )
  1442. {
  1443. case 1:
  1444. // Keep arc clockwise while drawing i.e. arc angle = 90 deg.
  1445. // it can be 90 or 270 deg depending on the arc center choice (c1 or c2)
  1446. m_arcCenter = c1; // first trial
  1447. if( GetArcAngle() > ANGLE_180 )
  1448. m_arcCenter = c2;
  1449. break;
  1450. case 2:
  1451. case 3:
  1452. // Pick the one of c1, c2 to keep arc on the same side
  1453. m_arcCenter = c1; // first trial
  1454. if( ( lastAngle < ANGLE_180 ) != ( GetArcAngle() < ANGLE_180 ) )
  1455. m_arcCenter = c2;
  1456. break;
  1457. case 4:
  1458. // Pick the one closer to the mouse position
  1459. m_arcCenter = c1.Distance( aPosition ) < c2.Distance( aPosition ) ? c1 : c2;
  1460. break;
  1461. }
  1462. }
  1463. break;
  1464. case SHAPE_T::POLY:
  1465. m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
  1466. break;
  1467. default:
  1468. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  1469. }
  1470. }
  1471. void EDA_SHAPE::endEdit( bool aClosed )
  1472. {
  1473. switch( GetShape() )
  1474. {
  1475. case SHAPE_T::ARC:
  1476. case SHAPE_T::SEGMENT:
  1477. case SHAPE_T::CIRCLE:
  1478. case SHAPE_T::RECTANGLE:
  1479. case SHAPE_T::BEZIER:
  1480. break;
  1481. case SHAPE_T::POLY:
  1482. {
  1483. SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
  1484. // do not include last point twice
  1485. if( poly.GetPointCount() > 2 )
  1486. {
  1487. if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
  1488. {
  1489. poly.SetClosed( aClosed );
  1490. }
  1491. else
  1492. {
  1493. poly.SetClosed( false );
  1494. poly.Remove( poly.GetPointCount() - 1 );
  1495. }
  1496. }
  1497. }
  1498. break;
  1499. default:
  1500. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  1501. }
  1502. }
  1503. void EDA_SHAPE::SwapShape( EDA_SHAPE* aImage )
  1504. {
  1505. EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
  1506. assert( image );
  1507. #define SWAPITEM( x ) std::swap( x, image->x )
  1508. SWAPITEM( m_stroke );
  1509. SWAPITEM( m_start );
  1510. SWAPITEM( m_end );
  1511. SWAPITEM( m_arcCenter );
  1512. SWAPITEM( m_shape );
  1513. SWAPITEM( m_bezierC1 );
  1514. SWAPITEM( m_bezierC2 );
  1515. SWAPITEM( m_bezierPoints );
  1516. SWAPITEM( m_poly );
  1517. SWAPITEM( m_fill );
  1518. SWAPITEM( m_fillColor );
  1519. SWAPITEM( m_editState );
  1520. SWAPITEM( m_endsSwapped );
  1521. #undef SWAPITEM
  1522. }
  1523. int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
  1524. {
  1525. #define EPSILON 2 // Should be enough for rounding errors on calculated items
  1526. #define TEST( a, b ) { if( a != b ) return a - b; }
  1527. #define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
  1528. #define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
  1529. TEST_PT( m_start, aOther->m_start );
  1530. TEST_PT( m_end, aOther->m_end );
  1531. TEST( (int) m_shape, (int) aOther->m_shape );
  1532. if( m_shape == SHAPE_T::ARC )
  1533. {
  1534. TEST_PT( m_arcCenter, aOther->m_arcCenter );
  1535. }
  1536. else if( m_shape == SHAPE_T::BEZIER )
  1537. {
  1538. TEST_PT( m_bezierC1, aOther->m_bezierC1 );
  1539. TEST_PT( m_bezierC2, aOther->m_bezierC2 );
  1540. }
  1541. else if( m_shape == SHAPE_T::POLY )
  1542. {
  1543. TEST( m_poly.TotalVertices(), aOther->m_poly.TotalVertices() );
  1544. }
  1545. for( size_t ii = 0; ii < m_bezierPoints.size(); ++ii )
  1546. TEST_PT( m_bezierPoints[ii], aOther->m_bezierPoints[ii] );
  1547. for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
  1548. TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
  1549. TEST_E( m_stroke.GetWidth(), aOther->m_stroke.GetWidth() );
  1550. TEST( (int) m_stroke.GetLineStyle(), (int) aOther->m_stroke.GetLineStyle() );
  1551. TEST( (int) m_fill, (int) aOther->m_fill );
  1552. return 0;
  1553. }
  1554. void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
  1555. ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const
  1556. {
  1557. int width = ignoreLineWidth ? 0 : GetWidth();
  1558. width += 2 * aClearance;
  1559. switch( m_shape )
  1560. {
  1561. case SHAPE_T::CIRCLE:
  1562. {
  1563. int r = GetRadius();
  1564. if( IsFilled() )
  1565. TransformCircleToPolygon( aBuffer, getCenter(), r + width / 2, aError, aErrorLoc );
  1566. else
  1567. TransformRingToPolygon( aBuffer, getCenter(), r, width, aError, aErrorLoc );
  1568. break;
  1569. }
  1570. case SHAPE_T::RECTANGLE:
  1571. {
  1572. std::vector<VECTOR2I> pts = GetRectCorners();
  1573. if( IsFilled() || IsProxyItem() )
  1574. {
  1575. aBuffer.NewOutline();
  1576. for( const VECTOR2I& pt : pts )
  1577. aBuffer.Append( pt );
  1578. }
  1579. if( width > 0 || !IsFilled() )
  1580. {
  1581. // Add in segments
  1582. TransformOvalToPolygon( aBuffer, pts[0], pts[1], width, aError, aErrorLoc );
  1583. TransformOvalToPolygon( aBuffer, pts[1], pts[2], width, aError, aErrorLoc );
  1584. TransformOvalToPolygon( aBuffer, pts[2], pts[3], width, aError, aErrorLoc );
  1585. TransformOvalToPolygon( aBuffer, pts[3], pts[0], width, aError, aErrorLoc );
  1586. }
  1587. break;
  1588. }
  1589. case SHAPE_T::ARC:
  1590. TransformArcToPolygon( aBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError, aErrorLoc );
  1591. break;
  1592. case SHAPE_T::SEGMENT:
  1593. TransformOvalToPolygon( aBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
  1594. break;
  1595. case SHAPE_T::POLY:
  1596. {
  1597. if( !IsPolyShapeValid() )
  1598. break;
  1599. if( IsFilled() )
  1600. {
  1601. for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
  1602. {
  1603. const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
  1604. SHAPE_POLY_SET tmp;
  1605. tmp.NewOutline();
  1606. for( int jj = 0; jj < (int) poly.GetPointCount(); ++jj )
  1607. tmp.Append( poly.GetPoint( jj ) );
  1608. if( width > 0 )
  1609. {
  1610. int inflate = width / 2;
  1611. if( aErrorLoc == ERROR_OUTSIDE )
  1612. inflate += aError;
  1613. tmp.Inflate( inflate, CORNER_STRATEGY::ROUND_ALL_CORNERS, aError );
  1614. }
  1615. aBuffer.Append( tmp );
  1616. }
  1617. }
  1618. else
  1619. {
  1620. for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
  1621. {
  1622. const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
  1623. for( int jj = 0; jj < (int) poly.SegmentCount(); ++jj )
  1624. {
  1625. const SEG& seg = poly.GetSegment( jj );
  1626. TransformOvalToPolygon( aBuffer, seg.A, seg.B, width, aError, aErrorLoc );
  1627. }
  1628. }
  1629. }
  1630. break;
  1631. }
  1632. case SHAPE_T::BEZIER:
  1633. {
  1634. std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
  1635. BEZIER_POLY converter( ctrlPts );
  1636. std::vector<VECTOR2I> poly;
  1637. converter.GetPoly( poly, aError );
  1638. for( unsigned ii = 1; ii < poly.size(); ii++ )
  1639. TransformOvalToPolygon( aBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );
  1640. break;
  1641. }
  1642. default:
  1643. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  1644. break;
  1645. }
  1646. }
  1647. void EDA_SHAPE::SetLineStyle( const LINE_STYLE aStyle )
  1648. {
  1649. m_stroke.SetLineStyle( aStyle );
  1650. }
  1651. LINE_STYLE EDA_SHAPE::GetLineStyle() const
  1652. {
  1653. if( m_stroke.GetLineStyle() != LINE_STYLE::DEFAULT )
  1654. return m_stroke.GetLineStyle();
  1655. return LINE_STYLE::SOLID;
  1656. }
  1657. bool EDA_SHAPE::operator==( const EDA_SHAPE& aOther ) const
  1658. {
  1659. if( GetShape() != aOther.GetShape() )
  1660. return false;
  1661. if( m_fill != aOther.m_fill )
  1662. return false;
  1663. if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
  1664. return false;
  1665. if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
  1666. return false;
  1667. if( m_fillColor != aOther.m_fillColor )
  1668. return false;
  1669. if( m_start != aOther.m_start )
  1670. return false;
  1671. if( m_end != aOther.m_end )
  1672. return false;
  1673. if( m_arcCenter != aOther.m_arcCenter )
  1674. return false;
  1675. if( m_bezierC1 != aOther.m_bezierC1 )
  1676. return false;
  1677. if( m_bezierC2 != aOther.m_bezierC2 )
  1678. return false;
  1679. if( m_bezierPoints != aOther.m_bezierPoints )
  1680. return false;
  1681. for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
  1682. {
  1683. if( m_poly.CVertex( ii ) != aOther.m_poly.CVertex( ii ) )
  1684. return false;
  1685. }
  1686. return true;
  1687. }
  1688. double EDA_SHAPE::Similarity( const EDA_SHAPE& aOther ) const
  1689. {
  1690. if( GetShape() != aOther.GetShape() )
  1691. return 0.0;
  1692. double similarity = 1.0;
  1693. if( m_fill != aOther.m_fill )
  1694. similarity *= 0.9;
  1695. if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
  1696. similarity *= 0.9;
  1697. if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
  1698. similarity *= 0.9;
  1699. if( m_fillColor != aOther.m_fillColor )
  1700. similarity *= 0.9;
  1701. if( m_start != aOther.m_start )
  1702. similarity *= 0.9;
  1703. if( m_end != aOther.m_end )
  1704. similarity *= 0.9;
  1705. if( m_arcCenter != aOther.m_arcCenter )
  1706. similarity *= 0.9;
  1707. if( m_bezierC1 != aOther.m_bezierC1 )
  1708. similarity *= 0.9;
  1709. if( m_bezierC2 != aOther.m_bezierC2 )
  1710. similarity *= 0.9;
  1711. {
  1712. int m = m_bezierPoints.size();
  1713. int n = aOther.m_bezierPoints.size();
  1714. size_t longest = alg::longest_common_subset( m_bezierPoints, aOther.m_bezierPoints );
  1715. similarity *= std::pow( 0.9, m + n - 2 * longest );
  1716. }
  1717. {
  1718. int m = m_poly.TotalVertices();
  1719. int n = aOther.m_poly.TotalVertices();
  1720. std::vector<VECTOR2I> poly;
  1721. std::vector<VECTOR2I> otherPoly;
  1722. VECTOR2I lastPt( 0, 0 );
  1723. // We look for the longest common subset of the two polygons, but we need to
  1724. // offset each point because we're actually looking for overall similarity, not just
  1725. // exact matches. So if the zone is moved by 1IU, we only want one point to be
  1726. // considered "moved" rather than the entire polygon. In this case, the first point
  1727. // will not be a match but the rest of the sequence will.
  1728. for( int ii = 0; ii < m; ++ii )
  1729. {
  1730. poly.emplace_back( lastPt - m_poly.CVertex( ii ) );
  1731. lastPt = m_poly.CVertex( ii );
  1732. }
  1733. lastPt = VECTOR2I( 0, 0 );
  1734. for( int ii = 0; ii < n; ++ii )
  1735. {
  1736. otherPoly.emplace_back( lastPt - aOther.m_poly.CVertex( ii ) );
  1737. lastPt = aOther.m_poly.CVertex( ii );
  1738. }
  1739. size_t longest = alg::longest_common_subset( poly, otherPoly );
  1740. similarity *= std::pow( 0.9, m + n - 2 * longest );
  1741. }
  1742. return similarity;
  1743. }
  1744. IMPLEMENT_ENUM_TO_WXANY( SHAPE_T )
  1745. IMPLEMENT_ENUM_TO_WXANY( LINE_STYLE )
  1746. static struct EDA_SHAPE_DESC
  1747. {
  1748. EDA_SHAPE_DESC()
  1749. {
  1750. ENUM_MAP<SHAPE_T>::Instance()
  1751. .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
  1752. .Map( SHAPE_T::RECTANGLE, _HKI( "Rectangle" ) )
  1753. .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
  1754. .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
  1755. .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
  1756. .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
  1757. auto& plotDashTypeEnum = ENUM_MAP<LINE_STYLE>::Instance();
  1758. if( plotDashTypeEnum.Choices().GetCount() == 0 )
  1759. {
  1760. plotDashTypeEnum.Map( LINE_STYLE::DEFAULT, _HKI( "Default" ) )
  1761. .Map( LINE_STYLE::SOLID, _HKI( "Solid" ) )
  1762. .Map( LINE_STYLE::DASH, _HKI( "Dashed" ) )
  1763. .Map( LINE_STYLE::DOT, _HKI( "Dotted" ) )
  1764. .Map( LINE_STYLE::DASHDOT, _HKI( "Dash-Dot" ) )
  1765. .Map( LINE_STYLE::DASHDOTDOT, _HKI( "Dash-Dot-Dot" ) );
  1766. }
  1767. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1768. REGISTER_TYPE( EDA_SHAPE );
  1769. auto isNotPolygonOrCircle = []( INSPECTABLE* aItem ) -> bool
  1770. {
  1771. // Polygons, unlike other shapes, have no meaningful start or end coordinates
  1772. if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
  1773. return shape->GetShape() != SHAPE_T::POLY && shape->GetShape() != SHAPE_T::CIRCLE;
  1774. return false;
  1775. };
  1776. auto isCircle = []( INSPECTABLE* aItem ) -> bool
  1777. {
  1778. // Polygons, unlike other shapes, have no meaningful start or end coordinates
  1779. if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
  1780. return shape->GetShape() == SHAPE_T::CIRCLE;
  1781. return false;
  1782. };
  1783. auto isRectangle = []( INSPECTABLE* aItem ) -> bool
  1784. {
  1785. // Polygons, unlike other shapes, have no meaningful start or end coordinates
  1786. if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
  1787. return shape->GetShape() == SHAPE_T::RECTANGLE;
  1788. return false;
  1789. };
  1790. const wxString shapeProps = _HKI( "Shape Properties" );
  1791. auto shape = new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
  1792. NO_SETTER( EDA_SHAPE, SHAPE_T ), &EDA_SHAPE::GetShape );
  1793. propMgr.AddProperty( shape, shapeProps );
  1794. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
  1795. &EDA_SHAPE::SetStartX, &EDA_SHAPE::GetStartX, PROPERTY_DISPLAY::PT_COORD,
  1796. ORIGIN_TRANSFORMS::ABS_X_COORD ),
  1797. shapeProps )
  1798. .SetAvailableFunc( isNotPolygonOrCircle );
  1799. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
  1800. &EDA_SHAPE::SetStartY, &EDA_SHAPE::GetStartY, PROPERTY_DISPLAY::PT_COORD,
  1801. ORIGIN_TRANSFORMS::ABS_Y_COORD ),
  1802. shapeProps )
  1803. .SetAvailableFunc( isNotPolygonOrCircle );
  1804. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center X" ),
  1805. &EDA_SHAPE::SetCenterX, &EDA_SHAPE::GetStartX, PROPERTY_DISPLAY::PT_COORD,
  1806. ORIGIN_TRANSFORMS::ABS_X_COORD ),
  1807. shapeProps )
  1808. .SetAvailableFunc( isCircle );
  1809. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center Y" ),
  1810. &EDA_SHAPE::SetCenterY, &EDA_SHAPE::GetStartY, PROPERTY_DISPLAY::PT_COORD,
  1811. ORIGIN_TRANSFORMS::ABS_Y_COORD ),
  1812. shapeProps )
  1813. .SetAvailableFunc( isCircle );
  1814. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Radius" ),
  1815. &EDA_SHAPE::SetRadius, &EDA_SHAPE::GetRadius, PROPERTY_DISPLAY::PT_SIZE,
  1816. ORIGIN_TRANSFORMS::NOT_A_COORD ),
  1817. shapeProps )
  1818. .SetAvailableFunc( isCircle );
  1819. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
  1820. &EDA_SHAPE::SetEndX, &EDA_SHAPE::GetEndX, PROPERTY_DISPLAY::PT_COORD,
  1821. ORIGIN_TRANSFORMS::ABS_X_COORD ),
  1822. shapeProps )
  1823. .SetAvailableFunc( isNotPolygonOrCircle );
  1824. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
  1825. &EDA_SHAPE::SetEndY, &EDA_SHAPE::GetEndY, PROPERTY_DISPLAY::PT_COORD,
  1826. ORIGIN_TRANSFORMS::ABS_Y_COORD ),
  1827. shapeProps )
  1828. .SetAvailableFunc( isNotPolygonOrCircle );
  1829. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Width" ),
  1830. &EDA_SHAPE::SetRectangleWidth, &EDA_SHAPE::GetRectangleWidth,
  1831. PROPERTY_DISPLAY::PT_COORD, ORIGIN_TRANSFORMS::ABS_Y_COORD ),
  1832. shapeProps )
  1833. .SetAvailableFunc( isRectangle );
  1834. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Height" ),
  1835. &EDA_SHAPE::SetRectangleHeight, &EDA_SHAPE::GetRectangleHeight,
  1836. PROPERTY_DISPLAY::PT_COORD, ORIGIN_TRANSFORMS::ABS_Y_COORD ),
  1837. shapeProps )
  1838. .SetAvailableFunc( isRectangle );
  1839. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
  1840. &EDA_SHAPE::SetWidth, &EDA_SHAPE::GetWidth, PROPERTY_DISPLAY::PT_SIZE ),
  1841. shapeProps );
  1842. propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, LINE_STYLE>( _HKI( "Line Style" ),
  1843. &EDA_SHAPE::SetLineStyle, &EDA_SHAPE::GetLineStyle ),
  1844. shapeProps );
  1845. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Line Color" ),
  1846. &EDA_SHAPE::SetLineColor, &EDA_SHAPE::GetLineColor ),
  1847. shapeProps )
  1848. .SetIsHiddenFromRulesEditor();
  1849. auto angle = new PROPERTY<EDA_SHAPE, EDA_ANGLE>( _HKI( "Angle" ),
  1850. NO_SETTER( EDA_SHAPE, EDA_ANGLE ), &EDA_SHAPE::GetArcAngle,
  1851. PROPERTY_DISPLAY::PT_DECIDEGREE );
  1852. angle->SetAvailableFunc(
  1853. [=]( INSPECTABLE* aItem ) -> bool
  1854. {
  1855. if( EDA_SHAPE* curr_shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
  1856. return curr_shape->GetShape() == SHAPE_T::ARC;
  1857. return false;
  1858. } );
  1859. propMgr.AddProperty( angle, shapeProps );
  1860. auto fillAvailable =
  1861. [=]( INSPECTABLE* aItem ) -> bool
  1862. {
  1863. if( EDA_ITEM* edaItem = dynamic_cast<EDA_ITEM*>( aItem ) )
  1864. {
  1865. // For some reason masking "Filled" and "Fill Color" at the
  1866. // PCB_TABLECELL level doesn't work.
  1867. if( edaItem->Type() == PCB_TABLECELL_T )
  1868. return false;
  1869. }
  1870. if( EDA_SHAPE* edaShape = dynamic_cast<EDA_SHAPE*>( aItem ) )
  1871. {
  1872. switch( edaShape->GetShape() )
  1873. {
  1874. case SHAPE_T::POLY:
  1875. case SHAPE_T::RECTANGLE:
  1876. case SHAPE_T::CIRCLE:
  1877. case SHAPE_T::BEZIER:
  1878. return true;
  1879. default:
  1880. return false;
  1881. }
  1882. }
  1883. return false;
  1884. };
  1885. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, bool>( _HKI( "Filled" ),
  1886. &EDA_SHAPE::SetFilled, &EDA_SHAPE::IsFilled ),
  1887. shapeProps )
  1888. .SetAvailableFunc( fillAvailable );
  1889. propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Fill Color" ),
  1890. &EDA_SHAPE::SetFillColor, &EDA_SHAPE::GetFillColor ),
  1891. shapeProps )
  1892. .SetAvailableFunc( fillAvailable )
  1893. .SetIsHiddenFromRulesEditor();
  1894. }
  1895. } _EDA_SHAPE_DESC;