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.

1543 lines
40 KiB

14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
12 years ago
9 years ago
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
12 years ago
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
12 years ago
11 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) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 2015 Wayne Stambaugh <stambaughw@gmail.com>
  7. * Copyright (C) 1992-2020 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 <plotter.h>
  29. #include <trigo.h>
  30. #include <confirm.h>
  31. #include <kicad_string.h>
  32. #include <pcbnew.h>
  33. #include <refdes_utils.h>
  34. #include <richio.h>
  35. #include <filter_reader.h>
  36. #include <macros.h>
  37. #include <msgpanel.h>
  38. #include <bitmaps.h>
  39. #include <unordered_set>
  40. #include <pcb_edit_frame.h>
  41. #include <class_board.h>
  42. #include <class_edge_mod.h>
  43. #include <class_module.h>
  44. #include <convert_basic_shapes_to_polygon.h>
  45. #include <validators.h>
  46. #include <view/view.h>
  47. MODULE::MODULE( BOARD* parent ) :
  48. BOARD_ITEM_CONTAINER( (BOARD_ITEM*) parent, PCB_MODULE_T ),
  49. m_initial_comments( 0 )
  50. {
  51. m_Attributs = MOD_DEFAULT;
  52. m_Layer = F_Cu;
  53. m_Orient = 0;
  54. m_ModuleStatus = MODULE_PADS_LOCKED;
  55. m_arflag = 0;
  56. m_CntRot90 = m_CntRot180 = 0;
  57. m_Link = 0;
  58. m_LastEditTime = 0;
  59. m_LocalClearance = 0;
  60. m_LocalSolderMaskMargin = 0;
  61. m_LocalSolderPasteMargin = 0;
  62. m_LocalSolderPasteMarginRatio = 0.0;
  63. m_ZoneConnection = ZONE_CONNECTION::INHERITED; // Use zone setting by default
  64. m_ThermalWidth = 0; // Use zone setting by default
  65. m_ThermalGap = 0; // Use zone setting by default
  66. // These are special and mandatory text fields
  67. m_Reference = new TEXTE_MODULE( this, TEXTE_MODULE::TEXT_is_REFERENCE );
  68. m_Value = new TEXTE_MODULE( this, TEXTE_MODULE::TEXT_is_VALUE );
  69. m_3D_Drawings.clear();
  70. }
  71. MODULE::MODULE( const MODULE& aModule ) :
  72. BOARD_ITEM_CONTAINER( aModule )
  73. {
  74. m_Pos = aModule.m_Pos;
  75. m_fpid = aModule.m_fpid;
  76. m_Attributs = aModule.m_Attributs;
  77. m_ModuleStatus = aModule.m_ModuleStatus;
  78. m_Orient = aModule.m_Orient;
  79. m_BoundaryBox = aModule.m_BoundaryBox;
  80. m_CntRot90 = aModule.m_CntRot90;
  81. m_CntRot180 = aModule.m_CntRot180;
  82. m_LastEditTime = aModule.m_LastEditTime;
  83. m_Link = aModule.m_Link;
  84. m_Path = aModule.m_Path;
  85. m_LocalClearance = aModule.m_LocalClearance;
  86. m_LocalSolderMaskMargin = aModule.m_LocalSolderMaskMargin;
  87. m_LocalSolderPasteMargin = aModule.m_LocalSolderPasteMargin;
  88. m_LocalSolderPasteMarginRatio = aModule.m_LocalSolderPasteMarginRatio;
  89. m_ZoneConnection = aModule.m_ZoneConnection;
  90. m_ThermalWidth = aModule.m_ThermalWidth;
  91. m_ThermalGap = aModule.m_ThermalGap;
  92. // Copy reference and value.
  93. m_Reference = new TEXTE_MODULE( *aModule.m_Reference );
  94. m_Reference->SetParent( this );
  95. m_Value = new TEXTE_MODULE( *aModule.m_Value );
  96. m_Value->SetParent( this );
  97. // Copy auxiliary data: Pads
  98. for( auto pad : aModule.Pads() )
  99. {
  100. Add( new D_PAD( *pad ) );
  101. }
  102. // Copy auxiliary data: Zones
  103. for( auto item : aModule.Zones() )
  104. {
  105. Add( static_cast<MODULE_ZONE_CONTAINER*>( item->Clone() ) );
  106. // Ensure the net info is OK and especially uses the net info list
  107. // living in the current board
  108. // Needed when copying a fp from fp editor that has its own board
  109. // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
  110. item->SetNetCode( -1 );
  111. }
  112. // Copy auxiliary data: Drawings
  113. for( auto item : aModule.GraphicalItems() )
  114. {
  115. switch( item->Type() )
  116. {
  117. case PCB_MODULE_TEXT_T:
  118. case PCB_MODULE_EDGE_T:
  119. Add( static_cast<BOARD_ITEM*>( item->Clone() ) );
  120. break;
  121. default:
  122. wxLogMessage( wxT( "Class MODULE copy constructor internal error: unknown type" ) );
  123. break;
  124. }
  125. }
  126. // Copy auxiliary data: 3D_Drawings info
  127. m_3D_Drawings = aModule.m_3D_Drawings;
  128. m_Doc = aModule.m_Doc;
  129. m_KeyWord = aModule.m_KeyWord;
  130. m_arflag = 0;
  131. // Ensure auxiliary data is up to date
  132. CalculateBoundingBox();
  133. m_initial_comments = aModule.m_initial_comments ?
  134. new wxArrayString( *aModule.m_initial_comments ) : nullptr;
  135. }
  136. MODULE::~MODULE()
  137. {
  138. // Clean up the owned elements
  139. delete m_Reference;
  140. delete m_Value;
  141. delete m_initial_comments;
  142. for( auto p : m_pads )
  143. delete p;
  144. m_pads.clear();
  145. for( auto p : m_fp_zones )
  146. delete p;
  147. m_fp_zones.clear();
  148. for( auto d : m_drawings )
  149. delete d;
  150. m_drawings.clear();
  151. }
  152. MODULE& MODULE::operator=( const MODULE& aOther )
  153. {
  154. BOARD_ITEM::operator=( aOther );
  155. m_Pos = aOther.m_Pos;
  156. m_fpid = aOther.m_fpid;
  157. m_Attributs = aOther.m_Attributs;
  158. m_ModuleStatus = aOther.m_ModuleStatus;
  159. m_Orient = aOther.m_Orient;
  160. m_BoundaryBox = aOther.m_BoundaryBox;
  161. m_CntRot90 = aOther.m_CntRot90;
  162. m_CntRot180 = aOther.m_CntRot180;
  163. m_LastEditTime = aOther.m_LastEditTime;
  164. m_Link = aOther.m_Link;
  165. m_Path = aOther.m_Path;
  166. m_LocalClearance = aOther.m_LocalClearance;
  167. m_LocalSolderMaskMargin = aOther.m_LocalSolderMaskMargin;
  168. m_LocalSolderPasteMargin = aOther.m_LocalSolderPasteMargin;
  169. m_LocalSolderPasteMarginRatio = aOther.m_LocalSolderPasteMarginRatio;
  170. m_ZoneConnection = aOther.m_ZoneConnection;
  171. m_ThermalWidth = aOther.m_ThermalWidth;
  172. m_ThermalGap = aOther.m_ThermalGap;
  173. // Copy reference and value
  174. *m_Reference = *aOther.m_Reference;
  175. m_Reference->SetParent( this );
  176. *m_Value = *aOther.m_Value;
  177. m_Value->SetParent( this );
  178. // Copy auxiliary data: Pads
  179. m_pads.clear();
  180. for( auto pad : aOther.Pads() )
  181. {
  182. Add( new D_PAD( *pad ) );
  183. }
  184. // Copy auxiliary data: Zones
  185. m_fp_zones.clear();
  186. for( auto item : aOther.Zones() )
  187. {
  188. Add( static_cast<MODULE_ZONE_CONTAINER*>( item->Clone() ) );
  189. // Ensure the net info is OK and especially uses the net info list
  190. // living in the current board
  191. // Needed when copying a fp from fp editor that has its own board
  192. // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
  193. item->SetNetCode( -1 );
  194. }
  195. // Copy auxiliary data: Drawings
  196. m_drawings.clear();
  197. for( auto item : aOther.GraphicalItems() )
  198. {
  199. switch( item->Type() )
  200. {
  201. case PCB_MODULE_TEXT_T:
  202. case PCB_MODULE_EDGE_T:
  203. Add( static_cast<BOARD_ITEM*>( item->Clone() ) );
  204. break;
  205. default:
  206. wxLogMessage( wxT( "MODULE::operator=() internal error: unknown type" ) );
  207. break;
  208. }
  209. }
  210. // Copy auxiliary data: 3D_Drawings info
  211. m_3D_Drawings.clear();
  212. m_3D_Drawings = aOther.m_3D_Drawings;
  213. m_Doc = aOther.m_Doc;
  214. m_KeyWord = aOther.m_KeyWord;
  215. // Ensure auxiliary data is up to date
  216. CalculateBoundingBox();
  217. return *this;
  218. }
  219. bool MODULE::ResolveTextVar( wxString* token, int aDepth ) const
  220. {
  221. if( token->IsSameAs( wxT( "REFERENCE" ) ) )
  222. {
  223. *token = m_Reference->GetShownText( aDepth + 1 );
  224. return true;
  225. }
  226. else if( token->IsSameAs( wxT( "VALUE" ) ) )
  227. {
  228. *token = m_Value->GetShownText( aDepth + 1 );
  229. return true;
  230. }
  231. else if( token->IsSameAs( wxT( "LAYER" ) ) )
  232. {
  233. *token = GetLayerName();
  234. return true;
  235. }
  236. return false;
  237. }
  238. void MODULE::ClearAllNets()
  239. {
  240. // Force the ORPHANED dummy net info for all pads.
  241. // ORPHANED dummy net does not depend on a board
  242. for( auto pad : m_pads )
  243. pad->SetNetCode( NETINFO_LIST::ORPHANED );
  244. }
  245. void MODULE::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
  246. {
  247. switch( aBoardItem->Type() )
  248. {
  249. case PCB_MODULE_TEXT_T:
  250. // Only user text can be added this way.
  251. assert( static_cast<TEXTE_MODULE*>( aBoardItem )->GetType() == TEXTE_MODULE::TEXT_is_DIVERS );
  252. KI_FALLTHROUGH;
  253. case PCB_MODULE_EDGE_T:
  254. if( aMode == ADD_MODE::APPEND )
  255. m_drawings.push_back( aBoardItem );
  256. else
  257. m_drawings.push_front( aBoardItem );
  258. break;
  259. case PCB_PAD_T:
  260. if( aMode == ADD_MODE::APPEND )
  261. m_pads.push_back( static_cast<D_PAD*>( aBoardItem ) );
  262. else
  263. m_pads.push_front( static_cast<D_PAD*>( aBoardItem ) );
  264. break;
  265. case PCB_MODULE_ZONE_AREA_T:
  266. if( aMode == ADD_MODE::APPEND )
  267. m_fp_zones.push_back( static_cast<MODULE_ZONE_CONTAINER*>( aBoardItem ) );
  268. else
  269. m_fp_zones.insert( m_fp_zones.begin(), static_cast<MODULE_ZONE_CONTAINER*>( aBoardItem ) );
  270. break;
  271. default:
  272. {
  273. wxString msg;
  274. msg.Printf( wxT( "MODULE::Add() needs work: BOARD_ITEM type (%d) not handled" ),
  275. aBoardItem->Type() );
  276. wxFAIL_MSG( msg );
  277. return;
  278. }
  279. }
  280. aBoardItem->ClearEditFlags();
  281. aBoardItem->SetParent( this );
  282. }
  283. void MODULE::Remove( BOARD_ITEM* aBoardItem )
  284. {
  285. switch( aBoardItem->Type() )
  286. {
  287. case PCB_MODULE_TEXT_T:
  288. // Only user text can be removed this way.
  289. wxCHECK_RET(
  290. static_cast<TEXTE_MODULE*>( aBoardItem )->GetType() == TEXTE_MODULE::TEXT_is_DIVERS,
  291. "Please report this bug: Invalid remove operation on required text" );
  292. KI_FALLTHROUGH;
  293. case PCB_MODULE_EDGE_T:
  294. for( auto it = m_drawings.begin(); it != m_drawings.end(); ++it )
  295. {
  296. if( *it == aBoardItem )
  297. {
  298. m_drawings.erase( it );
  299. break;
  300. }
  301. }
  302. break;
  303. case PCB_PAD_T:
  304. for( auto it = m_pads.begin(); it != m_pads.end(); ++it )
  305. {
  306. if( *it == static_cast<D_PAD*>( aBoardItem ) )
  307. {
  308. m_pads.erase( it );
  309. break;
  310. }
  311. }
  312. break;
  313. case PCB_MODULE_ZONE_AREA_T:
  314. for( auto it = m_fp_zones.begin(); it != m_fp_zones.end(); ++it )
  315. {
  316. if( *it == static_cast<MODULE_ZONE_CONTAINER*>( aBoardItem ) )
  317. {
  318. m_fp_zones.erase( it );
  319. break;
  320. }
  321. }
  322. break;
  323. default:
  324. {
  325. wxString msg;
  326. msg.Printf( wxT( "MODULE::Remove() needs work: BOARD_ITEM type (%d) not handled" ),
  327. aBoardItem->Type() );
  328. wxFAIL_MSG( msg );
  329. }
  330. }
  331. }
  332. void MODULE::CalculateBoundingBox()
  333. {
  334. m_BoundaryBox = GetFootprintRect();
  335. }
  336. double MODULE::GetArea( int aPadding ) const
  337. {
  338. double w = std::abs( m_BoundaryBox.GetWidth() ) + aPadding;
  339. double h = std::abs( m_BoundaryBox.GetHeight() ) + aPadding;
  340. return w * h;
  341. }
  342. EDA_RECT MODULE::GetFootprintRect() const
  343. {
  344. EDA_RECT area;
  345. area.SetOrigin( m_Pos );
  346. area.SetEnd( m_Pos );
  347. area.Inflate( Millimeter2iu( 0.25 ) ); // Give a min size to the area
  348. for( auto item : m_drawings )
  349. {
  350. if( item->Type() == PCB_MODULE_EDGE_T )
  351. area.Merge( item->GetBoundingBox() );
  352. }
  353. for( auto pad : m_pads )
  354. area.Merge( pad->GetBoundingBox() );
  355. for( auto zone : m_fp_zones )
  356. area.Merge( zone->GetBoundingBox() );
  357. return area;
  358. }
  359. EDA_RECT MODULE::GetFpPadsLocalBbox() const
  360. {
  361. EDA_RECT area;
  362. // We want the bounding box of the footprint pads at rot 0, not flipped
  363. // Create such a image:
  364. MODULE dummy( *this );
  365. dummy.SetPosition( wxPoint( 0, 0 ) );
  366. if( dummy.IsFlipped() )
  367. dummy.Flip( wxPoint( 0, 0 ) , false );
  368. if( dummy.GetOrientation() )
  369. dummy.SetOrientation( 0 );
  370. for( auto pad : dummy.Pads() )
  371. area.Merge( pad->GetBoundingBox() );
  372. return area;
  373. }
  374. const EDA_RECT MODULE::GetBoundingBox() const
  375. {
  376. EDA_RECT area = GetFootprintRect();
  377. // Add in items not collected by GetFootprintRect():
  378. for( auto item : m_drawings )
  379. {
  380. if( item->Type() != PCB_MODULE_EDGE_T )
  381. area.Merge( item->GetBoundingBox() );
  382. }
  383. area.Merge( m_Value->GetBoundingBox() );
  384. area.Merge( m_Reference->GetBoundingBox() );
  385. return area;
  386. }
  387. const EDA_RECT MODULE::GetBoundingBox( bool aIncludeInvisibleText ) const
  388. {
  389. EDA_RECT area = GetFootprintRect();
  390. // Add in items not collected by GetFootprintRect():
  391. for( auto item : m_drawings )
  392. {
  393. if( item->Type() != PCB_MODULE_EDGE_T )
  394. area.Merge( item->GetBoundingBox() );
  395. }
  396. if( m_Value->IsVisible() || aIncludeInvisibleText )
  397. area.Merge( m_Value->GetBoundingBox() );
  398. if( m_Reference->IsVisible() || aIncludeInvisibleText )
  399. area.Merge( m_Reference->GetBoundingBox() );
  400. return area;
  401. }
  402. /**
  403. * This is a bit hacky right now for performance reasons.
  404. *
  405. * We assume that most footprints will have features aligned to the axes in
  406. * the zero-rotation state. Therefore, if the footprint is rotated, we
  407. * temporarily rotate back to zero, get the bounding box (excluding reference
  408. * and value text) and then rotate the resulting poly back to the correct
  409. * orientation.
  410. *
  411. * This is more accurate than using the AABB when most footprints are rotated
  412. * off of the axes, but less accurate than computing some kind of bounding hull.
  413. * We should consider doing that instead at some point in the future if we can
  414. * use a performant algorithm and cache the result to avoid extra computing.
  415. */
  416. SHAPE_POLY_SET MODULE::GetBoundingPoly() const
  417. {
  418. SHAPE_POLY_SET poly;
  419. double orientation = GetOrientationRadians();
  420. MODULE temp = *this;
  421. temp.SetOrientation( 0.0 );
  422. BOX2I area = temp.GetFootprintRect();
  423. poly.NewOutline();
  424. VECTOR2I p = area.GetPosition();
  425. poly.Append( p );
  426. p.x = area.GetRight();
  427. poly.Append( p );
  428. p.y = area.GetBottom();
  429. poly.Append( p );
  430. p.x = area.GetX();
  431. poly.Append( p );
  432. BOARD* board = GetBoard();
  433. if( board )
  434. {
  435. int biggest_clearance = board->GetDesignSettings().GetBiggestClearanceValue();
  436. poly.Inflate( biggest_clearance, 4 );
  437. }
  438. poly.Inflate( Millimeter2iu( 0.01 ), 4 );
  439. poly.Rotate( -orientation, m_Pos );
  440. return poly;
  441. }
  442. void MODULE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  443. {
  444. wxString msg;
  445. aList.emplace_back( MSG_PANEL_ITEM( m_Reference->GetShownText(), m_Value->GetShownText(), DARKCYAN ) );
  446. // Display last date the component was edited (useful in Module Editor).
  447. wxDateTime date( static_cast<time_t>( m_LastEditTime ) );
  448. if( m_LastEditTime && date.IsValid() )
  449. // Date format: see http://www.cplusplus.com/reference/ctime/strftime
  450. msg = date.Format( wxT( "%b %d, %Y" ) ); // Abbreviated_month_name Day, Year
  451. else
  452. msg = _( "Unknown" );
  453. aList.emplace_back( MSG_PANEL_ITEM( _( "Last Change" ), msg, BROWN ) );
  454. // display the board side placement
  455. aList.emplace_back( MSG_PANEL_ITEM( _( "Board Side" ),
  456. IsFlipped()? _( "Back (Flipped)" ) : _( "Front" ), RED ) );
  457. msg.Printf( wxT( "%zu" ), m_pads.size() );
  458. aList.emplace_back( MSG_PANEL_ITEM( _( "Pads" ), msg, BLUE ) );
  459. msg = wxT( ".." );
  460. if( IsLocked() )
  461. msg[0] = 'L';
  462. if( m_ModuleStatus & MODULE_is_PLACED )
  463. msg[1] = 'P';
  464. aList.emplace_back( MSG_PANEL_ITEM( _( "Status" ), msg, MAGENTA ) );
  465. msg.Printf( wxT( "%.1f" ), GetOrientationDegrees() );
  466. aList.emplace_back( MSG_PANEL_ITEM( _( "Rotation" ), msg, BROWN ) );
  467. // Controls on right side of the dialog
  468. switch( m_Attributs & 255 )
  469. {
  470. case 0:
  471. msg = _( "Normal" );
  472. break;
  473. case MOD_CMS:
  474. msg = _( "Insert" );
  475. break;
  476. case MOD_VIRTUAL:
  477. msg = _( "Virtual" );
  478. break;
  479. default:
  480. msg = wxT( "???" );
  481. break;
  482. }
  483. aList.emplace_back( MSG_PANEL_ITEM( _( "Attributes" ), msg, BROWN ) );
  484. aList.emplace_back( MSG_PANEL_ITEM( _( "Footprint" ), FROM_UTF8( m_fpid.Format().c_str() ), BLUE ) );
  485. if( m_3D_Drawings.empty() )
  486. msg = _( "No 3D shape" );
  487. else
  488. msg = m_3D_Drawings.front().m_Filename;
  489. // Search the first active 3D shape in list
  490. aList.emplace_back( MSG_PANEL_ITEM( _( "3D-Shape" ), msg, RED ) );
  491. wxString doc, keyword;
  492. doc.Printf( _( "Doc: %s" ), m_Doc );
  493. keyword.Printf( _( "Key Words: %s" ), m_KeyWord );
  494. aList.emplace_back( MSG_PANEL_ITEM( doc, keyword, BLACK ) );
  495. }
  496. bool MODULE::HitTest( const wxPoint& aPosition, int aAccuracy ) const
  497. {
  498. EDA_RECT rect = m_BoundaryBox;
  499. return rect.Inflate( aAccuracy ).Contains( aPosition );
  500. }
  501. bool MODULE::HitTestAccurate( const wxPoint& aPosition, int aAccuracy ) const
  502. {
  503. return GetBoundingPoly().Collide( aPosition, aAccuracy );
  504. }
  505. bool MODULE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
  506. {
  507. EDA_RECT arect = aRect;
  508. arect.Inflate( aAccuracy );
  509. if( aContained )
  510. return arect.Contains( m_BoundaryBox );
  511. else
  512. {
  513. // If the rect does not intersect the bounding box, skip any tests
  514. if( !aRect.Intersects( GetBoundingBox() ) )
  515. return false;
  516. // Determine if any elements in the MODULE intersect the rect
  517. for( auto pad : m_pads )
  518. {
  519. if( pad->HitTest( arect, false, 0 ) )
  520. return true;
  521. }
  522. for( auto zone : m_fp_zones )
  523. {
  524. if( zone->HitTest( arect, false, 0 ) )
  525. return true;
  526. }
  527. for( auto item : m_drawings )
  528. {
  529. if( item->HitTest( arect, false, 0 ) )
  530. return true;
  531. }
  532. // No items were hit
  533. return false;
  534. }
  535. }
  536. D_PAD* MODULE::FindPadByName( const wxString& aPadName ) const
  537. {
  538. for( auto pad : m_pads )
  539. {
  540. if( pad->GetName() == aPadName )
  541. return pad;
  542. }
  543. return NULL;
  544. }
  545. D_PAD* MODULE::GetPad( const wxPoint& aPosition, LSET aLayerMask )
  546. {
  547. for( auto pad : m_pads )
  548. {
  549. // ... and on the correct layer.
  550. if( !( pad->GetLayerSet() & aLayerMask ).any() )
  551. continue;
  552. if( pad->HitTest( aPosition ) )
  553. return pad;
  554. }
  555. return NULL;
  556. }
  557. D_PAD* MODULE::GetTopLeftPad()
  558. {
  559. D_PAD* topLeftPad = GetFirstPad();
  560. for( auto p : m_pads )
  561. {
  562. wxPoint pnt = p->GetPosition(); // GetPosition() returns the center of the pad
  563. if( ( pnt.x < topLeftPad->GetPosition().x ) ||
  564. ( ( topLeftPad->GetPosition().x == pnt.x ) &&
  565. ( pnt.y < topLeftPad->GetPosition().y ) ) )
  566. {
  567. topLeftPad = p;
  568. }
  569. }
  570. return topLeftPad;
  571. }
  572. unsigned MODULE::GetPadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
  573. {
  574. if( aIncludeNPTH )
  575. return m_pads.size();
  576. unsigned cnt = 0;
  577. for( auto pad : m_pads )
  578. {
  579. if( pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
  580. continue;
  581. cnt++;
  582. }
  583. return cnt;
  584. }
  585. unsigned MODULE::GetUniquePadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
  586. {
  587. std::set<wxString> usedNames;
  588. // Create a set of used pad numbers
  589. for( auto pad : m_pads )
  590. {
  591. // Skip pads not on copper layers (used to build complex
  592. // solder paste shapes for instance)
  593. if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none() )
  594. continue;
  595. // Skip pads with no name, because they are usually "mechanical"
  596. // pads, not "electrical" pads
  597. if( pad->GetName().IsEmpty() )
  598. continue;
  599. if( !aIncludeNPTH )
  600. {
  601. // skip NPTH
  602. if( pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
  603. {
  604. continue;
  605. }
  606. }
  607. usedNames.insert( pad->GetName() );
  608. }
  609. return usedNames.size();
  610. }
  611. void MODULE::Add3DModel( MODULE_3D_SETTINGS* a3DModel )
  612. {
  613. if( NULL == a3DModel )
  614. return;
  615. if( !a3DModel->m_Filename.empty() )
  616. m_3D_Drawings.push_back( *a3DModel );
  617. delete a3DModel;
  618. }
  619. // see class_module.h
  620. SEARCH_RESULT MODULE::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] )
  621. {
  622. KICAD_T stype;
  623. SEARCH_RESULT result = SEARCH_RESULT::CONTINUE;
  624. const KICAD_T* p = scanTypes;
  625. bool done = false;
  626. #if 0 && defined(DEBUG)
  627. std::cout << GetClass().mb_str() << ' ';
  628. #endif
  629. while( !done )
  630. {
  631. stype = *p;
  632. switch( stype )
  633. {
  634. case PCB_MODULE_T:
  635. result = inspector( this, testData ); // inspect me
  636. ++p;
  637. break;
  638. case PCB_PAD_T:
  639. result = IterateForward<D_PAD*>( m_pads, inspector, testData, p );
  640. ++p;
  641. break;
  642. case PCB_MODULE_ZONE_AREA_T:
  643. result = IterateForward<MODULE_ZONE_CONTAINER*>( m_fp_zones, inspector, testData, p );
  644. ++p;
  645. break;
  646. case PCB_MODULE_TEXT_T:
  647. result = inspector( m_Reference, testData );
  648. if( result == SEARCH_RESULT::QUIT )
  649. break;
  650. result = inspector( m_Value, testData );
  651. if( result == SEARCH_RESULT::QUIT )
  652. break;
  653. // Intentionally fall through since m_Drawings can hold TYPETEXTMODULE also
  654. KI_FALLTHROUGH;
  655. case PCB_MODULE_EDGE_T:
  656. result = IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, p );
  657. // skip over any types handled in the above call.
  658. for( ; ; )
  659. {
  660. switch( stype = *++p )
  661. {
  662. case PCB_MODULE_TEXT_T:
  663. case PCB_MODULE_EDGE_T:
  664. continue;
  665. default:
  666. ;
  667. }
  668. break;
  669. }
  670. break;
  671. default:
  672. done = true;
  673. break;
  674. }
  675. if( result == SEARCH_RESULT::QUIT )
  676. break;
  677. }
  678. return result;
  679. }
  680. wxString MODULE::GetSelectMenuText( EDA_UNITS aUnits ) const
  681. {
  682. wxString reference = GetReference();
  683. if( reference.IsEmpty() )
  684. reference = _( "<no reference designator>" );
  685. return wxString::Format( _( "Footprint %s on %s" ), reference, GetLayerName() );
  686. }
  687. BITMAP_DEF MODULE::GetMenuImage() const
  688. {
  689. return module_xpm;
  690. }
  691. EDA_ITEM* MODULE::Clone() const
  692. {
  693. return new MODULE( *this );
  694. }
  695. void MODULE::RunOnChildren( const std::function<void (BOARD_ITEM*)>& aFunction )
  696. {
  697. try
  698. {
  699. for( auto pad : m_pads )
  700. aFunction( static_cast<BOARD_ITEM*>( pad ) );
  701. for( auto zone : m_fp_zones )
  702. aFunction( static_cast<MODULE_ZONE_CONTAINER*>( zone ) );
  703. for( auto drawing : m_drawings )
  704. aFunction( static_cast<BOARD_ITEM*>( drawing ) );
  705. aFunction( static_cast<BOARD_ITEM*>( m_Reference ) );
  706. aFunction( static_cast<BOARD_ITEM*>( m_Value ) );
  707. }
  708. catch( std::bad_function_call& )
  709. {
  710. DisplayError( NULL, wxT( "Error running MODULE::RunOnChildren" ) );
  711. }
  712. }
  713. void MODULE::GetAllDrawingLayers( int aLayers[], int& aCount, bool aIncludePads ) const
  714. {
  715. std::unordered_set<int> layers;
  716. for( auto item : m_drawings )
  717. {
  718. layers.insert( static_cast<int>( item->GetLayer() ) );
  719. }
  720. if( aIncludePads )
  721. {
  722. for( auto pad : m_pads )
  723. {
  724. int pad_layers[KIGFX::VIEW::VIEW_MAX_LAYERS], pad_layers_count;
  725. pad->ViewGetLayers( pad_layers, pad_layers_count );
  726. for( int i = 0; i < pad_layers_count; i++ )
  727. layers.insert( pad_layers[i] );
  728. }
  729. }
  730. aCount = layers.size();
  731. int i = 0;
  732. for( auto layer : layers )
  733. aLayers[i++] = layer;
  734. }
  735. void MODULE::ViewGetLayers( int aLayers[], int& aCount ) const
  736. {
  737. aCount = 2;
  738. aLayers[0] = LAYER_ANCHOR;
  739. switch( m_Layer )
  740. {
  741. default:
  742. wxASSERT_MSG( false, "Illegal layer" ); // do you really have modules placed on other layers?
  743. KI_FALLTHROUGH;
  744. case F_Cu:
  745. aLayers[1] = LAYER_MOD_FR;
  746. break;
  747. case B_Cu:
  748. aLayers[1] = LAYER_MOD_BK;
  749. break;
  750. }
  751. // If there are no pads, and only drawings on a silkscreen layer, then
  752. // report the silkscreen layer as well so that the component can be edited
  753. // with the silkscreen layer
  754. bool f_silk = false, b_silk = false, non_silk = false;
  755. for( auto item : m_drawings )
  756. {
  757. if( item->GetLayer() == F_SilkS )
  758. f_silk = true;
  759. else if( item->GetLayer() == B_SilkS )
  760. b_silk = true;
  761. else
  762. non_silk = true;
  763. }
  764. if( ( f_silk || b_silk ) && !non_silk && m_pads.empty() )
  765. {
  766. if( f_silk )
  767. aLayers[ aCount++ ] = F_SilkS;
  768. if( b_silk )
  769. aLayers[ aCount++ ] = B_SilkS;
  770. }
  771. }
  772. unsigned int MODULE::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  773. {
  774. int layer = ( m_Layer == F_Cu ) ? LAYER_MOD_FR :
  775. ( m_Layer == B_Cu ) ? LAYER_MOD_BK : LAYER_ANCHOR;
  776. // Currently it is only for anchor layer
  777. if( aView->IsLayerVisible( layer ) )
  778. return 3;
  779. return std::numeric_limits<unsigned int>::max();
  780. }
  781. const BOX2I MODULE::ViewBBox() const
  782. {
  783. EDA_RECT area = GetFootprintRect();
  784. // Calculate extended area including text fields
  785. area.Merge( m_Reference->GetBoundingBox() );
  786. area.Merge( m_Value->GetBoundingBox() );
  787. // Add the Clearance shape size: (shape around the pads when the
  788. // clearance is shown. Not optimized, but the draw cost is small
  789. // (perhaps smaller than optimization).
  790. BOARD* board = GetBoard();
  791. if( board )
  792. {
  793. int biggest_clearance = board->GetDesignSettings().GetBiggestClearanceValue();
  794. area.Inflate( biggest_clearance );
  795. }
  796. return area;
  797. }
  798. bool MODULE::IsLibNameValid( const wxString & aName )
  799. {
  800. const wxChar * invalids = StringLibNameInvalidChars( false );
  801. if( aName.find_first_of( invalids ) != std::string::npos )
  802. return false;
  803. return true;
  804. }
  805. const wxChar* MODULE::StringLibNameInvalidChars( bool aUserReadable )
  806. {
  807. // This list of characters is also duplicated in validators.cpp and
  808. // lib_id.cpp
  809. // TODO: Unify forbidden character lists
  810. static const wxChar invalidChars[] = wxT("%$<>\t\n\r\"\\/:");
  811. static const wxChar invalidCharsReadable[] = wxT("% $ < > 'tab' 'return' 'line feed' \\ \" / :");
  812. if( aUserReadable )
  813. return invalidCharsReadable;
  814. else
  815. return invalidChars;
  816. }
  817. void MODULE::Move( const wxPoint& aMoveVector )
  818. {
  819. wxPoint newpos = m_Pos + aMoveVector;
  820. SetPosition( newpos );
  821. }
  822. void MODULE::Rotate( const wxPoint& aRotCentre, double aAngle )
  823. {
  824. double orientation = GetOrientation();
  825. double newOrientation = orientation + aAngle;
  826. wxPoint newpos = m_Pos;
  827. RotatePoint( &newpos, aRotCentre, aAngle );
  828. SetPosition( newpos );
  829. SetOrientation( newOrientation );
  830. m_Reference->KeepUpright( orientation, newOrientation );
  831. m_Value->KeepUpright( orientation, newOrientation );
  832. for( auto item : m_drawings )
  833. {
  834. if( item->Type() == PCB_MODULE_TEXT_T )
  835. static_cast<TEXTE_MODULE*>( item )->KeepUpright( orientation, newOrientation );
  836. }
  837. }
  838. void MODULE::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
  839. {
  840. // Move module to its final position:
  841. wxPoint finalPos = m_Pos;
  842. // Now Flip the footprint.
  843. // Flipping a footprint is a specific transform:
  844. // it is not mirrored like a text.
  845. // We have to change the side, and ensure the footprint rotation is
  846. // modified accordint to the transform, because this parameter is used
  847. // in pick and place files, and when updating the footprint from library.
  848. // When flipped around the X axis (Y coordinates changed) orientation is negated
  849. // When flipped around the Y axis (X coordinates changed) orientation is 180 - old orient.
  850. // Because it is specfic to a footprint, we flip around the X axis, and after rotate 180 deg
  851. MIRROR( finalPos.y, aCentre.y ); /// Mirror the Y position (around the X axis)
  852. SetPosition( finalPos );
  853. // Flip layer
  854. SetLayer( FlipLayer( GetLayer() ) );
  855. // Reverse mirror orientation.
  856. m_Orient = -m_Orient;
  857. NORMALIZE_ANGLE_180( m_Orient );
  858. // Mirror pads to other side of board.
  859. for( auto pad : m_pads )
  860. pad->Flip( m_Pos, false );
  861. // Mirror zones to other side of board.
  862. for( auto zone : m_fp_zones )
  863. zone->Flip( m_Pos, aFlipLeftRight );
  864. // Mirror reference and value.
  865. m_Reference->Flip( m_Pos, false );
  866. m_Value->Flip( m_Pos, false );
  867. // Reverse mirror module graphics and texts.
  868. for( auto item : m_drawings )
  869. {
  870. switch( item->Type() )
  871. {
  872. case PCB_MODULE_EDGE_T:
  873. static_cast<EDGE_MODULE*>( item )->Flip( m_Pos, false );
  874. break;
  875. case PCB_MODULE_TEXT_T:
  876. static_cast<TEXTE_MODULE*>( item )->Flip( m_Pos, false );
  877. break;
  878. default:
  879. wxMessageBox( wxT( "MODULE::Flip() error: Unknown Draw Type" ) );
  880. break;
  881. }
  882. }
  883. // Now rotate 180 deg if required
  884. if( aFlipLeftRight )
  885. Rotate( aCentre, 1800.0 );
  886. CalculateBoundingBox();
  887. }
  888. void MODULE::SetPosition( const wxPoint& newpos )
  889. {
  890. wxPoint delta = newpos - m_Pos;
  891. m_Pos += delta;
  892. m_Reference->EDA_TEXT::Offset( delta );
  893. m_Value->EDA_TEXT::Offset( delta );
  894. for( auto pad : m_pads )
  895. {
  896. pad->SetPosition( pad->GetPosition() + delta );
  897. }
  898. for( auto zone : m_fp_zones )
  899. zone->Move( delta );
  900. for( auto item : m_drawings )
  901. {
  902. switch( item->Type() )
  903. {
  904. case PCB_MODULE_EDGE_T:
  905. {
  906. EDGE_MODULE* pt_edgmod = (EDGE_MODULE*) item;
  907. pt_edgmod->SetDrawCoord();
  908. break;
  909. }
  910. case PCB_MODULE_TEXT_T:
  911. {
  912. TEXTE_MODULE* text = static_cast<TEXTE_MODULE*>( item );
  913. text->EDA_TEXT::Offset( delta );
  914. break;
  915. }
  916. default:
  917. wxMessageBox( wxT( "Draw type undefined." ) );
  918. break;
  919. }
  920. }
  921. CalculateBoundingBox();
  922. }
  923. void MODULE::MoveAnchorPosition( const wxPoint& aMoveVector )
  924. {
  925. /* Move the reference point of the footprint
  926. * the footprints elements (pads, outlines, edges .. ) are moved
  927. * but:
  928. * - the footprint position is not modified.
  929. * - the relative (local) coordinates of these items are modified
  930. * - Draw coordinates are updated
  931. */
  932. // Update (move) the relative coordinates relative to the new anchor point.
  933. wxPoint moveVector = aMoveVector;
  934. RotatePoint( &moveVector, -GetOrientation() );
  935. // Update of the reference and value.
  936. m_Reference->SetPos0( m_Reference->GetPos0() + moveVector );
  937. m_Reference->SetDrawCoord();
  938. m_Value->SetPos0( m_Value->GetPos0() + moveVector );
  939. m_Value->SetDrawCoord();
  940. // Update the pad local coordinates.
  941. for( auto pad : m_pads )
  942. {
  943. pad->SetPos0( pad->GetPos0() + moveVector );
  944. pad->SetDrawCoord();
  945. }
  946. // Update the draw element coordinates.
  947. for( auto item : GraphicalItems() )
  948. {
  949. switch( item->Type() )
  950. {
  951. case PCB_MODULE_EDGE_T:
  952. {
  953. EDGE_MODULE* edge = static_cast<EDGE_MODULE*>( item );
  954. edge->Move( moveVector );
  955. }
  956. break;
  957. case PCB_MODULE_TEXT_T:
  958. {
  959. TEXTE_MODULE* text = static_cast<TEXTE_MODULE*>( item );
  960. text->SetPos0( text->GetPos0() + moveVector );
  961. text->SetDrawCoord();
  962. }
  963. break;
  964. default:
  965. break;
  966. }
  967. }
  968. CalculateBoundingBox();
  969. }
  970. void MODULE::SetOrientation( double newangle )
  971. {
  972. double angleChange = newangle - m_Orient; // change in rotation
  973. NORMALIZE_ANGLE_180( newangle );
  974. m_Orient = newangle;
  975. for( auto pad : m_pads )
  976. {
  977. pad->SetOrientation( pad->GetOrientation() + angleChange );
  978. pad->SetDrawCoord();
  979. }
  980. for( auto zone : m_fp_zones )
  981. {
  982. zone->Rotate( GetPosition(), angleChange );
  983. }
  984. // Update of the reference and value.
  985. m_Reference->SetDrawCoord();
  986. m_Value->SetDrawCoord();
  987. // Displace contours and text of the footprint.
  988. for( auto item : m_drawings )
  989. {
  990. if( item->Type() == PCB_MODULE_EDGE_T )
  991. {
  992. static_cast<EDGE_MODULE*>( item )->SetDrawCoord();
  993. }
  994. else if( item->Type() == PCB_MODULE_TEXT_T )
  995. {
  996. static_cast<TEXTE_MODULE*>( item )->SetDrawCoord();
  997. }
  998. }
  999. CalculateBoundingBox();
  1000. }
  1001. BOARD_ITEM* MODULE::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToModule )
  1002. {
  1003. BOARD_ITEM* new_item = NULL;
  1004. MODULE_ZONE_CONTAINER* new_zone = NULL;
  1005. switch( aItem->Type() )
  1006. {
  1007. case PCB_PAD_T:
  1008. {
  1009. D_PAD* new_pad = new D_PAD( *static_cast<const D_PAD*>( aItem ) );
  1010. if( aAddToModule )
  1011. m_pads.push_back( new_pad );
  1012. new_item = new_pad;
  1013. break;
  1014. }
  1015. case PCB_MODULE_ZONE_AREA_T:
  1016. {
  1017. new_zone = new MODULE_ZONE_CONTAINER( *static_cast<const MODULE_ZONE_CONTAINER*>( aItem ) );
  1018. if( aAddToModule )
  1019. m_fp_zones.push_back( new_zone );
  1020. new_item = new_zone;
  1021. break;
  1022. }
  1023. case PCB_MODULE_TEXT_T:
  1024. {
  1025. TEXTE_MODULE* new_text = new TEXTE_MODULE( *static_cast<const TEXTE_MODULE*>( aItem ) );
  1026. if( new_text->GetType() == TEXTE_MODULE::TEXT_is_REFERENCE )
  1027. {
  1028. new_text->SetText( wxT( "${REFERENCE}" ) );
  1029. new_text->SetType( TEXTE_MODULE::TEXT_is_DIVERS );
  1030. }
  1031. else if( new_text->GetType() == TEXTE_MODULE::TEXT_is_VALUE )
  1032. {
  1033. new_text->SetText( wxT( "${VALUE}" ) );
  1034. new_text->SetType( TEXTE_MODULE::TEXT_is_DIVERS );
  1035. }
  1036. if( aAddToModule )
  1037. Add( new_text );
  1038. new_item = new_text;
  1039. break;
  1040. }
  1041. case PCB_MODULE_EDGE_T:
  1042. {
  1043. EDGE_MODULE* new_edge = new EDGE_MODULE( *static_cast<const EDGE_MODULE*>(aItem) );
  1044. if( aAddToModule )
  1045. Add( new_edge );
  1046. new_item = new_edge;
  1047. break;
  1048. }
  1049. case PCB_MODULE_T:
  1050. // Ignore the module itself
  1051. break;
  1052. default:
  1053. // Un-handled item for duplication
  1054. wxFAIL_MSG( "Duplication not supported for items of class " + aItem->GetClass() );
  1055. break;
  1056. }
  1057. return new_item;
  1058. }
  1059. wxString MODULE::GetNextPadName( const wxString& aLastPadName ) const
  1060. {
  1061. std::set<wxString> usedNames;
  1062. // Create a set of used pad numbers
  1063. for( D_PAD* pad : m_pads )
  1064. usedNames.insert( pad->GetName() );
  1065. wxString prefix = UTIL::GetReferencePrefix( aLastPadName );
  1066. int num = GetTrailingInt( aLastPadName );
  1067. while( usedNames.count( wxString::Format( "%s%d", prefix, num ) ) )
  1068. num++;
  1069. return wxString::Format( "%s%d", prefix, num );
  1070. }
  1071. void MODULE::IncrementReference( int aDelta )
  1072. {
  1073. const auto& refdes = GetReference();
  1074. SetReference( wxString::Format( wxT( "%s%i" ), UTIL::GetReferencePrefix( refdes ),
  1075. GetTrailingInt( refdes ) + aDelta ) );
  1076. }
  1077. // Calculate the area of aPolySet, after fracturation, because
  1078. // polygons with no hole are expected.
  1079. static double polygonArea( SHAPE_POLY_SET& aPolySet )
  1080. {
  1081. double area = 0.0;
  1082. for( int ii = 0; ii < aPolySet.OutlineCount(); ii++ )
  1083. {
  1084. SHAPE_LINE_CHAIN& outline = aPolySet.Outline( ii );
  1085. // Ensure the curr outline is closed, to calculate area
  1086. outline.SetClosed( true );
  1087. area += outline.Area();
  1088. }
  1089. return area;
  1090. }
  1091. // a helper function to add a rectangular polygon aRect to aPolySet
  1092. static void addRect( SHAPE_POLY_SET& aPolySet, wxRect aRect )
  1093. {
  1094. aPolySet.NewOutline();
  1095. aPolySet.Append( aRect.GetX(), aRect.GetY() );
  1096. aPolySet.Append( aRect.GetX()+aRect.width, aRect.GetY() );
  1097. aPolySet.Append( aRect.GetX()+aRect.width, aRect.GetY()+aRect.height );
  1098. aPolySet.Append( aRect.GetX(), aRect.GetY()+aRect.height );
  1099. }
  1100. double MODULE::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
  1101. {
  1102. double moduleArea = GetFootprintRect().GetArea();
  1103. SHAPE_POLY_SET coveredRegion;
  1104. addRect( coveredRegion, GetFootprintRect() );
  1105. // build list of holes (covered areas not available for selection)
  1106. SHAPE_POLY_SET holes;
  1107. for( auto pad : m_pads )
  1108. addRect( holes, pad->GetBoundingBox() );
  1109. addRect( holes, m_Reference->GetBoundingBox() );
  1110. addRect( holes, m_Value->GetBoundingBox() );
  1111. for( int i = 0; i < aCollector.GetCount(); ++i )
  1112. {
  1113. BOARD_ITEM* item = aCollector[i];
  1114. switch( item->Type() )
  1115. {
  1116. case PCB_TEXT_T:
  1117. case PCB_MODULE_TEXT_T:
  1118. case PCB_TRACE_T:
  1119. case PCB_ARC_T:
  1120. case PCB_VIA_T:
  1121. addRect( holes, item->GetBoundingBox() );
  1122. break;
  1123. default:
  1124. break;
  1125. }
  1126. }
  1127. SHAPE_POLY_SET uncoveredRegion;
  1128. try
  1129. {
  1130. uncoveredRegion.BooleanSubtract( coveredRegion, holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  1131. uncoveredRegion.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  1132. uncoveredRegion.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  1133. }
  1134. catch( ClipperLib::clipperException& )
  1135. {
  1136. // better to be conservative (this will result in the disambiguate dialog)
  1137. return 1.0;
  1138. }
  1139. double uncoveredRegionArea = polygonArea( uncoveredRegion );
  1140. double coveredArea = moduleArea - uncoveredRegionArea;
  1141. double ratio = ( coveredArea / moduleArea );
  1142. return std::min( ratio, 1.0 );
  1143. }
  1144. // see convert_drawsegment_list_to_polygon.cpp:
  1145. extern bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SET& aPolygons,
  1146. wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation = nullptr );
  1147. bool MODULE::BuildPolyCourtyard()
  1148. {
  1149. m_poly_courtyard_front.RemoveAllContours();
  1150. m_poly_courtyard_back.RemoveAllContours();
  1151. // Build the courtyard area from graphic items on the courtyard.
  1152. // Only PCB_MODULE_EDGE_T have meaning, graphic texts are ignored.
  1153. // Collect items:
  1154. std::vector< DRAWSEGMENT* > list_front;
  1155. std::vector< DRAWSEGMENT* > list_back;
  1156. for( auto item : GraphicalItems() )
  1157. {
  1158. if( item->GetLayer() == B_CrtYd && item->Type() == PCB_MODULE_EDGE_T )
  1159. list_back.push_back( static_cast< DRAWSEGMENT* > ( item ) );
  1160. if( item->GetLayer() == F_CrtYd && item->Type() == PCB_MODULE_EDGE_T )
  1161. list_front.push_back( static_cast< DRAWSEGMENT* > ( item ) );
  1162. }
  1163. // Note: if no item found on courtyard layers, return true.
  1164. // false is returned only when the shape defined on courtyard layers
  1165. // is not convertible to a polygon
  1166. if( !list_front.size() && !list_back.size() )
  1167. return true;
  1168. wxString error_msg;
  1169. #define ARC_ERROR_MAX 0.02 /* error max in mm to approximate a arc by segments */
  1170. bool success = ConvertOutlineToPolygon( list_front, m_poly_courtyard_front,
  1171. &error_msg,
  1172. (unsigned) Millimeter2iu( ARC_ERROR_MAX ) );
  1173. if( success )
  1174. {
  1175. success = ConvertOutlineToPolygon( list_back, m_poly_courtyard_back,
  1176. &error_msg,
  1177. (unsigned) Millimeter2iu( ARC_ERROR_MAX ) );
  1178. }
  1179. if( !error_msg.IsEmpty() )
  1180. {
  1181. wxLogMessage( wxString::Format( _( "Processing courtyard of \"%s\": %s" ),
  1182. GetChars( GetFPID().Format() ),
  1183. error_msg) );
  1184. }
  1185. return success;
  1186. }
  1187. void MODULE::SwapData( BOARD_ITEM* aImage )
  1188. {
  1189. assert( aImage->Type() == PCB_MODULE_T );
  1190. std::swap( *((MODULE*) this), *((MODULE*) aImage) );
  1191. }
  1192. bool MODULE::HasNonSMDPins() const
  1193. {
  1194. // returns true if the given module has at lesat one non smd pin, such as through hole
  1195. for( auto pad : Pads() )
  1196. {
  1197. if( pad->GetAttribute() != PAD_ATTRIB_SMD )
  1198. return true;
  1199. }
  1200. return false;
  1201. }