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.

896 lines
24 KiB

14 years ago
14 years ago
14 years ago
14 years ago
12 years ago
12 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 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@verizon.net>
  7. * Copyright (C) 1992-2017 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. /**
  27. * @file class_drawsegment.cpp
  28. * @brief Class and functions to handle a graphic segments.
  29. */
  30. #include <fctsys.h>
  31. #include <macros.h>
  32. #include <gr_basic.h>
  33. #include <bezier_curves.h>
  34. #include <class_drawpanel.h>
  35. #include <pcb_screen.h>
  36. #include <trigo.h>
  37. #include <msgpanel.h>
  38. #include <bitmaps.h>
  39. #include <pcb_edit_frame.h>
  40. #include <pcbnew.h>
  41. #include <class_board.h>
  42. #include <class_module.h>
  43. #include <class_drawsegment.h>
  44. #include <base_units.h>
  45. DRAWSEGMENT::DRAWSEGMENT( BOARD_ITEM* aParent, KICAD_T idtype ) :
  46. BOARD_ITEM( aParent, idtype )
  47. {
  48. m_Type = 0;
  49. m_Angle = 0;
  50. m_Flags = 0;
  51. m_Shape = S_SEGMENT;
  52. // Gives a decent pen size to draw shape:
  53. m_Width = m_Shape == S_POLYGON ? 0 : Millimeter2iu( 0.15 );
  54. }
  55. DRAWSEGMENT::~DRAWSEGMENT()
  56. {
  57. }
  58. void DRAWSEGMENT::SetPosition( const wxPoint& aPos )
  59. {
  60. m_Start = aPos;
  61. }
  62. const wxPoint DRAWSEGMENT::GetPosition() const
  63. {
  64. if( m_Shape == S_POLYGON )
  65. return (wxPoint) m_Poly.CVertex( 0 );
  66. else
  67. return m_Start;
  68. }
  69. void DRAWSEGMENT::Move( const wxPoint& aMoveVector )
  70. {
  71. m_Start += aMoveVector;
  72. m_End += aMoveVector;
  73. switch ( m_Shape )
  74. {
  75. case S_POLYGON:
  76. for( auto iter = m_Poly.Iterate(); iter; iter++ )
  77. {
  78. (*iter) += VECTOR2I( aMoveVector );
  79. }
  80. break;
  81. default:
  82. break;
  83. }
  84. }
  85. void DRAWSEGMENT::Rotate( const wxPoint& aRotCentre, double aAngle )
  86. {
  87. switch( m_Shape )
  88. {
  89. case S_ARC:
  90. case S_SEGMENT:
  91. case S_CIRCLE:
  92. // these can all be done by just rotating the start and end points
  93. RotatePoint( &m_Start, aRotCentre, aAngle);
  94. RotatePoint( &m_End, aRotCentre, aAngle);
  95. break;
  96. case S_POLYGON:
  97. for( auto iter = m_Poly.Iterate(); iter; iter++ )
  98. {
  99. RotatePoint( *iter, VECTOR2I(aRotCentre), aAngle);
  100. }
  101. break;
  102. case S_CURVE:
  103. RotatePoint( &m_Start, aRotCentre, aAngle);
  104. RotatePoint( &m_End, aRotCentre, aAngle);
  105. for( unsigned int ii = 0; ii < m_BezierPoints.size(); ii++ )
  106. {
  107. RotatePoint( &m_BezierPoints[ii], aRotCentre, aAngle);
  108. }
  109. break;
  110. case S_RECT:
  111. default:
  112. // un-handled edge transform
  113. wxASSERT_MSG( false, wxT( "DRAWSEGMENT::Rotate not implemented for "
  114. + ShowShape( m_Shape ) ) );
  115. break;
  116. }
  117. }
  118. void DRAWSEGMENT::Flip( const wxPoint& aCentre )
  119. {
  120. m_Start.y = aCentre.y - (m_Start.y - aCentre.y);
  121. m_End.y = aCentre.y - (m_End.y - aCentre.y);
  122. switch ( m_Shape )
  123. {
  124. case S_ARC:
  125. m_Angle = -m_Angle;
  126. break;
  127. case S_POLYGON:
  128. for( auto iter = m_Poly.Iterate(); iter; iter++ )
  129. {
  130. iter->y = aCentre.y - (iter->y - aCentre.y);
  131. }
  132. break;
  133. default:
  134. break;
  135. }
  136. // DRAWSEGMENT items are not allowed on copper layers, so
  137. // copper layers count is not taken in accoun in Flip transform
  138. SetLayer( FlipLayer( GetLayer() ) );
  139. }
  140. const wxPoint DRAWSEGMENT::GetCenter() const
  141. {
  142. wxPoint c;
  143. switch( m_Shape )
  144. {
  145. case S_ARC:
  146. case S_CIRCLE:
  147. c = m_Start;
  148. break;
  149. case S_SEGMENT:
  150. // Midpoint of the line
  151. c = ( GetStart() + GetEnd() ) / 2;
  152. break;
  153. case S_POLYGON:
  154. case S_RECT:
  155. case S_CURVE:
  156. c = GetBoundingBox().Centre();
  157. break;
  158. default:
  159. wxASSERT_MSG( false, "DRAWSEGMENT::GetCentre not implemented for shape"
  160. + ShowShape( GetShape() ) );
  161. break;
  162. }
  163. return c;
  164. }
  165. const wxPoint DRAWSEGMENT::GetArcEnd() const
  166. {
  167. wxPoint endPoint; // start of arc
  168. switch( m_Shape )
  169. {
  170. case S_ARC:
  171. // rotate the starting point of the arc, given by m_End, through the
  172. // angle m_Angle to get the ending point of the arc.
  173. // m_Start is the arc centre
  174. endPoint = m_End; // m_End = start point of arc
  175. RotatePoint( &endPoint, m_Start, -m_Angle );
  176. break;
  177. default:
  178. break;
  179. }
  180. return endPoint; // after rotation, the end of the arc.
  181. }
  182. double DRAWSEGMENT::GetArcAngleStart() const
  183. {
  184. // due to the Y axis orient atan2 needs - y value
  185. double angleStart = ArcTangente( GetArcStart().y - GetCenter().y,
  186. GetArcStart().x - GetCenter().x );
  187. // Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg
  188. // because 180 deg and -180 are very near angles when ampping betewwen -180 ... 180 deg.
  189. // and this is not easy to handle in calculations
  190. NORMALIZE_ANGLE_POS( angleStart );
  191. return angleStart;
  192. }
  193. void DRAWSEGMENT::SetAngle( double aAngle )
  194. {
  195. // m_Angle must be >= -360 and <= +360 degrees
  196. m_Angle = NormalizeAngle360Max( aAngle );
  197. }
  198. MODULE* DRAWSEGMENT::GetParentModule() const
  199. {
  200. if( !m_Parent || m_Parent->Type() != PCB_MODULE_T )
  201. return NULL;
  202. return (MODULE*) m_Parent;
  203. }
  204. void DRAWSEGMENT::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode,
  205. const wxPoint& aOffset )
  206. {
  207. int ux0, uy0, dx, dy;
  208. int l_trace;
  209. int radius;
  210. PCB_LAYER_ID curr_layer = ( (PCB_SCREEN*) panel->GetScreen() )->m_Active_Layer;
  211. BOARD * brd = GetBoard( );
  212. if( brd->IsLayerVisible( GetLayer() ) == false )
  213. return;
  214. auto frame = static_cast<PCB_EDIT_FRAME*> ( panel->GetParent() );
  215. auto color = frame->Settings().Colors().GetLayerColor( GetLayer() );
  216. auto displ_opts = (PCB_DISPLAY_OPTIONS*) panel->GetDisplayOptions();
  217. if( ( draw_mode & GR_ALLOW_HIGHCONTRAST ) && displ_opts && displ_opts->m_ContrastModeDisplay )
  218. {
  219. if( !IsOnLayer( curr_layer ) && !IsOnLayer( Edge_Cuts ) )
  220. color = COLOR4D( DARKDARKGRAY );
  221. }
  222. GRSetDrawMode( DC, draw_mode );
  223. l_trace = m_Width >> 1; // half trace width
  224. // Line start point or Circle and Arc center
  225. ux0 = m_Start.x + aOffset.x;
  226. uy0 = m_Start.y + aOffset.y;
  227. // Line end point or circle and arc start point
  228. dx = m_End.x + aOffset.x;
  229. dy = m_End.y + aOffset.y;
  230. bool filled = displ_opts ? displ_opts->m_DisplayDrawItemsFill : FILLED;
  231. if( m_Flags & FORCE_SKETCH )
  232. filled = SKETCH;
  233. switch( m_Shape )
  234. {
  235. case S_CIRCLE:
  236. radius = KiROUND( Distance( ux0, uy0, dx, dy ) );
  237. if( filled )
  238. {
  239. GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius, m_Width, color );
  240. }
  241. else
  242. {
  243. GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius - l_trace, color );
  244. GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius + l_trace, color );
  245. }
  246. break;
  247. case S_ARC:
  248. double StAngle, EndAngle;
  249. radius = KiROUND( Distance( ux0, uy0, dx, dy ) );
  250. StAngle = ArcTangente( dy - uy0, dx - ux0 );
  251. EndAngle = StAngle + m_Angle;
  252. if( !panel->GetPrintMirrored() )
  253. {
  254. if( StAngle > EndAngle )
  255. std::swap( StAngle, EndAngle );
  256. }
  257. else // Mirrored mode: arc orientation is reversed
  258. {
  259. #ifdef __WXMAC__ // wxWidgets OSX print driver handles arc mirroring for us
  260. if( StAngle > EndAngle )
  261. std::swap( StAngle, EndAngle );
  262. #else
  263. if( StAngle < EndAngle )
  264. std::swap( StAngle, EndAngle );
  265. #endif
  266. }
  267. if( filled )
  268. {
  269. GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle,
  270. radius, m_Width, color );
  271. }
  272. else
  273. {
  274. GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle,
  275. radius - l_trace, color );
  276. GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle,
  277. radius + l_trace, color );
  278. }
  279. break;
  280. case S_CURVE:
  281. {
  282. std::vector<wxPoint> ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End };
  283. BEZIER_POLY converter( ctrlPoints );
  284. converter.GetPoly( m_BezierPoints );
  285. }
  286. for( unsigned int i=1; i < m_BezierPoints.size(); i++ )
  287. {
  288. if( filled )
  289. {
  290. GRFillCSegm( panel->GetClipBox(), DC,
  291. m_BezierPoints[i].x, m_BezierPoints[i].y,
  292. m_BezierPoints[i-1].x, m_BezierPoints[i-1].y,
  293. m_Width, color );
  294. }
  295. else
  296. {
  297. GRCSegm( panel->GetClipBox(), DC,
  298. m_BezierPoints[i].x, m_BezierPoints[i].y,
  299. m_BezierPoints[i-1].x, m_BezierPoints[i-1].y,
  300. m_Width, color );
  301. }
  302. }
  303. break;
  304. case S_POLYGON:
  305. {
  306. SHAPE_POLY_SET& outline = GetPolyShape();
  307. // Draw the polygon: only one polygon is expected
  308. // However we provide a multi polygon shape drawing
  309. // ( for the future or to show a non expected shape )
  310. for( int jj = 0; jj < outline.OutlineCount(); ++jj )
  311. {
  312. SHAPE_LINE_CHAIN& poly = outline.Outline( jj );
  313. GRClosedPoly( panel->GetClipBox(), DC, poly.PointCount(),
  314. (wxPoint*)&poly.Point( 0 ), FILLED, GetWidth(),
  315. color, color );
  316. }
  317. }
  318. break;
  319. default:
  320. if( filled )
  321. {
  322. GRFillCSegm( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color );
  323. }
  324. else
  325. {
  326. GRCSegm( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color );
  327. }
  328. break;
  329. }
  330. }
  331. void DRAWSEGMENT::GetMsgPanelInfo( EDA_UNITS_T aUnits, std::vector< MSG_PANEL_ITEM >& aList )
  332. {
  333. wxString msg;
  334. msg = _( "Drawing" );
  335. aList.push_back( MSG_PANEL_ITEM( _( "Type" ), msg, DARKCYAN ) );
  336. wxString shape = _( "Shape" );
  337. switch( m_Shape )
  338. {
  339. case S_CIRCLE:
  340. aList.push_back( MSG_PANEL_ITEM( shape, _( "Circle" ), RED ) );
  341. msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
  342. aList.push_back( MSG_PANEL_ITEM( _( "Radius" ), msg, RED ) );
  343. break;
  344. case S_ARC:
  345. aList.push_back( MSG_PANEL_ITEM( shape, _( "Arc" ), RED ) );
  346. msg.Printf( wxT( "%.1f" ), m_Angle / 10.0 );
  347. aList.push_back( MSG_PANEL_ITEM( _( "Angle" ), msg, RED ) );
  348. msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
  349. aList.push_back( MSG_PANEL_ITEM( _( "Radius" ), msg, RED ) );
  350. break;
  351. case S_CURVE:
  352. aList.push_back( MSG_PANEL_ITEM( shape, _( "Curve" ), RED ) );
  353. break;
  354. default:
  355. {
  356. aList.push_back( MSG_PANEL_ITEM( shape, _( "Segment" ), RED ) );
  357. msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
  358. aList.push_back( MSG_PANEL_ITEM( _( "Length" ), msg, DARKGREEN ) );
  359. // angle counter-clockwise from 3'o-clock
  360. const double deg = RAD2DEG( atan2( (double)( m_Start.y - m_End.y ),
  361. (double)( m_End.x - m_Start.x ) ) );
  362. msg.Printf( wxT( "%.1f" ), deg );
  363. aList.push_back( MSG_PANEL_ITEM( _( "Angle" ), msg, DARKGREEN ) );
  364. }
  365. }
  366. wxString start = wxString::Format( _( "@(%s, %s)" ),
  367. MessageTextFromValue( aUnits, GetStart().x ),
  368. MessageTextFromValue( aUnits, GetStart().y ) );
  369. wxString end = wxString::Format( _( "@(%s, %s)" ),
  370. MessageTextFromValue( aUnits, GetEnd().x ),
  371. MessageTextFromValue( aUnits, GetEnd().y ) );
  372. aList.push_back( MSG_PANEL_ITEM( start, end, DARKGREEN ) );
  373. aList.push_back( MSG_PANEL_ITEM( _( "Layer" ), GetLayerName(), DARKBROWN ) );
  374. msg = MessageTextFromValue( aUnits, m_Width, true );
  375. aList.push_back( MSG_PANEL_ITEM( _( "Width" ), msg, DARKCYAN ) );
  376. }
  377. const EDA_RECT DRAWSEGMENT::GetBoundingBox() const
  378. {
  379. EDA_RECT bbox;
  380. bbox.SetOrigin( m_Start );
  381. switch( m_Shape )
  382. {
  383. case S_SEGMENT:
  384. bbox.SetEnd( m_End );
  385. break;
  386. case S_CIRCLE:
  387. bbox.Inflate( GetRadius() );
  388. break;
  389. case S_ARC:
  390. computeArcBBox( bbox );
  391. break;
  392. case S_POLYGON:
  393. if( m_Poly.IsEmpty() )
  394. break;
  395. {
  396. wxPoint p_end;
  397. MODULE* module = GetParentModule();
  398. bool first = true;
  399. for( auto iter = m_Poly.CIterate(); iter; iter++ )
  400. {
  401. wxPoint pt ( iter->x, iter->y );
  402. if( module ) // Transform, if we belong to a module
  403. {
  404. RotatePoint( &pt, module->GetOrientation() );
  405. pt += module->GetPosition();
  406. }
  407. if( first )
  408. {
  409. p_end = pt;
  410. bbox.SetX( pt.x );
  411. bbox.SetY( pt.y );
  412. first = false;
  413. }
  414. else
  415. {
  416. bbox.SetX( std::min( bbox.GetX(), pt.x ) );
  417. bbox.SetY( std::min( bbox.GetY(), pt.y ) );
  418. p_end.x = std::max( p_end.x, pt.x );
  419. p_end.y = std::max( p_end.y, pt.y );
  420. }
  421. }
  422. bbox.SetEnd( p_end );
  423. break;
  424. }
  425. default:
  426. break;
  427. }
  428. bbox.Inflate( ((m_Width+1) / 2) + 1 );
  429. bbox.Normalize();
  430. return bbox;
  431. }
  432. bool DRAWSEGMENT::HitTest( const wxPoint& aPosition ) const
  433. {
  434. switch( m_Shape )
  435. {
  436. case S_CIRCLE:
  437. case S_ARC:
  438. {
  439. wxPoint relPos = aPosition - GetCenter();
  440. int radius = GetRadius();
  441. int dist = KiROUND( EuclideanNorm( relPos ) );
  442. if( abs( radius - dist ) <= ( m_Width / 2 ) )
  443. {
  444. if( m_Shape == S_CIRCLE )
  445. return true;
  446. // For arcs, the test point angle must be >= arc angle start
  447. // and <= arc angle end
  448. // However angle values > 360 deg are not easy to handle
  449. // so we calculate the relative angle between arc start point and teast point
  450. // this relative arc should be < arc angle if arc angle > 0 (CW arc)
  451. // and > arc angle if arc angle < 0 (CCW arc)
  452. double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg
  453. double arc_hittest = ArcTangente( relPos.y, relPos.x );
  454. // Calculate relative angle between the starting point of the arc, and the test point
  455. arc_hittest -= arc_angle_start;
  456. // Normalise arc_hittest between 0 ... 360 deg
  457. NORMALIZE_ANGLE_POS( arc_hittest );
  458. // Check angle: inside the arc angle when it is > 0
  459. // and outside the not drawn arc when it is < 0
  460. if( GetAngle() >= 0.0 )
  461. {
  462. if( arc_hittest <= GetAngle() )
  463. return true;
  464. }
  465. else
  466. {
  467. if( arc_hittest >= (3600.0 + GetAngle()) )
  468. return true;
  469. }
  470. }
  471. }
  472. break;
  473. case S_CURVE:
  474. for( unsigned int i= 1; i < m_BezierPoints.size(); i++)
  475. {
  476. if( TestSegmentHit( aPosition, m_BezierPoints[i-1], m_BezierPoints[i-1], m_Width / 2 ) )
  477. return true;
  478. }
  479. break;
  480. case S_SEGMENT:
  481. if( TestSegmentHit( aPosition, m_Start, m_End, m_Width / 2 ) )
  482. return true;
  483. break;
  484. case S_POLYGON: // not yet handled
  485. {
  486. #define MAX_DIST_IN_MM 0.25
  487. int distmax = Millimeter2iu( 0.25 );
  488. SHAPE_POLY_SET::VERTEX_INDEX dummy;
  489. auto poly = m_Poly;
  490. if( poly.CollideVertex( VECTOR2I( aPosition ), dummy, distmax ) )
  491. return true;
  492. if( poly.CollideEdge( VECTOR2I( aPosition ), dummy, distmax ) )
  493. return true;
  494. }
  495. break;
  496. default:
  497. wxASSERT_MSG( 0, wxString::Format( "unknown DRAWSEGMENT shape: %d", m_Shape ) );
  498. break;
  499. }
  500. return false;
  501. }
  502. bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
  503. {
  504. EDA_RECT arect = aRect;
  505. arect.Normalize();
  506. arect.Inflate( aAccuracy );
  507. EDA_RECT arcRect;
  508. EDA_RECT bb = GetBoundingBox();
  509. switch( m_Shape )
  510. {
  511. case S_CIRCLE:
  512. // Test if area intersects or contains the circle:
  513. if( aContained )
  514. return arect.Contains( bb );
  515. else
  516. {
  517. // If the rectangle does not intersect the bounding box, this is a much quicker test
  518. if( !aRect.Intersects( bb ) )
  519. {
  520. return false;
  521. }
  522. else
  523. {
  524. return arect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() );
  525. }
  526. }
  527. break;
  528. case S_ARC:
  529. // Test for full containment of this arc in the rect
  530. if( aContained )
  531. {
  532. return arect.Contains( bb );
  533. }
  534. // Test if the rect crosses the arc
  535. else
  536. {
  537. arcRect = bb.Common( arect );
  538. /* All following tests must pass:
  539. * 1. Rectangle must intersect arc BoundingBox
  540. * 2. Rectangle must cross the outside of the arc
  541. */
  542. return arcRect.Intersects( arect ) &&
  543. arcRect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() );
  544. }
  545. break;
  546. case S_SEGMENT:
  547. if( aContained )
  548. {
  549. return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
  550. }
  551. else
  552. {
  553. // Account for the width of the line
  554. arect.Inflate( GetWidth() / 2 );
  555. return arect.Intersects( GetStart(), GetEnd() );
  556. }
  557. break;
  558. case S_POLYGON:
  559. if( aContained )
  560. {
  561. return arect.Contains( bb );
  562. }
  563. else
  564. {
  565. // Fast test: if aRect is outside the polygon bounding box,
  566. // rectangles cannot intersect
  567. if( !arect.Intersects( bb ) )
  568. return false;
  569. // Account for the width of the line
  570. arect.Inflate( GetWidth() / 2 );
  571. int count = m_Poly.TotalVertices();
  572. for( int ii = 0; ii < count; ii++ )
  573. {
  574. auto vertex = m_Poly.CVertex( ii );
  575. auto vertexNext = m_Poly.CVertex( ( ii + 1 ) % count );
  576. // Test if the point is within aRect
  577. if( arect.Contains( ( wxPoint ) vertex ) )
  578. return true;
  579. // Test if this edge intersects aRect
  580. if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
  581. return true;
  582. }
  583. }
  584. break;
  585. case S_CURVE: // not yet handled
  586. break;
  587. default:
  588. wxASSERT_MSG( 0, wxString::Format( "unknown DRAWSEGMENT shape: %d", m_Shape ) );
  589. break;
  590. }
  591. return false;
  592. }
  593. wxString DRAWSEGMENT::GetSelectMenuText( EDA_UNITS_T aUnits ) const
  594. {
  595. return wxString::Format( _( "Pcb Graphic %s, length %s on %s" ),
  596. ShowShape( m_Shape ),
  597. MessageTextFromValue( aUnits, GetLength() ),
  598. GetLayerName() );
  599. }
  600. BITMAP_DEF DRAWSEGMENT::GetMenuImage() const
  601. {
  602. return add_dashed_line_xpm;
  603. }
  604. EDA_ITEM* DRAWSEGMENT::Clone() const
  605. {
  606. return new DRAWSEGMENT( *this );
  607. }
  608. const BOX2I DRAWSEGMENT::ViewBBox() const
  609. {
  610. // For arcs - do not include the center point in the bounding box,
  611. // it is redundant for displaying an arc
  612. if( m_Shape == S_ARC )
  613. {
  614. EDA_RECT bbox;
  615. bbox.SetOrigin( m_End );
  616. computeArcBBox( bbox );
  617. return BOX2I( bbox.GetOrigin(), bbox.GetSize() );
  618. }
  619. return EDA_ITEM::ViewBBox();
  620. }
  621. void DRAWSEGMENT::computeArcBBox( EDA_RECT& aBBox ) const
  622. {
  623. // Do not include the center, which is not necessarily
  624. // inside the BB of a arc with a small angle
  625. aBBox.SetOrigin( m_End );
  626. wxPoint end = m_End;
  627. RotatePoint( &end, m_Start, -m_Angle );
  628. aBBox.Merge( end );
  629. // Determine the starting quarter
  630. // 0 right-bottom
  631. // 1 left-bottom
  632. // 2 left-top
  633. // 3 right-top
  634. unsigned int quarter = 0; // assume right-bottom
  635. if( m_End.x < m_Start.x )
  636. {
  637. if( m_End.y <= m_Start.y )
  638. quarter = 2;
  639. else // ( m_End.y > m_Start.y )
  640. quarter = 1;
  641. }
  642. else if( m_End.x >= m_Start.x )
  643. {
  644. if( m_End.y < m_Start.y )
  645. quarter = 3;
  646. else if( m_End.x == m_Start.x )
  647. quarter = 1;
  648. }
  649. int radius = GetRadius();
  650. int angle = (int) GetArcAngleStart() % 900 + m_Angle;
  651. bool directionCW = ( m_Angle > 0 ); // Is the direction of arc clockwise?
  652. // Make the angle positive, so we go clockwise and merge points belonging to the arc
  653. if( !directionCW )
  654. {
  655. angle = 900 - angle;
  656. quarter = ( quarter + 3 ) % 4; // -1 modulo arithmetic
  657. }
  658. while( angle > 900 )
  659. {
  660. switch( quarter )
  661. {
  662. case 0:
  663. aBBox.Merge( wxPoint( m_Start.x, m_Start.y + radius ) ); // down
  664. break;
  665. case 1:
  666. aBBox.Merge( wxPoint( m_Start.x - radius, m_Start.y ) ); // left
  667. break;
  668. case 2:
  669. aBBox.Merge( wxPoint( m_Start.x, m_Start.y - radius ) ); // up
  670. break;
  671. case 3:
  672. aBBox.Merge( wxPoint( m_Start.x + radius, m_Start.y ) ); // right
  673. break;
  674. }
  675. if( directionCW )
  676. ++quarter;
  677. else
  678. quarter += 3; // -1 modulo arithmetic
  679. quarter %= 4;
  680. angle -= 900;
  681. }
  682. }
  683. void DRAWSEGMENT::SetPolyPoints( const std::vector<wxPoint>& aPoints )
  684. {
  685. m_Poly.RemoveAllContours();
  686. m_Poly.NewOutline();
  687. for ( auto p : aPoints )
  688. {
  689. m_Poly.Append( p.x, p.y );
  690. }
  691. }
  692. const std::vector<wxPoint> DRAWSEGMENT::BuildPolyPointsList() const
  693. {
  694. std::vector<wxPoint> rv;
  695. if( m_Poly.OutlineCount() )
  696. {
  697. if( m_Poly.COutline( 0 ).PointCount() )
  698. {
  699. for ( auto iter = m_Poly.CIterate(); iter; iter++ )
  700. {
  701. rv.push_back( wxPoint( iter->x, iter->y ) );
  702. }
  703. }
  704. }
  705. return rv;
  706. }
  707. bool DRAWSEGMENT::IsPolyShapeValid() const
  708. {
  709. // return true if the polygonal shape is valid (has more than 2 points)
  710. if( GetPolyShape().OutlineCount() == 0 )
  711. return false;
  712. const SHAPE_LINE_CHAIN& outline = ((SHAPE_POLY_SET&)GetPolyShape()).Outline( 0 );
  713. return outline.PointCount() > 2;
  714. }
  715. int DRAWSEGMENT::GetPointCount() const
  716. {
  717. // return the number of corners of the polygonal shape
  718. // this shape is expected to be only one polygon without hole
  719. if( GetPolyShape().OutlineCount() )
  720. return GetPolyShape().VertexCount( 0 );
  721. return 0;
  722. }
  723. void DRAWSEGMENT::SwapData( BOARD_ITEM* aImage )
  724. {
  725. assert( aImage->Type() == PCB_LINE_T );
  726. std::swap( *((DRAWSEGMENT*) this), *((DRAWSEGMENT*) aImage) );
  727. }