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.

1021 lines
31 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
  7. * Copyright (C) 1992-2023 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 <google/protobuf/any.pb.h>
  27. #include <magic_enum.hpp>
  28. #include <bitmaps.h>
  29. #include <core/mirror.h>
  30. #include <macros.h>
  31. #include <pcb_edit_frame.h>
  32. #include <board_design_settings.h>
  33. #include <board.h>
  34. #include <footprint.h>
  35. #include <lset.h>
  36. #include <pad.h>
  37. #include <base_units.h>
  38. #include <geometry/shape_compound.h>
  39. #include <pcb_shape.h>
  40. #include <pcb_painter.h>
  41. #include <api/board/board_types.pb.h>
  42. #include <api/api_enums.h>
  43. #include <api/api_utils.h>
  44. PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T aItemType, SHAPE_T aShapeType ) :
  45. BOARD_CONNECTED_ITEM( aParent, aItemType ),
  46. EDA_SHAPE( aShapeType, pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL )
  47. {
  48. }
  49. PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, SHAPE_T shapetype ) :
  50. BOARD_CONNECTED_ITEM( aParent, PCB_SHAPE_T ),
  51. EDA_SHAPE( shapetype, pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL )
  52. {
  53. }
  54. PCB_SHAPE::~PCB_SHAPE()
  55. {
  56. }
  57. void PCB_SHAPE::Serialize( google::protobuf::Any &aContainer ) const
  58. {
  59. kiapi::board::types::GraphicShape msg;
  60. msg.mutable_id()->set_value( m_Uuid.AsStdString() );
  61. msg.set_layer( ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( GetLayer() ) );
  62. msg.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
  63. : kiapi::common::types::LockedState::LS_UNLOCKED );
  64. msg.mutable_net()->mutable_code()->set_value( GetNetCode() );
  65. msg.mutable_net()->set_name( GetNetname() );
  66. kiapi::common::types::StrokeAttributes* stroke
  67. = msg.mutable_attributes()->mutable_stroke();
  68. kiapi::common::types::GraphicFillAttributes* fill = msg.mutable_attributes()->mutable_fill();
  69. stroke->mutable_width()->set_value_nm( GetWidth() );
  70. switch( GetLineStyle() )
  71. {
  72. case LINE_STYLE::DEFAULT: stroke->set_style( kiapi::common::types::SLS_DEFAULT ); break;
  73. case LINE_STYLE::SOLID: stroke->set_style( kiapi::common::types::SLS_SOLID ); break;
  74. case LINE_STYLE::DASH: stroke->set_style( kiapi::common::types::SLS_DASH ); break;
  75. case LINE_STYLE::DOT: stroke->set_style( kiapi::common::types::SLS_DOT ); break;
  76. case LINE_STYLE::DASHDOT: stroke->set_style( kiapi::common::types::SLS_DASHDOT ); break;
  77. case LINE_STYLE::DASHDOTDOT: stroke->set_style( kiapi::common::types::SLS_DASHDOTDOT ); break;
  78. default: break;
  79. }
  80. switch( GetFillMode() )
  81. {
  82. case FILL_T::FILLED_SHAPE: fill->set_fill_type( kiapi::common::types::GFT_FILLED ); break;
  83. default: fill->set_fill_type( kiapi::common::types::GFT_UNFILLED ); break;
  84. }
  85. switch( GetShape() )
  86. {
  87. case SHAPE_T::SEGMENT:
  88. {
  89. kiapi::board::types::GraphicSegmentAttributes* segment = msg.mutable_segment();
  90. kiapi::common::PackVector2( *segment->mutable_start(), GetStart() );
  91. kiapi::common::PackVector2( *segment->mutable_end(), GetEnd() );
  92. break;
  93. }
  94. case SHAPE_T::RECTANGLE:
  95. {
  96. kiapi::board::types::GraphicRectangleAttributes* rectangle = msg.mutable_rectangle();
  97. kiapi::common::PackVector2( *rectangle->mutable_top_left(), GetStart() );
  98. kiapi::common::PackVector2( *rectangle->mutable_bottom_right(), GetEnd() );
  99. break;
  100. }
  101. case SHAPE_T::ARC:
  102. {
  103. kiapi::board::types::GraphicArcAttributes* arc = msg.mutable_arc();
  104. kiapi::common::PackVector2( *arc->mutable_start(), GetStart() );
  105. kiapi::common::PackVector2( *arc->mutable_mid(), GetArcMid() );
  106. kiapi::common::PackVector2( *arc->mutable_end(), GetEnd() );
  107. break;
  108. }
  109. case SHAPE_T::CIRCLE:
  110. {
  111. kiapi::board::types::GraphicCircleAttributes* circle = msg.mutable_circle();
  112. kiapi::common::PackVector2( *circle->mutable_center(), GetStart() );
  113. kiapi::common::PackVector2( *circle->mutable_radius_point(), GetEnd() );
  114. break;
  115. }
  116. case SHAPE_T::POLY:
  117. {
  118. kiapi::common::types::PolySet* polyset = msg.mutable_polygon();
  119. for( int idx = 0; idx < GetPolyShape().OutlineCount(); ++idx )
  120. {
  121. const SHAPE_POLY_SET::POLYGON& poly = GetPolyShape().Polygon( idx );
  122. if( poly.empty() )
  123. continue;
  124. kiapi::common::types::PolygonWithHoles* polyMsg = polyset->mutable_polygons()->Add();
  125. kiapi::common::PackPolyLine( *polyMsg->mutable_outline(), poly.front() );
  126. if( poly.size() > 1 )
  127. {
  128. for( size_t hole = 1; hole < poly.size(); ++hole )
  129. {
  130. kiapi::common::types::PolyLine* pl = polyMsg->mutable_holes()->Add();
  131. kiapi::common::PackPolyLine( *pl, poly[hole] );
  132. }
  133. }
  134. }
  135. break;
  136. }
  137. case SHAPE_T::BEZIER:
  138. {
  139. kiapi::board::types::GraphicBezierAttributes* bezier = msg.mutable_bezier();
  140. kiapi::common::PackVector2( *bezier->mutable_start(), GetStart() );
  141. kiapi::common::PackVector2( *bezier->mutable_control1(), GetBezierC1() );
  142. kiapi::common::PackVector2( *bezier->mutable_control2(), GetBezierC2() );
  143. kiapi::common::PackVector2( *bezier->mutable_end(), GetEnd() );
  144. break;
  145. }
  146. default:
  147. wxASSERT_MSG( false, "Unhandled shape in PCB_SHAPE::Serialize" );
  148. }
  149. aContainer.PackFrom( msg );
  150. }
  151. bool PCB_SHAPE::Deserialize( const google::protobuf::Any &aContainer )
  152. {
  153. kiapi::board::types::GraphicShape msg;
  154. if( !aContainer.UnpackTo( &msg ) )
  155. return false;
  156. // Initialize everything to a known state that doesn't get touched by every
  157. // codepath below, to make sure the equality operator is consistent
  158. m_start = {};
  159. m_end = {};
  160. m_arcCenter = {};
  161. m_arcMidData = {};
  162. m_bezierC1 = {};
  163. m_bezierC2 = {};
  164. m_editState = 0;
  165. m_proxyItem = false;
  166. m_endsSwapped = false;
  167. const_cast<KIID&>( m_Uuid ) = KIID( msg.id().value() );
  168. SetLocked( msg.locked() == kiapi::common::types::LS_LOCKED );
  169. SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( msg.layer() ) );
  170. SetNetCode( msg.net().code().value() );
  171. SetFilled( msg.attributes().fill().fill_type() == kiapi::common::types::GFT_FILLED );
  172. SetWidth( msg.attributes().stroke().width().value_nm() );
  173. switch( msg.attributes().stroke().style() )
  174. {
  175. case kiapi::common::types::SLS_DEFAULT: SetLineStyle( LINE_STYLE::DEFAULT ); break;
  176. case kiapi::common::types::SLS_SOLID: SetLineStyle( LINE_STYLE::SOLID ); break;
  177. case kiapi::common::types::SLS_DASH: SetLineStyle( LINE_STYLE::DASH ); break;
  178. case kiapi::common::types::SLS_DOT: SetLineStyle( LINE_STYLE::DOT ); break;
  179. case kiapi::common::types::SLS_DASHDOT: SetLineStyle( LINE_STYLE::DASHDOT ); break;
  180. case kiapi::common::types::SLS_DASHDOTDOT: SetLineStyle( LINE_STYLE::DASHDOTDOT ); break;
  181. default: break;
  182. }
  183. if( msg.has_segment() )
  184. {
  185. SetShape( SHAPE_T::SEGMENT );
  186. SetStart( kiapi::common::UnpackVector2( msg.segment().start() ) );
  187. SetEnd( kiapi::common::UnpackVector2( msg.segment().end() ) );
  188. }
  189. else if( msg.has_rectangle() )
  190. {
  191. SetShape( SHAPE_T::RECTANGLE );
  192. SetStart( kiapi::common::UnpackVector2( msg.rectangle().top_left() ) );
  193. SetEnd( kiapi::common::UnpackVector2( msg.rectangle().bottom_right() ) );
  194. }
  195. else if( msg.has_arc() )
  196. {
  197. SetShape( SHAPE_T::ARC );
  198. SetArcGeometry( kiapi::common::UnpackVector2( msg.arc().start() ),
  199. kiapi::common::UnpackVector2( msg.arc().mid() ),
  200. kiapi::common::UnpackVector2( msg.arc().end() ) );
  201. }
  202. else if( msg.has_circle() )
  203. {
  204. SetShape( SHAPE_T::CIRCLE );
  205. SetStart( kiapi::common::UnpackVector2( msg.circle().center() ) );
  206. SetEnd( kiapi::common::UnpackVector2( msg.circle().radius_point() ) );
  207. }
  208. else if( msg.has_polygon() )
  209. {
  210. SetShape( SHAPE_T::POLY );
  211. const auto& polyMsg = msg.polygon().polygons();
  212. SHAPE_POLY_SET sps;
  213. for( const kiapi::common::types::PolygonWithHoles& polygonWithHoles : polyMsg )
  214. {
  215. SHAPE_POLY_SET::POLYGON polygon;
  216. polygon.emplace_back( kiapi::common::UnpackPolyLine( polygonWithHoles.outline() ) );
  217. for( const kiapi::common::types::PolyLine& holeMsg : polygonWithHoles.holes() )
  218. polygon.emplace_back( kiapi::common::UnpackPolyLine( holeMsg ) );
  219. sps.AddPolygon( polygon );
  220. }
  221. SetPolyShape( sps );
  222. }
  223. else if( msg.has_bezier() )
  224. {
  225. SetShape( SHAPE_T::BEZIER );
  226. SetStart( kiapi::common::UnpackVector2( msg.bezier().start() ) );
  227. SetBezierC1( kiapi::common::UnpackVector2( msg.bezier().control1() ) );
  228. SetBezierC2( kiapi::common::UnpackVector2( msg.bezier().control2() ) );
  229. SetEnd( kiapi::common::UnpackVector2( msg.bezier().end() ) );
  230. RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
  231. }
  232. return true;
  233. }
  234. bool PCB_SHAPE::IsType( const std::vector<KICAD_T>& aScanTypes ) const
  235. {
  236. if( BOARD_ITEM::IsType( aScanTypes ) )
  237. return true;
  238. bool sametype = false;
  239. for( KICAD_T scanType : aScanTypes )
  240. {
  241. if( scanType == PCB_LOCATE_BOARD_EDGE_T )
  242. sametype = m_layer == Edge_Cuts;
  243. else if( scanType == PCB_SHAPE_LOCATE_ARC_T )
  244. sametype = m_shape == SHAPE_T::ARC;
  245. else if( scanType == PCB_SHAPE_LOCATE_CIRCLE_T )
  246. sametype = m_shape == SHAPE_T::CIRCLE;
  247. else if( scanType == PCB_SHAPE_LOCATE_RECT_T )
  248. sametype = m_shape == SHAPE_T::RECTANGLE;
  249. else if( scanType == PCB_SHAPE_LOCATE_SEGMENT_T )
  250. sametype = m_shape == SHAPE_T::SEGMENT;
  251. else if( scanType == PCB_SHAPE_LOCATE_POLY_T )
  252. sametype = m_shape == SHAPE_T::POLY;
  253. else if( scanType == PCB_SHAPE_LOCATE_BEZIER_T )
  254. sametype = m_shape == SHAPE_T::BEZIER;
  255. if( sametype )
  256. return true;
  257. }
  258. return false;
  259. }
  260. bool PCB_SHAPE::IsConnected() const
  261. {
  262. // Only board-level copper shapes are connectable
  263. return IsOnCopperLayer() && !GetParentFootprint();
  264. }
  265. void PCB_SHAPE::SetLayer( PCB_LAYER_ID aLayer )
  266. {
  267. BOARD_ITEM::SetLayer( aLayer );
  268. if( !IsOnCopperLayer() )
  269. SetNetCode( -1 );
  270. }
  271. std::vector<VECTOR2I> PCB_SHAPE::GetConnectionPoints() const
  272. {
  273. std::vector<VECTOR2I> ret;
  274. // For filled shapes, we may as well use a centroid
  275. if( IsFilled() )
  276. {
  277. ret.emplace_back( GetCenter() );
  278. return ret;
  279. }
  280. switch( m_shape )
  281. {
  282. case SHAPE_T::ARC:
  283. ret.emplace_back( GetArcMid() );
  284. KI_FALLTHROUGH;
  285. case SHAPE_T::SEGMENT:
  286. case SHAPE_T::BEZIER:
  287. ret.emplace_back( GetStart() );
  288. ret.emplace_back( GetEnd() );
  289. break;
  290. case SHAPE_T::POLY:
  291. for( auto iter = GetPolyShape().CIterate(); iter; ++iter )
  292. ret.emplace_back( *iter );
  293. break;
  294. case SHAPE_T::RECTANGLE:
  295. for( const VECTOR2I& pt : GetRectCorners() )
  296. ret.emplace_back( pt );
  297. break;
  298. default:
  299. break;
  300. }
  301. return ret;
  302. }
  303. int PCB_SHAPE::GetWidth() const
  304. {
  305. // A stroke width of 0 in PCBNew means no-border, but negative stroke-widths are only used
  306. // in EEschema (see SCH_SHAPE::GetPenWidth()).
  307. // Since negative stroke widths can trip up down-stream code (such as the Gerber plotter), we
  308. // weed them out here.
  309. return std::max( EDA_SHAPE::GetWidth(), 0 );
  310. }
  311. void PCB_SHAPE::StyleFromSettings( const BOARD_DESIGN_SETTINGS& settings )
  312. {
  313. m_stroke.SetWidth( settings.GetLineThickness( GetLayer() ) );
  314. }
  315. const VECTOR2I PCB_SHAPE::GetFocusPosition() const
  316. {
  317. // For some shapes return the visual center, but for not filled polygonal shapes,
  318. // the center is usually far from the shape: a point on the outline is better
  319. switch( m_shape )
  320. {
  321. case SHAPE_T::CIRCLE:
  322. if( !IsFilled() )
  323. return VECTOR2I( GetCenter().x + GetRadius(), GetCenter().y );
  324. else
  325. return GetCenter();
  326. case SHAPE_T::RECTANGLE:
  327. if( !IsFilled() )
  328. return GetStart();
  329. else
  330. return GetCenter();
  331. case SHAPE_T::POLY:
  332. if( !IsFilled() )
  333. {
  334. VECTOR2I pos = GetPolyShape().Outline(0).CPoint(0);
  335. return VECTOR2I( pos.x, pos.y );
  336. }
  337. else
  338. {
  339. return GetCenter();
  340. }
  341. case SHAPE_T::ARC:
  342. return GetArcMid();
  343. case SHAPE_T::BEZIER:
  344. return GetStart();
  345. default:
  346. return GetCenter();
  347. }
  348. }
  349. std::vector<VECTOR2I> PCB_SHAPE::GetCorners() const
  350. {
  351. std::vector<VECTOR2I> pts;
  352. if( GetShape() == SHAPE_T::RECTANGLE )
  353. {
  354. pts = GetRectCorners();
  355. }
  356. else if( GetShape() == SHAPE_T::POLY )
  357. {
  358. for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
  359. {
  360. for( const VECTOR2I& pt : GetPolyShape().Outline( ii ).CPoints() )
  361. pts.emplace_back( pt );
  362. }
  363. }
  364. else
  365. {
  366. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  367. }
  368. while( pts.size() < 4 )
  369. pts.emplace_back( pts.back() + VECTOR2I( 10, 10 ) );
  370. return pts;
  371. }
  372. void PCB_SHAPE::Move( const VECTOR2I& aMoveVector )
  373. {
  374. move( aMoveVector );
  375. }
  376. void PCB_SHAPE::Scale( double aScale )
  377. {
  378. scale( aScale );
  379. }
  380. void PCB_SHAPE::Normalize()
  381. {
  382. if( m_shape == SHAPE_T::RECTANGLE )
  383. {
  384. VECTOR2I start = GetStart();
  385. VECTOR2I end = GetEnd();
  386. BOX2I rect( start, end - start );
  387. rect.Normalize();
  388. SetStart( rect.GetPosition() );
  389. SetEnd( rect.GetEnd() );
  390. }
  391. else if( m_shape == SHAPE_T::POLY )
  392. {
  393. auto horizontal =
  394. []( const SEG& seg )
  395. {
  396. return seg.A.y == seg.B.y;
  397. };
  398. auto vertical =
  399. []( const SEG& seg )
  400. {
  401. return seg.A.x == seg.B.x;
  402. };
  403. // Convert a poly back to a rectangle if appropriate
  404. if( m_poly.OutlineCount() == 1 && m_poly.Outline( 0 ).SegmentCount() == 4 )
  405. {
  406. SHAPE_LINE_CHAIN& outline = m_poly.Outline( 0 );
  407. if( horizontal( outline.Segment( 0 ) )
  408. && vertical( outline.Segment( 1 ) )
  409. && horizontal( outline.Segment( 2 ) )
  410. && vertical( outline.Segment( 3 ) ) )
  411. {
  412. m_shape = SHAPE_T::RECTANGLE;
  413. m_start.x = std::min( outline.Segment( 0 ).A.x, outline.Segment( 0 ).B.x );
  414. m_start.y = std::min( outline.Segment( 1 ).A.y, outline.Segment( 1 ).B.y );
  415. m_end.x = std::max( outline.Segment( 0 ).A.x, outline.Segment( 0 ).B.x );
  416. m_end.y = std::max( outline.Segment( 1 ).A.y, outline.Segment( 1 ).B.y );
  417. }
  418. else if( vertical( outline.Segment( 0 ) )
  419. && horizontal( outline.Segment( 1 ) )
  420. && vertical( outline.Segment( 2 ) )
  421. && horizontal( outline.Segment( 3 ) ) )
  422. {
  423. m_shape = SHAPE_T::RECTANGLE;
  424. m_start.x = std::min( outline.Segment( 1 ).A.x, outline.Segment( 1 ).B.x );
  425. m_start.y = std::min( outline.Segment( 0 ).A.y, outline.Segment( 0 ).B.y );
  426. m_end.x = std::max( outline.Segment( 1 ).A.x, outline.Segment( 1 ).B.x );
  427. m_end.y = std::max( outline.Segment( 0 ).A.y, outline.Segment( 0 ).B.y );
  428. }
  429. }
  430. }
  431. }
  432. void PCB_SHAPE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  433. {
  434. rotate( aRotCentre, aAngle );
  435. }
  436. void PCB_SHAPE::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
  437. {
  438. flip( aCentre, aFlipLeftRight );
  439. SetLayer( GetBoard()->FlipLayer( GetLayer() ) );
  440. }
  441. void PCB_SHAPE::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
  442. {
  443. // Mirror an edge of the footprint. the layer is not modified
  444. // This is a footprint shape modification.
  445. switch( GetShape() )
  446. {
  447. case SHAPE_T::ARC:
  448. case SHAPE_T::SEGMENT:
  449. case SHAPE_T::RECTANGLE:
  450. case SHAPE_T::CIRCLE:
  451. case SHAPE_T::BEZIER:
  452. if( aMirrorAroundXAxis )
  453. {
  454. MIRROR( m_start.y, aCentre.y );
  455. MIRROR( m_end.y, aCentre.y );
  456. MIRROR( m_arcCenter.y, aCentre.y );
  457. MIRROR( m_bezierC1.y, aCentre.y );
  458. MIRROR( m_bezierC2.y, aCentre.y );
  459. }
  460. else
  461. {
  462. MIRROR( m_start.x, aCentre.x );
  463. MIRROR( m_end.x, aCentre.x );
  464. MIRROR( m_arcCenter.x, aCentre.x );
  465. MIRROR( m_bezierC1.x, aCentre.x );
  466. MIRROR( m_bezierC2.x, aCentre.x );
  467. }
  468. if( GetShape() == SHAPE_T::ARC )
  469. std::swap( m_start, m_end );
  470. if( GetShape() == SHAPE_T::BEZIER )
  471. RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
  472. break;
  473. case SHAPE_T::POLY:
  474. m_poly.Mirror( !aMirrorAroundXAxis, aMirrorAroundXAxis, aCentre );
  475. break;
  476. default:
  477. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  478. }
  479. }
  480. void PCB_SHAPE::SetIsProxyItem( bool aIsProxy )
  481. {
  482. PAD* parentPad = nullptr;
  483. if( GetBoard() && GetBoard()->IsFootprintHolder() )
  484. {
  485. for( FOOTPRINT* fp : GetBoard()->Footprints() )
  486. {
  487. for( PAD* pad : fp->Pads() )
  488. {
  489. if( pad->IsEntered() )
  490. {
  491. parentPad = pad;
  492. break;
  493. }
  494. }
  495. }
  496. }
  497. if( aIsProxy && !m_proxyItem )
  498. {
  499. if( GetShape() == SHAPE_T::SEGMENT )
  500. {
  501. if( parentPad && parentPad->GetThermalSpokeWidth() )
  502. SetWidth( parentPad->GetThermalSpokeWidth() );
  503. else
  504. SetWidth( pcbIUScale.mmToIU( ZONE_THERMAL_RELIEF_COPPER_WIDTH_MM ) );
  505. }
  506. else
  507. {
  508. SetWidth( 1 );
  509. }
  510. }
  511. else if( m_proxyItem && !aIsProxy )
  512. {
  513. SetWidth( pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ) );
  514. }
  515. m_proxyItem = aIsProxy;
  516. }
  517. double PCB_SHAPE::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  518. {
  519. constexpr double HIDE = std::numeric_limits<double>::max();
  520. constexpr double SHOW = 0.0;
  521. KIGFX::PCB_PAINTER* painter = static_cast<KIGFX::PCB_PAINTER*>( aView->GetPainter() );
  522. KIGFX::PCB_RENDER_SETTINGS* renderSettings = painter->GetSettings();
  523. if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
  524. {
  525. // Hide shadow if the main layer is not shown
  526. if( !aView->IsLayerVisible( m_layer ) )
  527. return HIDE;
  528. // Hide shadow on dimmed tracks
  529. if( renderSettings->GetHighContrast() )
  530. {
  531. if( m_layer != renderSettings->GetPrimaryHighContrastLayer() )
  532. return HIDE;
  533. }
  534. }
  535. if( FOOTPRINT* parent = GetParentFootprint() )
  536. {
  537. if( parent->GetLayer() == F_Cu && !aView->IsLayerVisible( LAYER_FOOTPRINTS_FR ) )
  538. return HIDE;
  539. if( parent->GetLayer() == B_Cu && !aView->IsLayerVisible( LAYER_FOOTPRINTS_BK ) )
  540. return HIDE;
  541. }
  542. return SHOW;
  543. }
  544. void PCB_SHAPE::ViewGetLayers( int aLayers[], int& aCount ) const
  545. {
  546. aLayers[0] = GetLayer();
  547. if( IsOnCopperLayer() )
  548. {
  549. aLayers[1] = GetNetnameLayer( aLayers[0] );
  550. aCount = 2;
  551. }
  552. else
  553. {
  554. aCount = 1;
  555. }
  556. if( IsLocked() )
  557. aLayers[ aCount++ ] = LAYER_LOCKED_ITEM_SHADOW;
  558. }
  559. void PCB_SHAPE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  560. {
  561. if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
  562. {
  563. if( FOOTPRINT* parent = GetParentFootprint() )
  564. aList.emplace_back( _( "Footprint" ), parent->GetReference() );
  565. }
  566. aList.emplace_back( _( "Type" ), _( "Drawing" ) );
  567. if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
  568. aList.emplace_back( _( "Status" ), _( "Locked" ) );
  569. ShapeGetMsgPanelInfo( aFrame, aList );
  570. aList.emplace_back( _( "Layer" ), GetLayerName() );
  571. }
  572. wxString PCB_SHAPE::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  573. {
  574. FOOTPRINT* parentFP = nullptr;
  575. if( EDA_DRAW_FRAME* frame = dynamic_cast<EDA_DRAW_FRAME*>( aUnitsProvider ) )
  576. {
  577. if( frame->GetName() == PCB_EDIT_FRAME_NAME )
  578. parentFP = GetParentFootprint();
  579. }
  580. if( IsOnCopperLayer() )
  581. {
  582. if( parentFP )
  583. {
  584. return wxString::Format( _( "%s %s of %s on %s" ),
  585. GetFriendlyName(),
  586. GetNetnameMsg(),
  587. parentFP->GetReference(),
  588. GetLayerName() );
  589. }
  590. else
  591. {
  592. return wxString::Format( _( "%s %s on %s" ),
  593. GetFriendlyName(),
  594. GetNetnameMsg(),
  595. GetLayerName() );
  596. }
  597. }
  598. else
  599. {
  600. if( parentFP )
  601. {
  602. return wxString::Format( _( "%s of %s on %s" ),
  603. GetFriendlyName(),
  604. parentFP->GetReference(),
  605. GetLayerName() );
  606. }
  607. else
  608. {
  609. return wxString::Format( _( "%s on %s" ),
  610. GetFriendlyName(),
  611. GetLayerName() );
  612. }
  613. }
  614. }
  615. BITMAPS PCB_SHAPE::GetMenuImage() const
  616. {
  617. if( GetParentFootprint() )
  618. return BITMAPS::show_mod_edge;
  619. else
  620. return BITMAPS::add_dashed_line;
  621. }
  622. EDA_ITEM* PCB_SHAPE::Clone() const
  623. {
  624. return new PCB_SHAPE( *this );
  625. }
  626. const BOX2I PCB_SHAPE::ViewBBox() const
  627. {
  628. BOX2I return_box = EDA_ITEM::ViewBBox();
  629. // Inflate the bounding box by just a bit more for safety.
  630. return_box.Inflate( GetWidth() );
  631. return return_box;
  632. }
  633. std::shared_ptr<SHAPE> PCB_SHAPE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  634. {
  635. return std::make_shared<SHAPE_COMPOUND>( MakeEffectiveShapes() );
  636. }
  637. void PCB_SHAPE::swapData( BOARD_ITEM* aImage )
  638. {
  639. PCB_SHAPE* image = dynamic_cast<PCB_SHAPE*>( aImage );
  640. wxCHECK( image, /* void */ );
  641. SwapShape( image );
  642. // Swap params not handled by SwapShape( image )
  643. std::swap( m_layer, image->m_layer );
  644. std::swap( m_isKnockout, image->m_isKnockout );
  645. std::swap( m_isLocked, image->m_isLocked );
  646. std::swap( m_flags, image->m_flags );
  647. std::swap( m_parent, image->m_parent );
  648. std::swap( m_forceVisible, image->m_forceVisible );
  649. std::swap( m_netinfo, image->m_netinfo );
  650. }
  651. bool PCB_SHAPE::cmp_drawings::operator()( const BOARD_ITEM* aFirst,
  652. const BOARD_ITEM* aSecond ) const
  653. {
  654. if( aFirst->Type() != aSecond->Type() )
  655. return aFirst->Type() < aSecond->Type();
  656. if( aFirst->GetLayer() != aSecond->GetLayer() )
  657. return aFirst->GetLayer() < aSecond->GetLayer();
  658. if( aFirst->Type() == PCB_SHAPE_T )
  659. {
  660. const PCB_SHAPE* dwgA = static_cast<const PCB_SHAPE*>( aFirst );
  661. const PCB_SHAPE* dwgB = static_cast<const PCB_SHAPE*>( aSecond );
  662. if( dwgA->GetShape() != dwgB->GetShape() )
  663. return dwgA->GetShape() < dwgB->GetShape();
  664. }
  665. return aFirst->m_Uuid < aSecond->m_Uuid;
  666. }
  667. void PCB_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
  668. int aClearance, int aError, ERROR_LOC aErrorLoc,
  669. bool ignoreLineWidth ) const
  670. {
  671. EDA_SHAPE::TransformShapeToPolygon( aBuffer, aClearance, aError, aErrorLoc, ignoreLineWidth );
  672. }
  673. bool PCB_SHAPE::operator==( const BOARD_ITEM& aOther ) const
  674. {
  675. if( aOther.Type() != Type() )
  676. return false;
  677. const PCB_SHAPE& other = static_cast<const PCB_SHAPE&>( aOther );
  678. return *this == other;
  679. }
  680. bool PCB_SHAPE::operator==( const PCB_SHAPE& aOther ) const
  681. {
  682. if( aOther.Type() != Type() )
  683. return false;
  684. const PCB_SHAPE& other = static_cast<const PCB_SHAPE&>( aOther );
  685. if( m_layer != other.m_layer )
  686. return false;
  687. if( m_isKnockout != other.m_isKnockout )
  688. return false;
  689. if( m_isLocked != other.m_isLocked )
  690. return false;
  691. if( m_flags != other.m_flags )
  692. return false;
  693. if( m_forceVisible != other.m_forceVisible )
  694. return false;
  695. if( m_netinfo->GetNetCode() != other.m_netinfo->GetNetCode() )
  696. return false;
  697. return EDA_SHAPE::operator==( other );
  698. }
  699. double PCB_SHAPE::Similarity( const BOARD_ITEM& aOther ) const
  700. {
  701. if( aOther.Type() != Type() )
  702. return 0.0;
  703. const PCB_SHAPE& other = static_cast<const PCB_SHAPE&>( aOther );
  704. double similarity = 1.0;
  705. if( GetLayer() != other.GetLayer() )
  706. similarity *= 0.9;
  707. if( m_isKnockout != other.m_isKnockout )
  708. similarity *= 0.9;
  709. if( m_isLocked != other.m_isLocked )
  710. similarity *= 0.9;
  711. if( m_flags != other.m_flags )
  712. similarity *= 0.9;
  713. if( m_forceVisible != other.m_forceVisible )
  714. similarity *= 0.9;
  715. if( m_netinfo->GetNetCode() != other.m_netinfo->GetNetCode() )
  716. similarity *= 0.9;
  717. similarity *= EDA_SHAPE::Similarity( other );
  718. return similarity;
  719. }
  720. static struct PCB_SHAPE_DESC
  721. {
  722. PCB_SHAPE_DESC()
  723. {
  724. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  725. REGISTER_TYPE( PCB_SHAPE );
  726. propMgr.AddTypeCast( new TYPE_CAST<PCB_SHAPE, BOARD_CONNECTED_ITEM> );
  727. propMgr.AddTypeCast( new TYPE_CAST<PCB_SHAPE, EDA_SHAPE> );
  728. propMgr.InheritsAfter( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
  729. propMgr.InheritsAfter( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ) );
  730. // Need to initialise enum_map before we can use a Property enum for it
  731. ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
  732. if( layerEnum.Choices().GetCount() == 0 )
  733. {
  734. layerEnum.Undefined( UNDEFINED_LAYER );
  735. for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() )
  736. layerEnum.Map( layer, LSET::Name( layer ) );
  737. }
  738. void ( PCB_SHAPE::*shapeLayerSetter )( PCB_LAYER_ID ) = &PCB_SHAPE::SetLayer;
  739. PCB_LAYER_ID ( PCB_SHAPE::*shapeLayerGetter )() const = &PCB_SHAPE::GetLayer;
  740. auto layerProperty = new PROPERTY_ENUM<PCB_SHAPE, PCB_LAYER_ID>(
  741. _HKI( "Layer" ), shapeLayerSetter, shapeLayerGetter );
  742. propMgr.ReplaceProperty( TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ), layerProperty );
  743. // Only polygons have meaningful Position properties.
  744. // On other shapes, these are duplicates of the Start properties.
  745. auto isPolygon =
  746. []( INSPECTABLE* aItem ) -> bool
  747. {
  748. if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
  749. return shape->GetShape() == SHAPE_T::POLY;
  750. return false;
  751. };
  752. propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_ITEM ),
  753. _HKI( "Position X" ), isPolygon );
  754. propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_ITEM ),
  755. _HKI( "Position Y" ), isPolygon );
  756. propMgr.Mask( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ), _HKI( "Line Color" ) );
  757. propMgr.Mask( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ), _HKI( "Fill Color" ) );
  758. auto isCopper =
  759. []( INSPECTABLE* aItem ) -> bool
  760. {
  761. if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
  762. return shape->IsOnCopperLayer();
  763. return false;
  764. };
  765. propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_CONNECTED_ITEM ),
  766. _HKI( "Net" ), isCopper );
  767. auto isPadEditMode =
  768. []( BOARD* aBoard ) -> bool
  769. {
  770. if( aBoard && aBoard->IsFootprintHolder() )
  771. {
  772. for( FOOTPRINT* fp : aBoard->Footprints() )
  773. {
  774. for( PAD* pad : fp->Pads() )
  775. {
  776. if( pad->IsEntered() )
  777. return true;
  778. }
  779. }
  780. }
  781. return false;
  782. };
  783. auto showNumberBoxProperty =
  784. [&]( INSPECTABLE* aItem ) -> bool
  785. {
  786. if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
  787. {
  788. if( shape->GetShape() == SHAPE_T::RECTANGLE )
  789. return isPadEditMode( shape->GetBoard() );
  790. }
  791. return false;
  792. };
  793. auto showSpokeTemplateProperty =
  794. [&]( INSPECTABLE* aItem ) -> bool
  795. {
  796. if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
  797. {
  798. if( shape->GetShape() == SHAPE_T::SEGMENT )
  799. return isPadEditMode( shape->GetBoard() );
  800. }
  801. return false;
  802. };
  803. const wxString groupPadPrimitives = _HKI( "Pad Primitives" );
  804. propMgr.AddProperty( new PROPERTY<PCB_SHAPE, bool>( _HKI( "Number Box" ),
  805. &PCB_SHAPE::SetIsProxyItem,
  806. &PCB_SHAPE::IsProxyItem ),
  807. groupPadPrimitives )
  808. .SetAvailableFunc( showNumberBoxProperty )
  809. .SetIsHiddenFromRulesEditor();
  810. propMgr.AddProperty( new PROPERTY<PCB_SHAPE, bool>( _HKI( "Thermal Spoke Template" ),
  811. &PCB_SHAPE::SetIsProxyItem,
  812. &PCB_SHAPE::IsProxyItem ),
  813. groupPadPrimitives )
  814. .SetAvailableFunc( showSpokeTemplateProperty )
  815. .SetIsHiddenFromRulesEditor();
  816. }
  817. } _PCB_SHAPE_DESC;