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.

1264 lines
34 KiB

14 years ago
14 years ago
14 years ago
14 years ago
14 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@verizon.net>
  7. * Copyright (C) 1992-2019 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 <fctsys.h>
  27. #include <gr_basic.h>
  28. #include <bezier_curves.h>
  29. #include <pcb_screen.h>
  30. #include <bitmaps.h>
  31. #include <pcb_edit_frame.h>
  32. #include <class_board.h>
  33. #include <class_module.h>
  34. #include <class_drawsegment.h>
  35. #include <base_units.h>
  36. #include <geometry/shape_simple.h>
  37. #include <geometry/shape_segment.h>
  38. #include <geometry/shape_circle.h>
  39. #include <geometry/shape_compound.h>
  40. #include <settings/color_settings.h>
  41. #include <settings/settings_manager.h>
  42. DRAWSEGMENT::DRAWSEGMENT( BOARD_ITEM* aParent, KICAD_T idtype ) :
  43. BOARD_ITEM( aParent, idtype )
  44. {
  45. m_Type = 0;
  46. m_Angle = 0;
  47. m_Flags = 0;
  48. m_Shape = S_SEGMENT;
  49. m_Width = Millimeter2iu( DEFAULT_LINE_WIDTH );
  50. }
  51. DRAWSEGMENT::~DRAWSEGMENT()
  52. {
  53. }
  54. void DRAWSEGMENT::SetPosition( const wxPoint& aPos )
  55. {
  56. m_Start = aPos;
  57. }
  58. wxPoint DRAWSEGMENT::GetPosition() const
  59. {
  60. if( m_Shape == S_POLYGON )
  61. return (wxPoint) m_Poly.CVertex( 0 );
  62. else
  63. return m_Start;
  64. }
  65. double DRAWSEGMENT::GetLength() const
  66. {
  67. double length = 0.0;
  68. switch( m_Shape )
  69. {
  70. case S_CURVE:
  71. for( size_t ii = 1; ii < m_BezierPoints.size(); ++ii )
  72. length += GetLineLength( m_BezierPoints[ii - 1], m_BezierPoints[ii] );
  73. break;
  74. case S_SEGMENT:
  75. length = GetLineLength( GetStart(), GetEnd() );
  76. break;
  77. default:
  78. wxASSERT_MSG( false, "DRAWSEGMENT::GetLength not implemented for shape"
  79. + ShowShape( GetShape() ) );
  80. break;
  81. }
  82. return length;
  83. }
  84. void DRAWSEGMENT::Move( const wxPoint& aMoveVector )
  85. {
  86. // Move vector should not affect start/end for polygon since it will
  87. // be applied directly to polygon outline.
  88. if( m_Shape != S_POLYGON )
  89. {
  90. m_Start += aMoveVector;
  91. m_End += aMoveVector;
  92. }
  93. switch ( m_Shape )
  94. {
  95. case S_POLYGON:
  96. m_Poly.Move( VECTOR2I( aMoveVector ) );
  97. break;
  98. case S_ARC:
  99. m_ThirdPoint += aMoveVector;
  100. break;
  101. case S_CURVE:
  102. m_BezierC1 += aMoveVector;
  103. m_BezierC2 += aMoveVector;
  104. for( wxPoint& pt : m_BezierPoints)
  105. pt += aMoveVector;
  106. break;
  107. default:
  108. break;
  109. }
  110. }
  111. void DRAWSEGMENT::Scale( double aScale )
  112. {
  113. auto scalePt = [&]( wxPoint& pt )
  114. {
  115. pt.x = KiROUND( pt.x * aScale );
  116. pt.y = KiROUND( pt.y * aScale );
  117. };
  118. int radius = GetRadius();
  119. scalePt( m_Start );
  120. scalePt( m_End );
  121. // specific parameters:
  122. switch( m_Shape )
  123. {
  124. case S_CURVE:
  125. scalePt( m_BezierC1 );
  126. scalePt( m_BezierC2 );
  127. break;
  128. case S_CIRCLE: // ring or circle
  129. m_End.x = m_Start.x + KiROUND( radius * aScale );
  130. m_End.y = m_Start.y;
  131. break;
  132. case S_POLYGON: // polygon
  133. {
  134. std::vector<wxPoint> pts;
  135. for( const VECTOR2I& pt : m_Poly.Outline( 0 ).CPoints() )
  136. {
  137. pts.emplace_back( pt );
  138. scalePt( pts.back() );
  139. }
  140. SetPolyPoints( pts );
  141. }
  142. break;
  143. default:
  144. break;
  145. }
  146. }
  147. void DRAWSEGMENT::Rotate( const wxPoint& aRotCentre, double aAngle )
  148. {
  149. switch( m_Shape )
  150. {
  151. case S_ARC:
  152. case S_SEGMENT:
  153. case S_CIRCLE:
  154. // these can all be done by just rotating the constituent points
  155. RotatePoint( &m_Start, aRotCentre, aAngle );
  156. RotatePoint( &m_End, aRotCentre, aAngle );
  157. RotatePoint( &m_ThirdPoint, aRotCentre, aAngle );
  158. break;
  159. case S_RECT:
  160. if( KiROUND( aAngle ) % 900 == 0 )
  161. {
  162. RotatePoint( &m_Start, aRotCentre, aAngle );
  163. RotatePoint( &m_End, aRotCentre, aAngle );
  164. break;
  165. }
  166. // Convert non-cartesian-rotated rect to a diamond
  167. m_Shape = S_POLYGON;
  168. m_Poly.RemoveAllContours();
  169. m_Poly.NewOutline();
  170. m_Poly.Append( m_Start );
  171. m_Poly.Append( m_End.x, m_Start.y );
  172. m_Poly.Append( m_End );
  173. m_Poly.Append( m_Start.x, m_End.y );
  174. KI_FALLTHROUGH;
  175. case S_POLYGON:
  176. m_Poly.Rotate( -DECIDEG2RAD( aAngle ), VECTOR2I( aRotCentre ) );
  177. break;
  178. case S_CURVE:
  179. RotatePoint( &m_Start, aRotCentre, aAngle);
  180. RotatePoint( &m_End, aRotCentre, aAngle);
  181. RotatePoint( &m_BezierC1, aRotCentre, aAngle);
  182. RotatePoint( &m_BezierC2, aRotCentre, aAngle);
  183. for( wxPoint& pt : m_BezierPoints )
  184. RotatePoint( &pt, aRotCentre, aAngle);
  185. break;
  186. default:
  187. wxFAIL_MSG( "DRAWSEGMENT::Rotate not implemented for " + STROKE_T_asString( m_Shape ) );
  188. break;
  189. }
  190. }
  191. void DRAWSEGMENT::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
  192. {
  193. if( aFlipLeftRight )
  194. {
  195. m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
  196. m_End.x = aCentre.x - ( m_End.x - aCentre.x );
  197. }
  198. else
  199. {
  200. m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
  201. m_End.y = aCentre.y - ( m_End.y - aCentre.y );
  202. }
  203. switch ( m_Shape )
  204. {
  205. case S_ARC:
  206. m_Angle = -m_Angle;
  207. break;
  208. case S_POLYGON:
  209. m_Poly.Mirror( aFlipLeftRight, !aFlipLeftRight, VECTOR2I( aCentre ) );
  210. break;
  211. case S_CURVE:
  212. {
  213. if( aFlipLeftRight )
  214. {
  215. m_BezierC1.x = aCentre.x - ( m_BezierC1.x - aCentre.x );
  216. m_BezierC2.x = aCentre.x - ( m_BezierC2.x - aCentre.x );
  217. }
  218. else
  219. {
  220. m_BezierC1.y = aCentre.y - ( m_BezierC1.y - aCentre.y );
  221. m_BezierC2.y = aCentre.y - ( m_BezierC2.y - aCentre.y );
  222. }
  223. // Rebuild the poly points shape
  224. std::vector<wxPoint> ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End };
  225. BEZIER_POLY converter( ctrlPoints );
  226. converter.GetPoly( m_BezierPoints, m_Width );
  227. }
  228. break;
  229. case S_SEGMENT:
  230. case S_RECT:
  231. case S_CIRCLE:
  232. break;
  233. default:
  234. wxFAIL_MSG( "DRAWSEGMENT::Flip not implemented for " + STROKE_T_asString( m_Shape ) );
  235. break;
  236. }
  237. // DRAWSEGMENT items are not allowed on copper layers, so
  238. // copper layers count is not taken in account in Flip transform
  239. SetLayer( FlipLayer( GetLayer() ) );
  240. }
  241. void DRAWSEGMENT::RebuildBezierToSegmentsPointsList( int aMinSegLen )
  242. {
  243. // Has meaning only for S_CURVE DRAW_SEGMENT shape
  244. if( m_Shape != S_CURVE )
  245. {
  246. m_BezierPoints.clear();
  247. return;
  248. }
  249. // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
  250. m_BezierPoints = buildBezierToSegmentsPointsList( aMinSegLen );
  251. }
  252. const std::vector<wxPoint> DRAWSEGMENT::buildBezierToSegmentsPointsList( int aMinSegLen ) const
  253. {
  254. std::vector<wxPoint> bezierPoints;
  255. // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
  256. std::vector<wxPoint> ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End };
  257. BEZIER_POLY converter( ctrlPoints );
  258. converter.GetPoly( bezierPoints, aMinSegLen );
  259. return bezierPoints;
  260. }
  261. wxPoint DRAWSEGMENT::GetCenter() const
  262. {
  263. wxPoint c;
  264. switch( m_Shape )
  265. {
  266. case S_ARC:
  267. case S_CIRCLE:
  268. c = m_Start;
  269. break;
  270. case S_SEGMENT:
  271. // Midpoint of the line
  272. c = ( GetStart() + GetEnd() ) / 2;
  273. break;
  274. case S_POLYGON:
  275. case S_RECT:
  276. case S_CURVE:
  277. c = GetBoundingBox().Centre();
  278. break;
  279. default:
  280. wxFAIL_MSG( "DRAWSEGMENT::GetCentre not implemented for " + STROKE_T_asString( m_Shape ) );
  281. break;
  282. }
  283. return c;
  284. }
  285. wxPoint DRAWSEGMENT::GetArcEnd() const
  286. {
  287. wxPoint endPoint( m_End ); // start of arc
  288. switch( m_Shape )
  289. {
  290. case S_ARC:
  291. endPoint = m_ThirdPoint;
  292. break;
  293. default:
  294. break;
  295. }
  296. return endPoint; // after rotation, the end of the arc.
  297. }
  298. wxPoint DRAWSEGMENT::GetArcMid() const
  299. {
  300. wxPoint endPoint( m_End );
  301. switch( m_Shape )
  302. {
  303. case S_ARC:
  304. // rotate the starting point of the arc, given by m_End, through half
  305. // the angle m_Angle to get the middle of the arc.
  306. // m_Start is the arc centre
  307. endPoint = m_End; // m_End = start point of arc
  308. RotatePoint( &endPoint, m_Start, -m_Angle / 2.0 );
  309. break;
  310. default:
  311. break;
  312. }
  313. return endPoint; // after rotation, the end of the arc.
  314. }
  315. double DRAWSEGMENT::GetArcAngleStart() const
  316. {
  317. // due to the Y axis orient atan2 needs - y value
  318. double angleStart = ArcTangente( GetArcStart().y - GetCenter().y,
  319. GetArcStart().x - GetCenter().x );
  320. // Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg
  321. // because 180 deg and -180 are very near angles when ampping betewwen -180 ... 180 deg.
  322. // and this is not easy to handle in calculations
  323. NORMALIZE_ANGLE_POS( angleStart );
  324. return angleStart;
  325. }
  326. double DRAWSEGMENT::GetArcAngleEnd() const
  327. {
  328. // due to the Y axis orient atan2 needs - y value
  329. double angleStart = ArcTangente( GetArcEnd().y - GetCenter().y,
  330. GetArcEnd().x - GetCenter().x );
  331. // Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg
  332. // because 180 deg and -180 are very near angles when ampping betewwen -180 ... 180 deg.
  333. // and this is not easy to handle in calculations
  334. NORMALIZE_ANGLE_POS( angleStart );
  335. return angleStart;
  336. }
  337. void DRAWSEGMENT::SetAngle( double aAngle )
  338. {
  339. // Mark as depreciated.
  340. // m_Angle does not define the arc anymore
  341. // m_Angle must be >= -360 and <= +360 degrees
  342. m_Angle = NormalizeAngle360Max( aAngle );
  343. m_ThirdPoint = m_End;
  344. RotatePoint( &m_ThirdPoint, m_Start, -m_Angle );
  345. }
  346. MODULE* DRAWSEGMENT::GetParentModule() const
  347. {
  348. if( !m_Parent || m_Parent->Type() != PCB_MODULE_T )
  349. return NULL;
  350. return (MODULE*) m_Parent;
  351. }
  352. void DRAWSEGMENT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  353. {
  354. EDA_UNITS units = aFrame->GetUserUnits();
  355. wxString msg;
  356. msg = _( "Drawing" );
  357. aList.emplace_back( _( "Type" ), msg, DARKCYAN );
  358. wxString shape = _( "Shape" );
  359. switch( m_Shape )
  360. {
  361. case S_CIRCLE:
  362. aList.emplace_back( shape, _( "Circle" ), RED );
  363. msg = MessageTextFromValue( units, GetLineLength( m_Start, m_End ) );
  364. aList.emplace_back( _( "Radius" ), msg, RED );
  365. break;
  366. case S_ARC:
  367. aList.emplace_back( shape, _( "Arc" ), RED );
  368. msg.Printf( wxT( "%.1f" ), m_Angle / 10.0 );
  369. aList.emplace_back( _( "Angle" ), msg, RED );
  370. msg = MessageTextFromValue( units, GetLineLength( m_Start, m_End ) );
  371. aList.emplace_back( _( "Radius" ), msg, RED );
  372. break;
  373. case S_CURVE:
  374. aList.emplace_back( shape, _( "Curve" ), RED );
  375. msg = MessageTextFromValue( units, GetLength() );
  376. aList.emplace_back( _( "Length" ), msg, DARKGREEN );
  377. break;
  378. case S_POLYGON:
  379. aList.emplace_back( shape, _( "Polygon" ), RED );
  380. msg.Printf( "%d", GetPolyShape().Outline(0).PointCount() );
  381. aList.emplace_back( _( "Points" ), msg, DARKGREEN );
  382. break;
  383. case S_RECT:
  384. aList.emplace_back( shape, _( "Rectangle" ), RED );
  385. msg = MessageTextFromValue( units, std::abs( m_End.x - m_Start.x ) );
  386. aList.emplace_back( _( "Width" ), msg, DARKGREEN );
  387. msg = MessageTextFromValue( units, std::abs( m_End.y - m_Start.y ) );
  388. aList.emplace_back( _( "Height" ), msg, DARKGREEN );
  389. break;
  390. case S_SEGMENT:
  391. {
  392. aList.emplace_back( shape, _( "Segment" ), RED );
  393. msg = MessageTextFromValue( units, GetLineLength( m_Start, m_End ) );
  394. aList.emplace_back( _( "Length" ), msg, DARKGREEN );
  395. // angle counter-clockwise from 3'o-clock
  396. const double deg = RAD2DEG( atan2( (double)( m_Start.y - m_End.y ),
  397. (double)( m_End.x - m_Start.x ) ) );
  398. msg.Printf( wxT( "%.1f" ), deg );
  399. aList.emplace_back( _( "Angle" ), msg, DARKGREEN );
  400. }
  401. break;
  402. default:
  403. aList.emplace_back( shape, _( "Unrecognized" ), RED );
  404. break;
  405. }
  406. if( m_Shape == S_POLYGON )
  407. {
  408. VECTOR2I point0 = GetPolyShape().Outline(0).CPoint(0);
  409. wxString origin = wxString::Format( "@(%s, %s)",
  410. MessageTextFromValue( units, point0.x ),
  411. MessageTextFromValue( units, point0.y ) );
  412. aList.emplace_back( _( "Origin" ), origin, DARKGREEN );
  413. }
  414. else
  415. {
  416. wxString start = wxString::Format( "@(%s, %s)",
  417. MessageTextFromValue( units, GetStart().x ),
  418. MessageTextFromValue( units, GetStart().y ) );
  419. wxString end = wxString::Format( "@(%s, %s)",
  420. MessageTextFromValue( units, GetEnd().x ),
  421. MessageTextFromValue( units, GetEnd().y ) );
  422. aList.emplace_back( start, end, DARKGREEN );
  423. }
  424. aList.emplace_back( _( "Layer" ), GetLayerName(), DARKBROWN );
  425. msg = MessageTextFromValue( units, m_Width, true );
  426. aList.emplace_back( _( "Width" ), msg, DARKCYAN );
  427. }
  428. const EDA_RECT DRAWSEGMENT::GetBoundingBox() const
  429. {
  430. EDA_RECT bbox;
  431. bbox.SetOrigin( m_Start );
  432. switch( m_Shape )
  433. {
  434. case S_RECT:
  435. {
  436. std::vector<wxPoint> pts;
  437. GetRectCorners( &pts );
  438. bbox = EDA_RECT(); // re-init for merging
  439. for( wxPoint& pt : pts )
  440. bbox.Merge( pt );
  441. }
  442. break;
  443. case S_SEGMENT:
  444. bbox.SetEnd( m_End );
  445. break;
  446. case S_CIRCLE:
  447. bbox.Inflate( GetRadius() );
  448. break;
  449. case S_ARC:
  450. computeArcBBox( bbox );
  451. break;
  452. case S_POLYGON:
  453. if( m_Poly.IsEmpty() )
  454. break;
  455. {
  456. MODULE* module = GetParentModule();
  457. bbox = EDA_RECT(); // re-init for merging
  458. for( auto iter = m_Poly.CIterate(); iter; iter++ )
  459. {
  460. wxPoint pt ( iter->x, iter->y );
  461. if( module ) // Transform, if we belong to a module
  462. {
  463. RotatePoint( &pt, module->GetOrientation() );
  464. pt += module->GetPosition();
  465. }
  466. bbox.Merge( pt );
  467. }
  468. }
  469. break;
  470. case S_CURVE:
  471. bbox.Merge( m_BezierC1 );
  472. bbox.Merge( m_BezierC2 );
  473. bbox.Merge( m_End );
  474. break;
  475. default:
  476. wxFAIL_MSG( "DRAWSEGMENT::GetBoundingBox not implemented for "
  477. + STROKE_T_asString( m_Shape ) );
  478. break;
  479. }
  480. bbox.Inflate( ((m_Width+1) / 2) + 1 );
  481. bbox.Normalize();
  482. return bbox;
  483. }
  484. bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const
  485. {
  486. int maxdist = aAccuracy + ( m_Width / 2 );
  487. switch( m_Shape )
  488. {
  489. case S_CIRCLE:
  490. {
  491. int radius = GetRadius();
  492. int dist = KiROUND( EuclideanNorm( aPosition - GetCenter() ) );
  493. if( m_Width == 0 ) // Filled circle hit-test
  494. {
  495. if( dist <= radius + maxdist )
  496. return true;
  497. }
  498. else // Ring hit-test
  499. {
  500. if( abs( radius - dist ) <= maxdist )
  501. return true;
  502. }
  503. }
  504. break;
  505. case S_ARC:
  506. {
  507. wxPoint relPos = aPosition - GetCenter();
  508. int radius = GetRadius();
  509. int dist = KiROUND( EuclideanNorm( relPos ) );
  510. if( abs( radius - dist ) <= maxdist )
  511. {
  512. // For arcs, the test point angle must be >= arc angle start
  513. // and <= arc angle end
  514. // However angle values > 360 deg are not easy to handle
  515. // so we calculate the relative angle between arc start point and teast point
  516. // this relative arc should be < arc angle if arc angle > 0 (CW arc)
  517. // and > arc angle if arc angle < 0 (CCW arc)
  518. double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg
  519. double arc_hittest = ArcTangente( relPos.y, relPos.x );
  520. // Calculate relative angle between the starting point of the arc, and the test point
  521. arc_hittest -= arc_angle_start;
  522. // Normalise arc_hittest between 0 ... 360 deg
  523. NORMALIZE_ANGLE_POS( arc_hittest );
  524. // Check angle: inside the arc angle when it is > 0
  525. // and outside the not drawn arc when it is < 0
  526. if( GetAngle() >= 0.0 )
  527. {
  528. if( arc_hittest <= GetAngle() )
  529. return true;
  530. }
  531. else
  532. {
  533. if( arc_hittest >= (3600.0 + GetAngle()) )
  534. return true;
  535. }
  536. }
  537. }
  538. break;
  539. case S_CURVE:
  540. ((DRAWSEGMENT*)this)->RebuildBezierToSegmentsPointsList( m_Width );
  541. for( unsigned int i= 1; i < m_BezierPoints.size(); i++)
  542. {
  543. if( TestSegmentHit( aPosition, m_BezierPoints[i-1], m_BezierPoints[i], maxdist ) )
  544. return true;
  545. }
  546. break;
  547. case S_SEGMENT:
  548. if( TestSegmentHit( aPosition, m_Start, m_End, maxdist ) )
  549. return true;
  550. break;
  551. case S_RECT:
  552. {
  553. std::vector<wxPoint> pts;
  554. GetRectCorners( &pts );
  555. if( m_Width == 0 ) // Filled rect hit-test
  556. {
  557. SHAPE_POLY_SET poly;
  558. poly.NewOutline();
  559. for( const wxPoint& pt : pts )
  560. poly.Append( pt );
  561. if( poly.Collide( VECTOR2I( aPosition ), maxdist ) )
  562. return true;
  563. }
  564. else // Open rect hit-test
  565. {
  566. if( TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
  567. || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
  568. || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
  569. || TestSegmentHit( aPosition, pts[3], pts[0], maxdist ) )
  570. {
  571. return true;
  572. }
  573. }
  574. }
  575. break;
  576. case S_POLYGON:
  577. {
  578. if( !IsPolygonFilled() )
  579. {
  580. SHAPE_POLY_SET::VERTEX_INDEX dummy;
  581. return m_Poly.CollideEdge( VECTOR2I( aPosition ), dummy, maxdist );
  582. }
  583. else
  584. return m_Poly.Collide( VECTOR2I( aPosition ), maxdist );
  585. }
  586. break;
  587. default:
  588. wxFAIL_MSG( "DRAWSEGMENT::HitTest (point) not implemented for "
  589. + STROKE_T_asString( m_Shape ) );
  590. break;
  591. }
  592. return false;
  593. }
  594. bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
  595. {
  596. EDA_RECT arect = aRect;
  597. arect.Normalize();
  598. arect.Inflate( aAccuracy );
  599. EDA_RECT arcRect;
  600. EDA_RECT bb = GetBoundingBox();
  601. switch( m_Shape )
  602. {
  603. case S_CIRCLE:
  604. // Test if area intersects or contains the circle:
  605. if( aContained )
  606. return arect.Contains( bb );
  607. else
  608. {
  609. // If the rectangle does not intersect the bounding box, this is a much quicker test
  610. if( !aRect.Intersects( bb ) )
  611. {
  612. return false;
  613. }
  614. else
  615. {
  616. return arect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() );
  617. }
  618. }
  619. break;
  620. case S_ARC:
  621. // Test for full containment of this arc in the rect
  622. if( aContained )
  623. {
  624. return arect.Contains( bb );
  625. }
  626. // Test if the rect crosses the arc
  627. else
  628. {
  629. arcRect = bb.Common( arect );
  630. /* All following tests must pass:
  631. * 1. Rectangle must intersect arc BoundingBox
  632. * 2. Rectangle must cross the outside of the arc
  633. */
  634. return arcRect.Intersects( arect ) &&
  635. arcRect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() );
  636. }
  637. break;
  638. case S_RECT:
  639. if( aContained )
  640. {
  641. return arect.Contains( bb );
  642. }
  643. else
  644. {
  645. std::vector<wxPoint> pts;
  646. GetRectCorners( &pts );
  647. // Account for the width of the lines
  648. arect.Inflate( GetWidth() / 2 );
  649. return ( arect.Intersects( pts[0], pts[1] )
  650. || arect.Intersects( pts[1], pts[2] )
  651. || arect.Intersects( pts[2], pts[3] )
  652. || arect.Intersects( pts[3], pts[0] ) );
  653. }
  654. break;
  655. case S_SEGMENT:
  656. if( aContained )
  657. {
  658. return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
  659. }
  660. else
  661. {
  662. // Account for the width of the line
  663. arect.Inflate( GetWidth() / 2 );
  664. return arect.Intersects( GetStart(), GetEnd() );
  665. }
  666. break;
  667. case S_POLYGON:
  668. if( aContained )
  669. {
  670. return arect.Contains( bb );
  671. }
  672. else
  673. {
  674. // Fast test: if aRect is outside the polygon bounding box,
  675. // rectangles cannot intersect
  676. if( !arect.Intersects( bb ) )
  677. return false;
  678. // Account for the width of the line
  679. arect.Inflate( GetWidth() / 2 );
  680. int count = m_Poly.TotalVertices();
  681. for( int ii = 0; ii < count; ii++ )
  682. {
  683. auto vertex = m_Poly.CVertex( ii );
  684. auto vertexNext = m_Poly.CVertex( ( ii + 1 ) % count );
  685. // Test if the point is within aRect
  686. if( arect.Contains( ( wxPoint ) vertex ) )
  687. return true;
  688. // Test if this edge intersects aRect
  689. if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
  690. return true;
  691. }
  692. }
  693. break;
  694. case S_CURVE:
  695. if( aContained )
  696. {
  697. return arect.Contains( bb );
  698. }
  699. else
  700. {
  701. // Fast test: if aRect is outside the polygon bounding box,
  702. // rectangles cannot intersect
  703. if( !arect.Intersects( bb ) )
  704. return false;
  705. // Account for the width of the line
  706. arect.Inflate( GetWidth() / 2 );
  707. unsigned count = m_BezierPoints.size();
  708. for( unsigned ii = 1; ii < count; ii++ )
  709. {
  710. wxPoint vertex = m_BezierPoints[ii-1];
  711. wxPoint vertexNext = m_BezierPoints[ii];
  712. // Test if the point is within aRect
  713. if( arect.Contains( ( wxPoint ) vertex ) )
  714. return true;
  715. // Test if this edge intersects aRect
  716. if( arect.Intersects( vertex, vertexNext ) )
  717. return true;
  718. }
  719. }
  720. break;
  721. default:
  722. wxFAIL_MSG( "DRAWSEGMENT::HitTest (rect) not implemented for "
  723. + STROKE_T_asString( m_Shape ) );
  724. break;
  725. }
  726. return false;
  727. }
  728. wxString DRAWSEGMENT::GetSelectMenuText( EDA_UNITS aUnits ) const
  729. {
  730. return wxString::Format( _( "Pcb Graphic %s on %s" ),
  731. ShowShape( m_Shape ),
  732. GetLayerName() );
  733. }
  734. BITMAP_DEF DRAWSEGMENT::GetMenuImage() const
  735. {
  736. return add_dashed_line_xpm;
  737. }
  738. EDA_ITEM* DRAWSEGMENT::Clone() const
  739. {
  740. return new DRAWSEGMENT( *this );
  741. }
  742. const BOX2I DRAWSEGMENT::ViewBBox() const
  743. {
  744. // For arcs - do not include the center point in the bounding box,
  745. // it is redundant for displaying an arc
  746. if( m_Shape == S_ARC )
  747. {
  748. EDA_RECT bbox;
  749. bbox.SetOrigin( m_End );
  750. computeArcBBox( bbox );
  751. return BOX2I( bbox.GetOrigin(), bbox.GetSize() );
  752. }
  753. return EDA_ITEM::ViewBBox();
  754. }
  755. void DRAWSEGMENT::GetRectCorners( std::vector<wxPoint>* pts ) const
  756. {
  757. MODULE* module = GetParentModule();
  758. wxPoint topLeft = GetStart();
  759. wxPoint botRight = GetEnd();
  760. // Un-rotate rect topLeft and botRight
  761. if( module && KiROUND( module->GetOrientation() ) % 900 != 0 )
  762. {
  763. topLeft -= module->GetPosition();
  764. RotatePoint( &topLeft, -module->GetOrientation() );
  765. botRight -= module->GetPosition();
  766. RotatePoint( &botRight, -module->GetOrientation() );
  767. }
  768. // Set up the un-rotated 4 corners
  769. pts->emplace_back( topLeft );
  770. pts->emplace_back( botRight.x, topLeft.y );
  771. pts->emplace_back( botRight );
  772. pts->emplace_back( topLeft.x, botRight.y );
  773. // Now re-rotate the 4 corners to get a diamond
  774. if( module && KiROUND( module->GetOrientation() ) % 900 != 0 )
  775. {
  776. for( wxPoint& pt : *pts )
  777. {
  778. RotatePoint( &pt,module->GetOrientation() );
  779. pt += module->GetPosition();
  780. }
  781. }
  782. }
  783. void DRAWSEGMENT::computeArcBBox( EDA_RECT& aBBox ) const
  784. {
  785. // Do not include the center, which is not necessarily
  786. // inside the BB of a arc with a small angle
  787. aBBox.SetOrigin( m_End );
  788. wxPoint end = m_End;
  789. RotatePoint( &end, m_Start, -m_Angle );
  790. aBBox.Merge( end );
  791. // Determine the starting quarter
  792. // 0 right-bottom
  793. // 1 left-bottom
  794. // 2 left-top
  795. // 3 right-top
  796. unsigned int quarter = 0; // assume right-bottom
  797. if( m_End.x < m_Start.x )
  798. {
  799. if( m_End.y <= m_Start.y )
  800. quarter = 2;
  801. else // ( m_End.y > m_Start.y )
  802. quarter = 1;
  803. }
  804. else if( m_End.x >= m_Start.x )
  805. {
  806. if( m_End.y < m_Start.y )
  807. quarter = 3;
  808. else if( m_End.x == m_Start.x )
  809. quarter = 1;
  810. }
  811. int radius = GetRadius();
  812. int angle = (int) GetArcAngleStart() % 900 + m_Angle;
  813. bool directionCW = ( m_Angle > 0 ); // Is the direction of arc clockwise?
  814. // Make the angle positive, so we go clockwise and merge points belonging to the arc
  815. if( !directionCW )
  816. {
  817. angle = 900 - angle;
  818. quarter = ( quarter + 3 ) % 4; // -1 modulo arithmetic
  819. }
  820. while( angle > 900 )
  821. {
  822. switch( quarter )
  823. {
  824. case 0: aBBox.Merge( wxPoint( m_Start.x, m_Start.y + radius ) ); break; // down
  825. case 1: aBBox.Merge( wxPoint( m_Start.x - radius, m_Start.y ) ); break; // left
  826. case 2: aBBox.Merge( wxPoint( m_Start.x, m_Start.y - radius ) ); break; // up
  827. case 3: aBBox.Merge( wxPoint( m_Start.x + radius, m_Start.y ) ); break; // right
  828. }
  829. if( directionCW )
  830. ++quarter;
  831. else
  832. quarter += 3; // -1 modulo arithmetic
  833. quarter %= 4;
  834. angle -= 900;
  835. }
  836. }
  837. void DRAWSEGMENT::SetPolyPoints( const std::vector<wxPoint>& aPoints )
  838. {
  839. m_Poly.RemoveAllContours();
  840. m_Poly.NewOutline();
  841. for ( const wxPoint& p : aPoints )
  842. m_Poly.Append( p.x, p.y );
  843. }
  844. std::vector<SHAPE*> DRAWSEGMENT::MakeEffectiveShapes() const
  845. {
  846. std::vector<SHAPE*> effectiveShapes;
  847. switch( m_Shape )
  848. {
  849. case S_ARC:
  850. {
  851. SHAPE_ARC arc( GetCenter(), GetArcStart(), (double) GetAngle() / 10.0 );
  852. SHAPE_LINE_CHAIN l = arc.ConvertToPolyline();
  853. for( int i = 0; i < l.SegmentCount(); i++ )
  854. {
  855. effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A,
  856. l.Segment( i ).B, m_Width ) );
  857. }
  858. break;
  859. }
  860. case S_SEGMENT:
  861. effectiveShapes.emplace_back( new SHAPE_SEGMENT( GetStart(), GetEnd(), m_Width ) );
  862. break;
  863. case S_RECT:
  864. {
  865. std::vector<wxPoint> pts;
  866. GetRectCorners( &pts );
  867. if( m_Width == 0 )
  868. {
  869. effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
  870. }
  871. else
  872. {
  873. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], m_Width ) );
  874. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], m_Width ) );
  875. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], m_Width ) );
  876. effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], m_Width ) );
  877. }
  878. }
  879. break;
  880. case S_CIRCLE:
  881. {
  882. if( m_Width == 0 )
  883. {
  884. effectiveShapes.emplace_back( new SHAPE_CIRCLE( GetCenter(), GetRadius() ) );
  885. }
  886. else
  887. {
  888. // SHAPE_CIRCLE has no ConvertToPolyline() method, so use a 360.0 SHAPE_ARC
  889. SHAPE_ARC circle( GetCenter(), GetEnd(), 360.0 );
  890. SHAPE_LINE_CHAIN l = circle.ConvertToPolyline();
  891. for( int i = 0; i < l.SegmentCount(); i++ )
  892. {
  893. effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A,
  894. l.Segment( i ).B, m_Width ) );
  895. }
  896. }
  897. break;
  898. }
  899. case S_CURVE:
  900. {
  901. auto bezierPoints = buildBezierToSegmentsPointsList( GetWidth() );
  902. wxPoint start_pt = bezierPoints[0];
  903. for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
  904. {
  905. wxPoint end_pt = bezierPoints[jj];
  906. effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, m_Width ) );
  907. start_pt = end_pt;
  908. }
  909. break;
  910. }
  911. case S_POLYGON:
  912. {
  913. SHAPE_LINE_CHAIN l = GetPolyShape().COutline( 0 );
  914. if( IsPolygonFilled() )
  915. {
  916. effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
  917. }
  918. else
  919. {
  920. for( int i = 0; i < l.SegmentCount(); i++ )
  921. effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ) ) );
  922. }
  923. }
  924. break;
  925. default:
  926. wxFAIL_MSG( "DRAWSEGMENT::MakeEffectiveShapes unsupported DRAWSEGMENT shape: "
  927. + STROKE_T_asString( m_Shape ) );
  928. break;
  929. }
  930. return effectiveShapes;
  931. }
  932. std::shared_ptr<SHAPE> DRAWSEGMENT::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
  933. {
  934. return std::shared_ptr<SHAPE>( new SHAPE_COMPOUND( MakeEffectiveShapes() ) );
  935. }
  936. const std::vector<wxPoint> DRAWSEGMENT::BuildPolyPointsList() const
  937. {
  938. std::vector<wxPoint> rv;
  939. if( m_Poly.OutlineCount() )
  940. {
  941. if( m_Poly.COutline( 0 ).PointCount() )
  942. {
  943. for ( auto iter = m_Poly.CIterate(); iter; iter++ )
  944. rv.emplace_back( iter->x, iter->y );
  945. }
  946. }
  947. return rv;
  948. }
  949. bool DRAWSEGMENT::IsPolyShapeValid() const
  950. {
  951. // return true if the polygonal shape is valid (has more than 2 points)
  952. if( GetPolyShape().OutlineCount() == 0 )
  953. return false;
  954. const SHAPE_LINE_CHAIN& outline = ((SHAPE_POLY_SET&)GetPolyShape()).Outline( 0 );
  955. return outline.PointCount() > 2;
  956. }
  957. int DRAWSEGMENT::GetPointCount() const
  958. {
  959. // return the number of corners of the polygonal shape
  960. // this shape is expected to be only one polygon without hole
  961. if( GetPolyShape().OutlineCount() )
  962. return GetPolyShape().VertexCount( 0 );
  963. return 0;
  964. }
  965. void DRAWSEGMENT::SwapData( BOARD_ITEM* aImage )
  966. {
  967. DRAWSEGMENT* image = dynamic_cast<DRAWSEGMENT*>( aImage );
  968. assert( image );
  969. std::swap( m_Width, image->m_Width );
  970. std::swap( m_Start, image->m_Start );
  971. std::swap( m_End, image->m_End );
  972. std::swap( m_Shape, image->m_Shape );
  973. std::swap( m_Type, image->m_Type );
  974. std::swap( m_Angle, image->m_Angle );
  975. std::swap( m_BezierC1, image->m_BezierC1 );
  976. std::swap( m_BezierC2, image->m_BezierC2 );
  977. std::swap( m_BezierPoints, image->m_BezierPoints );
  978. std::swap( m_Poly, image->m_Poly );
  979. std::swap( m_Layer, image->m_Layer );
  980. std::swap( m_Flags, image->m_Flags );
  981. std::swap( m_Status, image->m_Status );
  982. std::swap( m_Parent, image->m_Parent );
  983. std::swap( m_forceVisible, image->m_forceVisible );
  984. }
  985. bool DRAWSEGMENT::cmp_drawings::operator()( const BOARD_ITEM* aFirst, const BOARD_ITEM* aSecond ) const
  986. {
  987. if( aFirst->Type() != aSecond->Type() )
  988. return aFirst->Type() < aSecond->Type();
  989. if( aFirst->GetLayer() != aSecond->GetLayer() )
  990. return aFirst->GetLayer() < aSecond->GetLayer();
  991. if( aFirst->Type() == PCB_LINE_T )
  992. {
  993. const DRAWSEGMENT* dwgA = static_cast<const DRAWSEGMENT*>( aFirst );
  994. const DRAWSEGMENT* dwgB = static_cast<const DRAWSEGMENT*>( aSecond );
  995. if( dwgA->GetShape() != dwgB->GetShape() )
  996. return dwgA->GetShape() < dwgB->GetShape();
  997. }
  998. return aFirst->m_Uuid < aSecond->m_Uuid;
  999. }
  1000. static struct DRAWSEGMENT_DESC
  1001. {
  1002. DRAWSEGMENT_DESC()
  1003. {
  1004. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1005. REGISTER_TYPE( DRAWSEGMENT );
  1006. propMgr.InheritsAfter( TYPE_HASH( DRAWSEGMENT ), TYPE_HASH( BOARD_ITEM ) );
  1007. propMgr.AddProperty( new PROPERTY<DRAWSEGMENT, int>( _( "Thickness" ),
  1008. &DRAWSEGMENT::SetWidth, &DRAWSEGMENT::GetWidth, PROPERTY_DISPLAY::DISTANCE ) );
  1009. // TODO show certain properties depending on the shape
  1010. propMgr.AddProperty( new PROPERTY<DRAWSEGMENT, double>( _( "Angle" ),
  1011. &DRAWSEGMENT::SetAngle, &DRAWSEGMENT::GetAngle, PROPERTY_DISPLAY::DECIDEGREE ) );
  1012. // TODO or may have different names (arcs)
  1013. // TODO type?
  1014. propMgr.AddProperty( new PROPERTY<DRAWSEGMENT, int>( _( "End X" ),
  1015. &DRAWSEGMENT::SetEndX, &DRAWSEGMENT::GetEndX, PROPERTY_DISPLAY::DISTANCE ) );
  1016. propMgr.AddProperty( new PROPERTY<DRAWSEGMENT, int>( _( "End Y" ),
  1017. &DRAWSEGMENT::SetEndY, &DRAWSEGMENT::GetEndY, PROPERTY_DISPLAY::DISTANCE ) );
  1018. }
  1019. } _DRAWSEGMENT_DESC;