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.

2923 lines
89 KiB

5 years ago
14 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
14 years ago
12 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 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
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
11 years ago
5 years ago
5 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-2022 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 <core/mirror.h>
  27. #include <confirm.h>
  28. #include <refdes_utils.h>
  29. #include <bitmaps.h>
  30. #include <unordered_set>
  31. #include <string_utils.h>
  32. #include <pcb_edit_frame.h>
  33. #include <board.h>
  34. #include <board_design_settings.h>
  35. #include <fp_shape.h>
  36. #include <macros.h>
  37. #include <pad.h>
  38. #include <pcb_marker.h>
  39. #include <pcb_group.h>
  40. #include <pcb_track.h>
  41. #include <pcb_dimension.h>
  42. #include <footprint.h>
  43. #include <zone.h>
  44. #include <view/view.h>
  45. #include <i18n_utility.h>
  46. #include <drc/drc_item.h>
  47. #include <geometry/shape_segment.h>
  48. #include <geometry/shape_simple.h>
  49. #include <convert_shape_list_to_polygon.h>
  50. #include <geometry/convex_hull.h>
  51. #include "fp_textbox.h"
  52. #include "convert_basic_shapes_to_polygon.h"
  53. FOOTPRINT::FOOTPRINT( BOARD* parent ) :
  54. BOARD_ITEM_CONTAINER((BOARD_ITEM*) parent, PCB_FOOTPRINT_T ),
  55. m_boundingBoxCacheTimeStamp( 0 ),
  56. m_visibleBBoxCacheTimeStamp( 0 ),
  57. m_textExcludedBBoxCacheTimeStamp( 0 ),
  58. m_hullCacheTimeStamp( 0 ),
  59. m_initial_comments( nullptr ),
  60. m_courtyard_cache_timestamp( 0 )
  61. {
  62. m_attributes = 0;
  63. m_layer = F_Cu;
  64. m_orient = ANGLE_0;
  65. m_fpStatus = FP_PADS_are_LOCKED;
  66. m_arflag = 0;
  67. m_link = 0;
  68. m_lastEditTime = 0;
  69. m_localClearance = 0;
  70. m_localSolderMaskMargin = 0;
  71. m_localSolderPasteMargin = 0;
  72. m_localSolderPasteMarginRatio = 0.0;
  73. m_zoneConnection = ZONE_CONNECTION::INHERITED;
  74. m_fileFormatVersionAtLoad = 0;
  75. // These are special and mandatory text fields
  76. m_reference = new FP_TEXT( this, FP_TEXT::TEXT_is_REFERENCE );
  77. m_value = new FP_TEXT( this, FP_TEXT::TEXT_is_VALUE );
  78. m_3D_Drawings.clear();
  79. }
  80. FOOTPRINT::FOOTPRINT( const FOOTPRINT& aFootprint ) :
  81. BOARD_ITEM_CONTAINER( aFootprint )
  82. {
  83. m_pos = aFootprint.m_pos;
  84. m_fpid = aFootprint.m_fpid;
  85. m_attributes = aFootprint.m_attributes;
  86. m_fpStatus = aFootprint.m_fpStatus;
  87. m_orient = aFootprint.m_orient;
  88. m_lastEditTime = aFootprint.m_lastEditTime;
  89. m_link = aFootprint.m_link;
  90. m_path = aFootprint.m_path;
  91. m_cachedBoundingBox = aFootprint.m_cachedBoundingBox;
  92. m_boundingBoxCacheTimeStamp = aFootprint.m_boundingBoxCacheTimeStamp;
  93. m_cachedVisibleBBox = aFootprint.m_cachedVisibleBBox;
  94. m_visibleBBoxCacheTimeStamp = aFootprint.m_visibleBBoxCacheTimeStamp;
  95. m_cachedTextExcludedBBox = aFootprint.m_cachedTextExcludedBBox;
  96. m_textExcludedBBoxCacheTimeStamp = aFootprint.m_textExcludedBBoxCacheTimeStamp;
  97. m_cachedHull = aFootprint.m_cachedHull;
  98. m_hullCacheTimeStamp = aFootprint.m_hullCacheTimeStamp;
  99. m_localClearance = aFootprint.m_localClearance;
  100. m_localSolderMaskMargin = aFootprint.m_localSolderMaskMargin;
  101. m_localSolderPasteMargin = aFootprint.m_localSolderPasteMargin;
  102. m_localSolderPasteMarginRatio = aFootprint.m_localSolderPasteMarginRatio;
  103. m_zoneConnection = aFootprint.m_zoneConnection;
  104. m_netTiePadGroups = aFootprint.m_netTiePadGroups;
  105. m_fileFormatVersionAtLoad = aFootprint.m_fileFormatVersionAtLoad;
  106. std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
  107. // Copy reference and value.
  108. m_reference = new FP_TEXT( *aFootprint.m_reference );
  109. m_reference->SetParent( this );
  110. ptrMap[ aFootprint.m_reference ] = m_reference;
  111. m_value = new FP_TEXT( *aFootprint.m_value );
  112. m_value->SetParent( this );
  113. ptrMap[ aFootprint.m_value ] = m_value;
  114. // Copy pads
  115. for( PAD* pad : aFootprint.Pads() )
  116. {
  117. PAD* newPad = static_cast<PAD*>( pad->Clone() );
  118. ptrMap[ pad ] = newPad;
  119. Add( newPad, ADD_MODE::APPEND ); // Append to ensure indexes are identical
  120. }
  121. // Copy zones
  122. for( FP_ZONE* zone : aFootprint.Zones() )
  123. {
  124. FP_ZONE* newZone = static_cast<FP_ZONE*>( zone->Clone() );
  125. ptrMap[ zone ] = newZone;
  126. Add( newZone, ADD_MODE::APPEND ); // Append to ensure indexes are identical
  127. // Ensure the net info is OK and especially uses the net info list
  128. // living in the current board
  129. // Needed when copying a fp from fp editor that has its own board
  130. // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
  131. newZone->SetNetCode( -1 );
  132. }
  133. // Copy drawings
  134. for( BOARD_ITEM* item : aFootprint.GraphicalItems() )
  135. {
  136. BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
  137. ptrMap[ item ] = newItem;
  138. Add( newItem, ADD_MODE::APPEND ); // Append to ensure indexes are identical
  139. }
  140. // Copy groups
  141. for( PCB_GROUP* group : aFootprint.Groups() )
  142. {
  143. PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
  144. ptrMap[ group ] = newGroup;
  145. Add( newGroup, ADD_MODE::APPEND ); // Append to ensure indexes are identical
  146. }
  147. // Rebuild groups
  148. for( PCB_GROUP* group : aFootprint.Groups() )
  149. {
  150. PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( ptrMap[ group ] );
  151. newGroup->GetItems().clear();
  152. for( BOARD_ITEM* member : group->GetItems() )
  153. {
  154. if( ptrMap.count( member ) )
  155. newGroup->AddItem( ptrMap[ member ] );
  156. }
  157. }
  158. // Copy auxiliary data
  159. m_3D_Drawings = aFootprint.m_3D_Drawings;
  160. m_doc = aFootprint.m_doc;
  161. m_keywords = aFootprint.m_keywords;
  162. m_properties = aFootprint.m_properties;
  163. m_privateLayers = aFootprint.m_privateLayers;
  164. m_arflag = 0;
  165. m_initial_comments = aFootprint.m_initial_comments ?
  166. new wxArrayString( *aFootprint.m_initial_comments ) : nullptr;
  167. }
  168. FOOTPRINT::FOOTPRINT( FOOTPRINT&& aFootprint ) :
  169. BOARD_ITEM_CONTAINER( aFootprint )
  170. {
  171. *this = std::move( aFootprint );
  172. }
  173. FOOTPRINT::~FOOTPRINT()
  174. {
  175. // Untangle group parents before doing any deleting
  176. for( PCB_GROUP* group : m_fp_groups )
  177. {
  178. for( BOARD_ITEM* item : group->GetItems() )
  179. item->SetParentGroup( nullptr );
  180. }
  181. // Clean up the owned elements
  182. delete m_reference;
  183. delete m_value;
  184. delete m_initial_comments;
  185. for( PAD* p : m_pads )
  186. delete p;
  187. m_pads.clear();
  188. for( FP_ZONE* zone : m_fp_zones )
  189. delete zone;
  190. m_fp_zones.clear();
  191. for( PCB_GROUP* group : m_fp_groups )
  192. delete group;
  193. m_fp_groups.clear();
  194. for( BOARD_ITEM* d : m_drawings )
  195. delete d;
  196. m_drawings.clear();
  197. }
  198. bool FOOTPRINT::FixUuids()
  199. {
  200. // replace null UUIDs if any by a valid uuid
  201. std::vector< BOARD_ITEM* > item_list;
  202. item_list.push_back( m_reference );
  203. item_list.push_back( m_value );
  204. for( PAD* pad : m_pads )
  205. item_list.push_back( pad );
  206. for( BOARD_ITEM* gr_item : m_drawings )
  207. item_list.push_back( gr_item );
  208. // Note: one cannot fix null UUIDs inside the group, but it should not happen
  209. // because null uuids can be found in old footprints, therefore without group
  210. for( PCB_GROUP* group : m_fp_groups )
  211. item_list.push_back( group );
  212. // Probably notneeded, because old fp do not have zones. But just in case.
  213. for( FP_ZONE* zone : m_fp_zones )
  214. item_list.push_back( zone );
  215. bool changed = false;
  216. for( BOARD_ITEM* item : item_list )
  217. {
  218. if( item->m_Uuid == niluuid )
  219. {
  220. const_cast<KIID&>( item->m_Uuid ) = KIID();
  221. changed = true;
  222. }
  223. }
  224. return changed;
  225. }
  226. FOOTPRINT& FOOTPRINT::operator=( FOOTPRINT&& aOther )
  227. {
  228. BOARD_ITEM::operator=( aOther );
  229. m_pos = aOther.m_pos;
  230. m_fpid = aOther.m_fpid;
  231. m_attributes = aOther.m_attributes;
  232. m_fpStatus = aOther.m_fpStatus;
  233. m_orient = aOther.m_orient;
  234. m_lastEditTime = aOther.m_lastEditTime;
  235. m_link = aOther.m_link;
  236. m_path = aOther.m_path;
  237. m_cachedBoundingBox = aOther.m_cachedBoundingBox;
  238. m_boundingBoxCacheTimeStamp = aOther.m_boundingBoxCacheTimeStamp;
  239. m_cachedVisibleBBox = aOther.m_cachedVisibleBBox;
  240. m_visibleBBoxCacheTimeStamp = aOther.m_visibleBBoxCacheTimeStamp;
  241. m_cachedTextExcludedBBox = aOther.m_cachedTextExcludedBBox;
  242. m_textExcludedBBoxCacheTimeStamp = aOther.m_textExcludedBBoxCacheTimeStamp;
  243. m_cachedHull = aOther.m_cachedHull;
  244. m_hullCacheTimeStamp = aOther.m_hullCacheTimeStamp;
  245. m_localClearance = aOther.m_localClearance;
  246. m_localSolderMaskMargin = aOther.m_localSolderMaskMargin;
  247. m_localSolderPasteMargin = aOther.m_localSolderPasteMargin;
  248. m_localSolderPasteMarginRatio = aOther.m_localSolderPasteMarginRatio;
  249. m_zoneConnection = aOther.m_zoneConnection;
  250. m_netTiePadGroups = aOther.m_netTiePadGroups;
  251. // Move reference and value
  252. m_reference = aOther.m_reference;
  253. m_reference->SetParent( this );
  254. m_value = aOther.m_value;
  255. m_value->SetParent( this );
  256. // Move the pads
  257. m_pads.clear();
  258. for( PAD* pad : aOther.Pads() )
  259. Add( pad );
  260. aOther.Pads().clear();
  261. // Move the zones
  262. m_fp_zones.clear();
  263. for( FP_ZONE* item : aOther.Zones() )
  264. {
  265. Add( item );
  266. // Ensure the net info is OK and especially uses the net info list
  267. // living in the current board
  268. // Needed when copying a fp from fp editor that has its own board
  269. // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
  270. item->SetNetCode( -1 );
  271. }
  272. aOther.Zones().clear();
  273. // Move the drawings
  274. m_drawings.clear();
  275. for( BOARD_ITEM* item : aOther.GraphicalItems() )
  276. Add( item );
  277. aOther.GraphicalItems().clear();
  278. // Move the groups
  279. m_fp_groups.clear();
  280. for( PCB_GROUP* group : aOther.Groups() )
  281. Add( group );
  282. aOther.Groups().clear();
  283. // Copy auxiliary data
  284. m_3D_Drawings = aOther.m_3D_Drawings;
  285. m_doc = aOther.m_doc;
  286. m_keywords = aOther.m_keywords;
  287. m_properties = aOther.m_properties;
  288. m_privateLayers = aOther.m_privateLayers;
  289. m_initial_comments = aOther.m_initial_comments;
  290. // Clear the other item's containers since this is a move
  291. aOther.Pads().clear();
  292. aOther.Zones().clear();
  293. aOther.GraphicalItems().clear();
  294. aOther.m_value = nullptr;
  295. aOther.m_reference = nullptr;
  296. aOther.m_initial_comments = nullptr;
  297. return *this;
  298. }
  299. FOOTPRINT& FOOTPRINT::operator=( const FOOTPRINT& aOther )
  300. {
  301. BOARD_ITEM::operator=( aOther );
  302. m_pos = aOther.m_pos;
  303. m_fpid = aOther.m_fpid;
  304. m_attributes = aOther.m_attributes;
  305. m_fpStatus = aOther.m_fpStatus;
  306. m_orient = aOther.m_orient;
  307. m_lastEditTime = aOther.m_lastEditTime;
  308. m_link = aOther.m_link;
  309. m_path = aOther.m_path;
  310. m_cachedBoundingBox = aOther.m_cachedBoundingBox;
  311. m_boundingBoxCacheTimeStamp = aOther.m_boundingBoxCacheTimeStamp;
  312. m_cachedVisibleBBox = aOther.m_cachedVisibleBBox;
  313. m_visibleBBoxCacheTimeStamp = aOther.m_visibleBBoxCacheTimeStamp;
  314. m_cachedTextExcludedBBox = aOther.m_cachedTextExcludedBBox;
  315. m_textExcludedBBoxCacheTimeStamp = aOther.m_textExcludedBBoxCacheTimeStamp;
  316. m_cachedHull = aOther.m_cachedHull;
  317. m_hullCacheTimeStamp = aOther.m_hullCacheTimeStamp;
  318. m_localClearance = aOther.m_localClearance;
  319. m_localSolderMaskMargin = aOther.m_localSolderMaskMargin;
  320. m_localSolderPasteMargin = aOther.m_localSolderPasteMargin;
  321. m_localSolderPasteMarginRatio = aOther.m_localSolderPasteMarginRatio;
  322. m_zoneConnection = aOther.m_zoneConnection;
  323. m_netTiePadGroups = aOther.m_netTiePadGroups;
  324. // Copy reference and value
  325. *m_reference = *aOther.m_reference;
  326. m_reference->SetParent( this );
  327. *m_value = *aOther.m_value;
  328. m_value->SetParent( this );
  329. std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
  330. // Copy pads
  331. m_pads.clear();
  332. for( PAD* pad : aOther.Pads() )
  333. {
  334. PAD* newPad = new PAD( *pad );
  335. ptrMap[ pad ] = newPad;
  336. Add( newPad );
  337. }
  338. // Copy zones
  339. m_fp_zones.clear();
  340. for( FP_ZONE* zone : aOther.Zones() )
  341. {
  342. FP_ZONE* newZone = static_cast<FP_ZONE*>( zone->Clone() );
  343. ptrMap[ zone ] = newZone;
  344. Add( newZone );
  345. // Ensure the net info is OK and especially uses the net info list
  346. // living in the current board
  347. // Needed when copying a fp from fp editor that has its own board
  348. // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
  349. newZone->SetNetCode( -1 );
  350. }
  351. // Copy drawings
  352. m_drawings.clear();
  353. for( BOARD_ITEM* item : aOther.GraphicalItems() )
  354. {
  355. BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
  356. ptrMap[ item ] = newItem;
  357. Add( newItem );
  358. }
  359. // Copy groups
  360. m_fp_groups.clear();
  361. for( PCB_GROUP* group : aOther.Groups() )
  362. {
  363. PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
  364. newGroup->GetItems().clear();
  365. for( BOARD_ITEM* member : group->GetItems() )
  366. newGroup->AddItem( ptrMap[ member ] );
  367. Add( newGroup );
  368. }
  369. // Copy auxiliary data
  370. m_3D_Drawings = aOther.m_3D_Drawings;
  371. m_doc = aOther.m_doc;
  372. m_keywords = aOther.m_keywords;
  373. m_properties = aOther.m_properties;
  374. m_privateLayers = aOther.m_privateLayers;
  375. m_initial_comments = aOther.m_initial_comments ?
  376. new wxArrayString( *aOther.m_initial_comments ) : nullptr;
  377. return *this;
  378. }
  379. bool FOOTPRINT::IsConflicting() const
  380. {
  381. return HasFlag( COURTYARD_CONFLICT );
  382. }
  383. void FOOTPRINT::GetContextualTextVars( wxArrayString* aVars ) const
  384. {
  385. aVars->push_back( wxT( "REFERENCE" ) );
  386. aVars->push_back( wxT( "VALUE" ) );
  387. aVars->push_back( wxT( "LAYER" ) );
  388. aVars->push_back( wxT( "FOOTPRINT_LIBRARY" ) );
  389. aVars->push_back( wxT( "FOOTPRINT_NAME" ) );
  390. aVars->push_back( wxT( "NET_NAME(<pad_number>)" ) );
  391. aVars->push_back( wxT( "NET_CLASS(<pad_number>)" ) );
  392. aVars->push_back( wxT( "PIN_NAME(<pad_number>)" ) );
  393. }
  394. bool FOOTPRINT::ResolveTextVar( wxString* token, int aDepth ) const
  395. {
  396. if( GetBoard() && GetBoard()->GetBoardUse() == BOARD_USE::FPHOLDER )
  397. return false;
  398. if( token->IsSameAs( wxT( "REFERENCE" ) ) )
  399. {
  400. *token = m_reference->GetShownText( aDepth + 1 );
  401. return true;
  402. }
  403. else if( token->IsSameAs( wxT( "VALUE" ) ) )
  404. {
  405. *token = m_value->GetShownText( aDepth + 1 );
  406. return true;
  407. }
  408. else if( token->IsSameAs( wxT( "LAYER" ) ) )
  409. {
  410. *token = GetLayerName();
  411. return true;
  412. }
  413. else if( token->IsSameAs( wxT( "FOOTPRINT_LIBRARY" ) ) )
  414. {
  415. *token = m_fpid.GetLibNickname();
  416. return true;
  417. }
  418. else if( token->IsSameAs( wxT( "FOOTPRINT_NAME" ) ) )
  419. {
  420. *token = m_fpid.GetLibItemName();
  421. return true;
  422. }
  423. else if( token->StartsWith( wxT( "NET_NAME(" ) )
  424. || token->StartsWith( wxT( "NET_CLASS(" ) )
  425. || token->StartsWith( wxT( "PIN_NAME(" ) ) )
  426. {
  427. wxString padNumber = token->AfterFirst( '(' );
  428. padNumber = padNumber.BeforeLast( ')' );
  429. for( PAD* pad : Pads() )
  430. {
  431. if( pad->GetNumber() == padNumber )
  432. {
  433. if( token->StartsWith( wxT( "NET_NAME" ) ) )
  434. *token = pad->GetNetname();
  435. else if( token->StartsWith( wxT( "NET_CLASS" ) ) )
  436. *token = pad->GetNetClassName();
  437. else
  438. *token = pad->GetPinFunction();
  439. return true;
  440. }
  441. }
  442. }
  443. else if( m_properties.count( *token ) )
  444. {
  445. *token = m_properties.at( *token );
  446. return true;
  447. }
  448. return false;
  449. }
  450. void FOOTPRINT::ClearAllNets()
  451. {
  452. // Force the ORPHANED dummy net info for all pads.
  453. // ORPHANED dummy net does not depend on a board
  454. for( PAD* pad : m_pads )
  455. pad->SetNetCode( NETINFO_LIST::ORPHANED );
  456. }
  457. void FOOTPRINT::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode, bool aSkipConnectivity )
  458. {
  459. switch( aBoardItem->Type() )
  460. {
  461. case PCB_FP_TEXT_T:
  462. // Only user text can be added this way.
  463. wxASSERT( static_cast<FP_TEXT*>( aBoardItem )->GetType() == FP_TEXT::TEXT_is_DIVERS );
  464. KI_FALLTHROUGH;
  465. case PCB_FP_DIM_ALIGNED_T:
  466. case PCB_FP_DIM_LEADER_T:
  467. case PCB_FP_DIM_CENTER_T:
  468. case PCB_FP_DIM_RADIAL_T:
  469. case PCB_FP_DIM_ORTHOGONAL_T:
  470. case PCB_FP_SHAPE_T:
  471. case PCB_FP_TEXTBOX_T:
  472. case PCB_BITMAP_T:
  473. if( aMode == ADD_MODE::APPEND )
  474. m_drawings.push_back( aBoardItem );
  475. else
  476. m_drawings.push_front( aBoardItem );
  477. break;
  478. case PCB_PAD_T:
  479. if( aMode == ADD_MODE::APPEND )
  480. m_pads.push_back( static_cast<PAD*>( aBoardItem ) );
  481. else
  482. m_pads.push_front( static_cast<PAD*>( aBoardItem ) );
  483. break;
  484. case PCB_FP_ZONE_T:
  485. if( aMode == ADD_MODE::APPEND )
  486. m_fp_zones.push_back( static_cast<FP_ZONE*>( aBoardItem ) );
  487. else
  488. m_fp_zones.insert( m_fp_zones.begin(), static_cast<FP_ZONE*>( aBoardItem ) );
  489. break;
  490. case PCB_GROUP_T:
  491. if( aMode == ADD_MODE::APPEND )
  492. m_fp_groups.push_back( static_cast<PCB_GROUP*>( aBoardItem ) );
  493. else
  494. m_fp_groups.insert( m_fp_groups.begin(), static_cast<PCB_GROUP*>( aBoardItem ) );
  495. break;
  496. default:
  497. {
  498. wxString msg;
  499. msg.Printf( wxT( "FOOTPRINT::Add() needs work: BOARD_ITEM type (%d) not handled" ),
  500. aBoardItem->Type() );
  501. wxFAIL_MSG( msg );
  502. return;
  503. }
  504. }
  505. aBoardItem->ClearEditFlags();
  506. aBoardItem->SetParent( this );
  507. }
  508. void FOOTPRINT::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aMode )
  509. {
  510. switch( aBoardItem->Type() )
  511. {
  512. case PCB_FP_TEXT_T:
  513. // Only user text can be removed this way.
  514. wxCHECK_RET( static_cast<FP_TEXT*>( aBoardItem )->GetType() == FP_TEXT::TEXT_is_DIVERS,
  515. wxT( "Please report this bug: Invalid remove operation on required text" ) );
  516. KI_FALLTHROUGH;
  517. case PCB_FP_DIM_ALIGNED_T:
  518. case PCB_FP_DIM_CENTER_T:
  519. case PCB_FP_DIM_ORTHOGONAL_T:
  520. case PCB_FP_DIM_RADIAL_T:
  521. case PCB_FP_DIM_LEADER_T:
  522. case PCB_FP_SHAPE_T:
  523. case PCB_FP_TEXTBOX_T:
  524. for( auto it = m_drawings.begin(); it != m_drawings.end(); ++it )
  525. {
  526. if( *it == aBoardItem )
  527. {
  528. m_drawings.erase( it );
  529. break;
  530. }
  531. }
  532. break;
  533. case PCB_PAD_T:
  534. for( auto it = m_pads.begin(); it != m_pads.end(); ++it )
  535. {
  536. if( *it == static_cast<PAD*>( aBoardItem ) )
  537. {
  538. m_pads.erase( it );
  539. break;
  540. }
  541. }
  542. break;
  543. case PCB_FP_ZONE_T:
  544. for( auto it = m_fp_zones.begin(); it != m_fp_zones.end(); ++it )
  545. {
  546. if( *it == static_cast<FP_ZONE*>( aBoardItem ) )
  547. {
  548. m_fp_zones.erase( it );
  549. break;
  550. }
  551. }
  552. break;
  553. case PCB_GROUP_T:
  554. for( auto it = m_fp_groups.begin(); it != m_fp_groups.end(); ++it )
  555. {
  556. if( *it == static_cast<PCB_GROUP*>( aBoardItem ) )
  557. {
  558. m_fp_groups.erase( it );
  559. break;
  560. }
  561. }
  562. break;
  563. default:
  564. {
  565. wxString msg;
  566. msg.Printf( wxT( "FOOTPRINT::Remove() needs work: BOARD_ITEM type (%d) not handled" ),
  567. aBoardItem->Type() );
  568. wxFAIL_MSG( msg );
  569. }
  570. }
  571. aBoardItem->SetFlags( STRUCT_DELETED );
  572. PCB_GROUP* parentGroup = aBoardItem->GetParentGroup();
  573. if( parentGroup && !( parentGroup->GetFlags() & STRUCT_DELETED ) )
  574. parentGroup->RemoveItem( aBoardItem );
  575. }
  576. double FOOTPRINT::GetArea( int aPadding ) const
  577. {
  578. BOX2I bbox = GetBoundingBox( false, false );
  579. double w = std::abs( static_cast<double>( bbox.GetWidth() ) ) + aPadding;
  580. double h = std::abs( static_cast<double>( bbox.GetHeight() ) ) + aPadding;
  581. return w * h;
  582. }
  583. int FOOTPRINT::GetLikelyAttribute() const
  584. {
  585. int smd_count = 0;
  586. int tht_count = 0;
  587. for( PAD* pad : m_pads )
  588. {
  589. switch( pad->GetProperty() )
  590. {
  591. case PAD_PROP::FIDUCIAL_GLBL:
  592. case PAD_PROP::FIDUCIAL_LOCAL:
  593. continue;
  594. case PAD_PROP::HEATSINK:
  595. case PAD_PROP::CASTELLATED:
  596. continue;
  597. case PAD_PROP::NONE:
  598. case PAD_PROP::BGA:
  599. case PAD_PROP::TESTPOINT:
  600. break;
  601. }
  602. switch( pad->GetAttribute() )
  603. {
  604. case PAD_ATTRIB::PTH:
  605. tht_count++;
  606. break;
  607. case PAD_ATTRIB::SMD:
  608. smd_count++;
  609. break;
  610. default:
  611. break;
  612. }
  613. }
  614. if( tht_count > 0 )
  615. return FP_THROUGH_HOLE;
  616. if( smd_count > 0 )
  617. return FP_SMD;
  618. return 0;
  619. }
  620. wxString FOOTPRINT::GetTypeName() const
  621. {
  622. if( ( m_attributes & FP_SMD ) == FP_SMD )
  623. return _( "SMD" );
  624. if( ( m_attributes & FP_THROUGH_HOLE ) == FP_THROUGH_HOLE )
  625. return _( "Through hole" );
  626. return _( "Other" );
  627. }
  628. BOX2I FOOTPRINT::GetFpPadsLocalBbox() const
  629. {
  630. BOX2I bbox;
  631. // We want the bounding box of the footprint pads at rot 0, not flipped
  632. // Create such a image:
  633. FOOTPRINT dummy( *this );
  634. dummy.SetPosition( VECTOR2I( 0, 0 ) );
  635. dummy.SetOrientation( ANGLE_0 );
  636. if( dummy.IsFlipped() )
  637. dummy.Flip( VECTOR2I( 0, 0 ), false );
  638. for( PAD* pad : dummy.Pads() )
  639. bbox.Merge( pad->GetBoundingBox() );
  640. return bbox;
  641. }
  642. const BOX2I FOOTPRINT::GetBoundingBox() const
  643. {
  644. return GetBoundingBox( true, true );
  645. }
  646. const BOX2I FOOTPRINT::GetBoundingBox( bool aIncludeText, bool aIncludeInvisibleText ) const
  647. {
  648. const BOARD* board = GetBoard();
  649. bool isFPEdit = board && board->IsFootprintHolder();
  650. if( board )
  651. {
  652. if( aIncludeText && aIncludeInvisibleText )
  653. {
  654. if( m_boundingBoxCacheTimeStamp >= board->GetTimeStamp() )
  655. return m_cachedBoundingBox;
  656. }
  657. else if( aIncludeText )
  658. {
  659. if( m_visibleBBoxCacheTimeStamp >= board->GetTimeStamp() )
  660. return m_cachedVisibleBBox;
  661. }
  662. else
  663. {
  664. if( m_textExcludedBBoxCacheTimeStamp >= board->GetTimeStamp() )
  665. return m_cachedTextExcludedBBox;
  666. }
  667. }
  668. BOX2I bbox( m_pos );
  669. bbox.Inflate( pcbIUScale.mmToIU( 0.25 ) ); // Give a min size to the bbox
  670. for( BOARD_ITEM* item : m_drawings )
  671. {
  672. if( m_privateLayers.test( item->GetLayer() ) && !isFPEdit )
  673. continue;
  674. // We want the bitmap bounding box just in the footprint editor
  675. // so it will start with the correct initial zoom
  676. if( item->Type() == PCB_BITMAP_T && !isFPEdit )
  677. continue;
  678. // Handle text separately
  679. if( item->Type() == PCB_FP_TEXT_T )
  680. continue;
  681. bbox.Merge( item->GetBoundingBox() );
  682. }
  683. for( PAD* pad : m_pads )
  684. bbox.Merge( pad->GetBoundingBox() );
  685. for( FP_ZONE* zone : m_fp_zones )
  686. bbox.Merge( zone->GetBoundingBox() );
  687. bool noDrawItems = ( m_drawings.empty() && m_pads.empty() && m_fp_zones.empty() );
  688. // Groups do not contribute to the rect, only their members
  689. if( aIncludeText || noDrawItems )
  690. {
  691. for( BOARD_ITEM* item : m_drawings )
  692. {
  693. if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
  694. continue;
  695. // Only FP_TEXT items are independently selectable; FP_TEXTBOX items go in with
  696. // other graphic items above.
  697. if( item->Type() == PCB_FP_TEXT_T )
  698. bbox.Merge( item->GetBoundingBox() );
  699. }
  700. // This can be further optimized when aIncludeInvisibleText is true, but currently
  701. // leaving this as is until it's determined there is a noticeable speed hit.
  702. bool valueLayerIsVisible = true;
  703. bool refLayerIsVisible = true;
  704. if( board )
  705. {
  706. // The first "&&" conditional handles the user turning layers off as well as layers
  707. // not being present in the current PCB stackup. Values, references, and all
  708. // footprint text can also be turned off via the GAL meta-layers, so the 2nd and
  709. // 3rd "&&" conditionals handle that.
  710. valueLayerIsVisible = board->IsLayerVisible( m_value->GetLayer() )
  711. && board->IsElementVisible( LAYER_MOD_VALUES )
  712. && board->IsElementVisible( LAYER_MOD_TEXT );
  713. refLayerIsVisible = board->IsLayerVisible( m_reference->GetLayer() )
  714. && board->IsElementVisible( LAYER_MOD_REFERENCES )
  715. && board->IsElementVisible( LAYER_MOD_TEXT );
  716. }
  717. if( ( m_value->IsVisible() && valueLayerIsVisible )
  718. || aIncludeInvisibleText
  719. || noDrawItems )
  720. {
  721. bbox.Merge( m_value->GetBoundingBox() );
  722. }
  723. if( ( m_reference->IsVisible() && refLayerIsVisible )
  724. || aIncludeInvisibleText
  725. || noDrawItems )
  726. {
  727. bbox.Merge( m_reference->GetBoundingBox() );
  728. }
  729. }
  730. if( board )
  731. {
  732. if( ( aIncludeText && aIncludeInvisibleText ) || noDrawItems )
  733. {
  734. m_boundingBoxCacheTimeStamp = board->GetTimeStamp();
  735. m_cachedBoundingBox = bbox;
  736. }
  737. else if( aIncludeText )
  738. {
  739. m_visibleBBoxCacheTimeStamp = board->GetTimeStamp();
  740. m_cachedVisibleBBox = bbox;
  741. }
  742. else
  743. {
  744. m_textExcludedBBoxCacheTimeStamp = board->GetTimeStamp();
  745. m_cachedTextExcludedBBox = bbox;
  746. }
  747. }
  748. return bbox;
  749. }
  750. SHAPE_POLY_SET FOOTPRINT::GetBoundingHull() const
  751. {
  752. const BOARD* board = GetBoard();
  753. bool isFPEdit = board && board->IsFootprintHolder();
  754. if( board )
  755. {
  756. if( m_hullCacheTimeStamp >= board->GetTimeStamp() )
  757. return m_cachedHull;
  758. }
  759. SHAPE_POLY_SET rawPolys;
  760. SHAPE_POLY_SET hull;
  761. for( BOARD_ITEM* item : m_drawings )
  762. {
  763. if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
  764. continue;
  765. if( item->Type() != PCB_FP_TEXT_T && item->Type() != PCB_BITMAP_T )
  766. {
  767. item->TransformShapeToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
  768. ERROR_OUTSIDE );
  769. }
  770. // We intentionally exclude footprint text from the bounding hull.
  771. }
  772. for( PAD* pad : m_pads )
  773. {
  774. pad->TransformShapeToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
  775. // In case hole is larger than pad
  776. pad->TransformHoleToPolygon( rawPolys, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
  777. }
  778. for( FP_ZONE* zone : m_fp_zones )
  779. {
  780. for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
  781. {
  782. const SHAPE_POLY_SET& layerPoly = *zone->GetFilledPolysList( layer );
  783. for( int ii = 0; ii < layerPoly.OutlineCount(); ii++ )
  784. {
  785. const SHAPE_LINE_CHAIN& poly = layerPoly.COutline( ii );
  786. rawPolys.AddOutline( poly );
  787. }
  788. }
  789. }
  790. // If there are some graphic items, build the actual hull.
  791. // However if no items, create a minimal polygon (can happen if a footprint
  792. // is created with no item: it contains only 2 texts.
  793. if( rawPolys.OutlineCount() == 0 )
  794. {
  795. // generate a small dummy rectangular outline around the anchor
  796. const int halfsize = pcbIUScale.mmToIU( 1.0 );
  797. rawPolys.NewOutline();
  798. // add a square:
  799. rawPolys.Append( GetPosition().x - halfsize, GetPosition().y - halfsize );
  800. rawPolys.Append( GetPosition().x + halfsize, GetPosition().y - halfsize );
  801. rawPolys.Append( GetPosition().x + halfsize, GetPosition().y + halfsize );
  802. rawPolys.Append( GetPosition().x - halfsize, GetPosition().y + halfsize );
  803. }
  804. std::vector<VECTOR2I> convex_hull;
  805. BuildConvexHull( convex_hull, rawPolys );
  806. m_cachedHull.RemoveAllContours();
  807. m_cachedHull.NewOutline();
  808. for( const VECTOR2I& pt : convex_hull )
  809. m_cachedHull.Append( pt );
  810. if( board )
  811. m_hullCacheTimeStamp = board->GetTimeStamp();
  812. return m_cachedHull;
  813. }
  814. void FOOTPRINT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  815. {
  816. wxString msg, msg2;
  817. aList.emplace_back( m_reference->GetShownText(), m_value->GetShownText() );
  818. if( aFrame->IsType( FRAME_FOOTPRINT_VIEWER )
  819. || aFrame->IsType( FRAME_FOOTPRINT_VIEWER_MODAL )
  820. || aFrame->IsType( FRAME_FOOTPRINT_EDITOR ) )
  821. {
  822. wxDateTime date( static_cast<time_t>( m_lastEditTime ) );
  823. // Date format: see http://www.cplusplus.com/reference/ctime/strftime
  824. if( m_lastEditTime && date.IsValid() )
  825. msg = date.Format( wxT( "%b %d, %Y" ) ); // Abbreviated_month_name Day, Year
  826. else
  827. msg = _( "Unknown" );
  828. aList.emplace_back( _( "Last Change" ), msg );
  829. }
  830. else if( aFrame->IsType( FRAME_PCB_EDITOR ) )
  831. {
  832. aList.emplace_back( _( "Board Side" ), IsFlipped() ? _( "Back (Flipped)" ) : _( "Front" ) );
  833. }
  834. auto addToken = []( wxString* aStr, const wxString& aAttr )
  835. {
  836. if( !aStr->IsEmpty() )
  837. *aStr += wxT( ", " );
  838. *aStr += aAttr;
  839. };
  840. wxString status;
  841. wxString attrs;
  842. if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
  843. addToken( &status, _( "Locked" ) );
  844. if( m_fpStatus & FP_is_PLACED )
  845. addToken( &status, _( "autoplaced" ) );
  846. if( m_attributes & FP_BOARD_ONLY )
  847. addToken( &attrs, _( "not in schematic" ) );
  848. if( m_attributes & FP_EXCLUDE_FROM_POS_FILES )
  849. addToken( &attrs, _( "exclude from pos files" ) );
  850. if( m_attributes & FP_EXCLUDE_FROM_BOM )
  851. addToken( &attrs, _( "exclude from BOM" ) );
  852. aList.emplace_back( _( "Status: " ) + status, _( "Attributes:" ) + wxS( " " ) + attrs );
  853. aList.emplace_back( _( "Rotation" ), wxString::Format( wxT( "%.4g" ),
  854. GetOrientation().AsDegrees() ) );
  855. msg.Printf( _( "Footprint: %s" ), m_fpid.GetUniStringLibId() );
  856. msg2.Printf( _( "3D-Shape: %s" ), m_3D_Drawings.empty() ? _( "<none>" )
  857. : m_3D_Drawings.front().m_Filename );
  858. aList.emplace_back( msg, msg2 );
  859. msg.Printf( _( "Doc: %s" ), m_doc );
  860. msg2.Printf( _( "Keywords: %s" ), m_keywords );
  861. aList.emplace_back( msg, msg2 );
  862. }
  863. bool FOOTPRINT::IsOnLayer( PCB_LAYER_ID aLayer ) const
  864. {
  865. // If we have any pads, fall back on normal checking
  866. if( !m_pads.empty() )
  867. return m_layer == aLayer;
  868. // No pads? Check if this entire footprint exists on the given layer
  869. for( FP_ZONE* zone : m_fp_zones )
  870. {
  871. if( !zone->IsOnLayer( aLayer ) )
  872. return false;
  873. }
  874. for( BOARD_ITEM* item : m_drawings )
  875. {
  876. if( !item->IsOnLayer( aLayer ) )
  877. return false;
  878. }
  879. return true;
  880. }
  881. bool FOOTPRINT::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  882. {
  883. BOX2I rect = GetBoundingBox( false, false );
  884. return rect.Inflate( aAccuracy ).Contains( aPosition );
  885. }
  886. bool FOOTPRINT::HitTestAccurate( const VECTOR2I& aPosition, int aAccuracy ) const
  887. {
  888. return GetBoundingHull().Collide( aPosition, aAccuracy );
  889. }
  890. bool FOOTPRINT::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  891. {
  892. BOX2I arect = aRect;
  893. arect.Inflate( aAccuracy );
  894. if( aContained )
  895. {
  896. return arect.Contains( GetBoundingBox( false, false ) );
  897. }
  898. else
  899. {
  900. // If the rect does not intersect the bounding box, skip any tests
  901. if( !aRect.Intersects( GetBoundingBox( false, false ) ) )
  902. return false;
  903. // The empty footprint dummy rectangle intersects the selection area.
  904. if( m_pads.empty() && m_fp_zones.empty() && m_drawings.empty() )
  905. return GetBoundingBox( true, false ).Intersects( arect );
  906. // Determine if any elements in the FOOTPRINT intersect the rect
  907. for( PAD* pad : m_pads )
  908. {
  909. if( pad->HitTest( arect, false, 0 ) )
  910. return true;
  911. }
  912. for( FP_ZONE* zone : m_fp_zones )
  913. {
  914. if( zone->HitTest( arect, false, 0 ) )
  915. return true;
  916. }
  917. for( BOARD_ITEM* item : m_drawings )
  918. {
  919. // Text items are selectable on their own, and are therefore excluded from this
  920. // test. TextBox items are NOT selectable on their own, and so MUST be included
  921. // here. Bitmaps aren't selectable since they aren't displayed.
  922. if( item->Type() != PCB_FP_TEXT_T && item->Type() != PCB_FP_TEXT_T
  923. && item->HitTest( arect, false, 0 ) )
  924. {
  925. return true;
  926. }
  927. }
  928. // Groups are not hit-tested; only their members
  929. // No items were hit
  930. return false;
  931. }
  932. }
  933. PAD* FOOTPRINT::FindPadByNumber( const wxString& aPadNumber, PAD* aSearchAfterMe ) const
  934. {
  935. bool can_select = aSearchAfterMe ? false : true;
  936. for( PAD* pad : m_pads )
  937. {
  938. if( !can_select && pad == aSearchAfterMe )
  939. {
  940. can_select = true;
  941. continue;
  942. }
  943. if( can_select && pad->GetNumber() == aPadNumber )
  944. return pad;
  945. }
  946. return nullptr;
  947. }
  948. PAD* FOOTPRINT::GetPad( const VECTOR2I& aPosition, LSET aLayerMask )
  949. {
  950. for( PAD* pad : m_pads )
  951. {
  952. // ... and on the correct layer.
  953. if( !( pad->GetLayerSet() & aLayerMask ).any() )
  954. continue;
  955. if( pad->HitTest( aPosition ) )
  956. return pad;
  957. }
  958. return nullptr;
  959. }
  960. unsigned FOOTPRINT::GetPadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
  961. {
  962. if( aIncludeNPTH )
  963. return m_pads.size();
  964. unsigned cnt = 0;
  965. for( PAD* pad : m_pads )
  966. {
  967. if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
  968. continue;
  969. cnt++;
  970. }
  971. return cnt;
  972. }
  973. unsigned FOOTPRINT::GetUniquePadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
  974. {
  975. std::set<wxString> usedNumbers;
  976. // Create a set of used pad numbers
  977. for( PAD* pad : m_pads )
  978. {
  979. // Skip pads not on copper layers (used to build complex
  980. // solder paste shapes for instance)
  981. if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none() )
  982. continue;
  983. // Skip pads with no name, because they are usually "mechanical"
  984. // pads, not "electrical" pads
  985. if( pad->GetNumber().IsEmpty() )
  986. continue;
  987. if( !aIncludeNPTH )
  988. {
  989. // skip NPTH
  990. if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
  991. continue;
  992. }
  993. usedNumbers.insert( pad->GetNumber() );
  994. }
  995. return usedNumbers.size();
  996. }
  997. void FOOTPRINT::Add3DModel( FP_3DMODEL* a3DModel )
  998. {
  999. if( nullptr == a3DModel )
  1000. return;
  1001. if( !a3DModel->m_Filename.empty() )
  1002. m_3D_Drawings.push_back( *a3DModel );
  1003. }
  1004. // see footprint.h
  1005. INSPECT_RESULT FOOTPRINT::Visit( INSPECTOR inspector, void* testData,
  1006. const std::vector<KICAD_T>& aScanTypes )
  1007. {
  1008. #if 0 && defined(DEBUG)
  1009. std::cout << GetClass().mb_str() << ' ';
  1010. #endif
  1011. bool drawingsScanned = false;
  1012. for( KICAD_T scanType : aScanTypes )
  1013. {
  1014. switch( scanType )
  1015. {
  1016. case PCB_FOOTPRINT_T:
  1017. if( inspector( this, testData ) == INSPECT_RESULT::QUIT )
  1018. return INSPECT_RESULT::QUIT;
  1019. break;
  1020. case PCB_PAD_T:
  1021. if( IterateForward<PAD*>( m_pads, inspector, testData, { scanType } )
  1022. == INSPECT_RESULT::QUIT )
  1023. {
  1024. return INSPECT_RESULT::QUIT;
  1025. }
  1026. break;
  1027. case PCB_FP_ZONE_T:
  1028. if( IterateForward<FP_ZONE*>( m_fp_zones, inspector, testData, { scanType } )
  1029. == INSPECT_RESULT::QUIT )
  1030. {
  1031. return INSPECT_RESULT::QUIT;
  1032. }
  1033. break;
  1034. case PCB_FP_TEXT_T:
  1035. if( inspector( m_reference, testData ) == INSPECT_RESULT::QUIT )
  1036. return INSPECT_RESULT::QUIT;
  1037. if( inspector( m_value, testData ) == INSPECT_RESULT::QUIT )
  1038. return INSPECT_RESULT::QUIT;
  1039. // Intentionally fall through since m_Drawings can hold PCB_FP_TEXT_T also
  1040. KI_FALLTHROUGH;
  1041. case PCB_FP_DIM_ALIGNED_T:
  1042. case PCB_FP_DIM_LEADER_T:
  1043. case PCB_FP_DIM_CENTER_T:
  1044. case PCB_FP_DIM_RADIAL_T:
  1045. case PCB_FP_DIM_ORTHOGONAL_T:
  1046. case PCB_FP_SHAPE_T:
  1047. case PCB_FP_TEXTBOX_T:
  1048. if( !drawingsScanned )
  1049. {
  1050. if( IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, aScanTypes )
  1051. == INSPECT_RESULT::QUIT )
  1052. {
  1053. return INSPECT_RESULT::QUIT;
  1054. }
  1055. drawingsScanned = true;
  1056. }
  1057. break;
  1058. case PCB_GROUP_T:
  1059. if( IterateForward<PCB_GROUP*>( m_fp_groups, inspector, testData, { scanType } )
  1060. == INSPECT_RESULT::QUIT )
  1061. {
  1062. return INSPECT_RESULT::QUIT;
  1063. }
  1064. break;
  1065. default:
  1066. break;
  1067. }
  1068. }
  1069. return INSPECT_RESULT::CONTINUE;
  1070. }
  1071. wxString FOOTPRINT::GetSelectMenuText( UNITS_PROVIDER* aUnitsProvider ) const
  1072. {
  1073. wxString reference = GetReference();
  1074. if( reference.IsEmpty() )
  1075. reference = _( "<no reference designator>" );
  1076. return wxString::Format( _( "Footprint %s" ), reference );
  1077. }
  1078. BITMAPS FOOTPRINT::GetMenuImage() const
  1079. {
  1080. return BITMAPS::module;
  1081. }
  1082. EDA_ITEM* FOOTPRINT::Clone() const
  1083. {
  1084. return new FOOTPRINT( *this );
  1085. }
  1086. void FOOTPRINT::RunOnChildren( const std::function<void ( BOARD_ITEM*)>& aFunction ) const
  1087. {
  1088. try
  1089. {
  1090. for( PAD* pad : m_pads )
  1091. aFunction( static_cast<BOARD_ITEM*>( pad ) );
  1092. for( FP_ZONE* zone : m_fp_zones )
  1093. aFunction( static_cast<FP_ZONE*>( zone ) );
  1094. for( PCB_GROUP* group : m_fp_groups )
  1095. aFunction( static_cast<PCB_GROUP*>( group ) );
  1096. for( BOARD_ITEM* drawing : m_drawings )
  1097. aFunction( static_cast<BOARD_ITEM*>( drawing ) );
  1098. aFunction( static_cast<BOARD_ITEM*>( m_reference ) );
  1099. aFunction( static_cast<BOARD_ITEM*>( m_value ) );
  1100. }
  1101. catch( std::bad_function_call& )
  1102. {
  1103. wxFAIL_MSG( wxT( "Error running FOOTPRINT::RunOnChildren" ) );
  1104. }
  1105. }
  1106. void FOOTPRINT::ViewGetLayers( int aLayers[], int& aCount ) const
  1107. {
  1108. aCount = 2;
  1109. aLayers[0] = LAYER_ANCHOR;
  1110. switch( m_layer )
  1111. {
  1112. default:
  1113. wxASSERT_MSG( false, wxT( "Illegal layer" ) ); // do you really have footprints placed
  1114. // on other layers?
  1115. KI_FALLTHROUGH;
  1116. case F_Cu:
  1117. aLayers[1] = LAYER_MOD_FR;
  1118. break;
  1119. case B_Cu:
  1120. aLayers[1] = LAYER_MOD_BK;
  1121. break;
  1122. }
  1123. if( IsLocked() )
  1124. aLayers[ aCount++ ] = LAYER_LOCKED_ITEM_SHADOW;
  1125. if( IsConflicting() )
  1126. aLayers[ aCount++ ] = LAYER_CONFLICTS_SHADOW;
  1127. // If there are no pads, and only drawings on a silkscreen layer, then report the silkscreen
  1128. // layer as well so that the component can be edited with the silkscreen layer
  1129. bool f_silk = false, b_silk = false, non_silk = false;
  1130. for( BOARD_ITEM* item : m_drawings )
  1131. {
  1132. if( item->GetLayer() == F_SilkS )
  1133. f_silk = true;
  1134. else if( item->GetLayer() == B_SilkS )
  1135. b_silk = true;
  1136. else
  1137. non_silk = true;
  1138. }
  1139. if( ( f_silk || b_silk ) && !non_silk && m_pads.empty() )
  1140. {
  1141. if( f_silk )
  1142. aLayers[ aCount++ ] = F_SilkS;
  1143. if( b_silk )
  1144. aLayers[ aCount++ ] = B_SilkS;
  1145. }
  1146. }
  1147. double FOOTPRINT::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  1148. {
  1149. if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
  1150. {
  1151. // The locked shadow shape is shown only if the footprint itself is visible
  1152. if( ( m_layer == F_Cu ) && aView->IsLayerVisible( LAYER_MOD_FR ) )
  1153. return 0.0;
  1154. if( ( m_layer == B_Cu ) && aView->IsLayerVisible( LAYER_MOD_BK ) )
  1155. return 0.0;
  1156. return std::numeric_limits<double>::max();
  1157. }
  1158. if( aLayer == LAYER_CONFLICTS_SHADOW && IsConflicting() )
  1159. {
  1160. // The locked shadow shape is shown only if the footprint itself is visible
  1161. if( ( m_layer == F_Cu ) && aView->IsLayerVisible( LAYER_MOD_FR ) )
  1162. return 0.0;
  1163. if( ( m_layer == B_Cu ) && aView->IsLayerVisible( LAYER_MOD_BK ) )
  1164. return 0.0;
  1165. return std::numeric_limits<double>::max();
  1166. }
  1167. int layer = ( m_layer == F_Cu ) ? LAYER_MOD_FR :
  1168. ( m_layer == B_Cu ) ? LAYER_MOD_BK : LAYER_ANCHOR;
  1169. // Currently this is only pertinent for the anchor layer; everything else is drawn from the
  1170. // children.
  1171. // The "good" value is experimentally chosen.
  1172. #define MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY 1.5
  1173. if( aView->IsLayerVisible( layer ) )
  1174. return MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY;
  1175. return std::numeric_limits<double>::max();
  1176. }
  1177. const BOX2I FOOTPRINT::ViewBBox() const
  1178. {
  1179. BOX2I area = GetBoundingBox( true, true );
  1180. // Add the Clearance shape size: (shape around the pads when the clearance is shown. Not
  1181. // optimized, but the draw cost is small (perhaps smaller than optimization).
  1182. const BOARD* board = GetBoard();
  1183. if( board )
  1184. {
  1185. int biggest_clearance = board->GetDesignSettings().GetBiggestClearanceValue();
  1186. area.Inflate( biggest_clearance );
  1187. }
  1188. return area;
  1189. }
  1190. bool FOOTPRINT::IsLibNameValid( const wxString & aName )
  1191. {
  1192. const wxChar * invalids = StringLibNameInvalidChars( false );
  1193. if( aName.find_first_of( invalids ) != std::string::npos )
  1194. return false;
  1195. return true;
  1196. }
  1197. const wxChar* FOOTPRINT::StringLibNameInvalidChars( bool aUserReadable )
  1198. {
  1199. // This list of characters is also duplicated in validators.cpp and
  1200. // lib_id.cpp
  1201. // TODO: Unify forbidden character lists - Warning, invalid filename characters are not the same
  1202. // as invalid LIB_ID characters. We will need to separate the FP filenames from FP names before this
  1203. // can be unified
  1204. static const wxChar invalidChars[] = wxT("%$<>\t\n\r\"\\/:");
  1205. static const wxChar invalidCharsReadable[] = wxT("% $ < > 'tab' 'return' 'line feed' \\ \" / :");
  1206. if( aUserReadable )
  1207. return invalidCharsReadable;
  1208. else
  1209. return invalidChars;
  1210. }
  1211. void FOOTPRINT::Move( const VECTOR2I& aMoveVector )
  1212. {
  1213. VECTOR2I newpos = m_pos + aMoveVector;
  1214. SetPosition( newpos );
  1215. }
  1216. void FOOTPRINT::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  1217. {
  1218. EDA_ANGLE orientation = GetOrientation();
  1219. EDA_ANGLE newOrientation = orientation + aAngle;
  1220. VECTOR2I newpos = m_pos;
  1221. RotatePoint( newpos, aRotCentre, aAngle );
  1222. SetPosition( newpos );
  1223. SetOrientation( newOrientation );
  1224. m_reference->KeepUpright( orientation, newOrientation );
  1225. m_value->KeepUpright( orientation, newOrientation );
  1226. for( BOARD_ITEM* item : m_drawings )
  1227. {
  1228. if( item->Type() == PCB_FP_TEXT_T )
  1229. static_cast<FP_TEXT*>( item )->KeepUpright( orientation, newOrientation );
  1230. }
  1231. m_boundingBoxCacheTimeStamp = 0;
  1232. m_visibleBBoxCacheTimeStamp = 0;
  1233. m_textExcludedBBoxCacheTimeStamp = 0;
  1234. m_hullCacheTimeStamp = 0;
  1235. m_courtyard_cache_timestamp = 0;
  1236. }
  1237. void FOOTPRINT::SetLayerAndFlip( PCB_LAYER_ID aLayer )
  1238. {
  1239. wxASSERT( aLayer == F_Cu || aLayer == B_Cu );
  1240. if( aLayer != GetLayer() )
  1241. Flip( GetPosition(), true );
  1242. }
  1243. void FOOTPRINT::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
  1244. {
  1245. // Move footprint to its final position:
  1246. VECTOR2I finalPos = m_pos;
  1247. // Now Flip the footprint.
  1248. // Flipping a footprint is a specific transform: it is not mirrored like a text.
  1249. // We have to change the side, and ensure the footprint rotation is modified according to the
  1250. // transform, because this parameter is used in pick and place files, and when updating the
  1251. // footprint from library.
  1252. // When flipped around the X axis (Y coordinates changed) orientation is negated
  1253. // When flipped around the Y axis (X coordinates changed) orientation is 180 - old orient.
  1254. // Because it is specific to a footprint, we flip around the X axis, and after rotate 180 deg
  1255. MIRROR( finalPos.y, aCentre.y ); /// Mirror the Y position (around the X axis)
  1256. SetPosition( finalPos );
  1257. // Flip layer
  1258. BOARD_ITEM::SetLayer( FlipLayer( GetLayer() ) );
  1259. // Reverse mirror orientation.
  1260. m_orient = -m_orient;
  1261. m_orient.Normalize180();
  1262. // Mirror pads to other side of board.
  1263. for( PAD* pad : m_pads )
  1264. pad->Flip( m_pos, false );
  1265. // Mirror zones to other side of board.
  1266. for( ZONE* zone : m_fp_zones )
  1267. zone->Flip( m_pos, false );
  1268. // Mirror reference and value.
  1269. m_reference->Flip( m_pos, false );
  1270. m_value->Flip( m_pos, false );
  1271. // Reverse mirror footprint graphics and texts.
  1272. for( BOARD_ITEM* item : m_drawings )
  1273. {
  1274. switch( item->Type() )
  1275. {
  1276. case PCB_FP_SHAPE_T:
  1277. static_cast<FP_SHAPE*>( item )->Flip( m_pos, false );
  1278. break;
  1279. case PCB_FP_TEXT_T:
  1280. static_cast<FP_TEXT*>( item )->Flip( m_pos, false );
  1281. break;
  1282. case PCB_FP_TEXTBOX_T:
  1283. static_cast<FP_TEXTBOX*>( item )->Flip( m_pos, false );
  1284. break;
  1285. case PCB_FP_DIM_ALIGNED_T:
  1286. case PCB_FP_DIM_ORTHOGONAL_T:
  1287. case PCB_FP_DIM_RADIAL_T:
  1288. case PCB_FP_DIM_CENTER_T:
  1289. case PCB_FP_DIM_LEADER_T:
  1290. static_cast<PCB_DIMENSION_BASE*>( item )->Flip( m_pos, false );
  1291. break;
  1292. default:
  1293. wxMessageBox( wxString::Format( wxT( "FOOTPRINT::Flip() error: Unknown Draw Type %d" ),
  1294. (int)item->Type() ) );
  1295. break;
  1296. }
  1297. }
  1298. // Now rotate 180 deg if required
  1299. if( aFlipLeftRight )
  1300. Rotate( aCentre, ANGLE_180 );
  1301. m_boundingBoxCacheTimeStamp = 0;
  1302. m_visibleBBoxCacheTimeStamp = 0;
  1303. m_textExcludedBBoxCacheTimeStamp = 0;
  1304. m_courtyard_cache_timestamp = 0;
  1305. m_cachedHull.Mirror( aFlipLeftRight, !aFlipLeftRight, m_pos );
  1306. std::swap( m_courtyard_cache_front, m_courtyard_cache_back );
  1307. }
  1308. void FOOTPRINT::SetPosition( const VECTOR2I& aPos )
  1309. {
  1310. VECTOR2I delta = aPos - m_pos;
  1311. m_pos += delta;
  1312. m_reference->EDA_TEXT::Offset( delta );
  1313. m_value->EDA_TEXT::Offset( delta );
  1314. for( PAD* pad : m_pads )
  1315. pad->SetPosition( pad->GetPosition() + delta );
  1316. for( ZONE* zone : m_fp_zones )
  1317. zone->Move( delta );
  1318. for( BOARD_ITEM* item : m_drawings )
  1319. {
  1320. switch( item->Type() )
  1321. {
  1322. case PCB_FP_SHAPE_T:
  1323. case PCB_FP_TEXTBOX_T:
  1324. {
  1325. FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
  1326. shape->SetDrawCoord();
  1327. break;
  1328. }
  1329. case PCB_FP_TEXT_T:
  1330. {
  1331. FP_TEXT* text = static_cast<FP_TEXT*>( item );
  1332. text->EDA_TEXT::Offset( delta );
  1333. break;
  1334. }
  1335. case PCB_FP_DIM_ALIGNED_T:
  1336. case PCB_FP_DIM_CENTER_T:
  1337. case PCB_FP_DIM_ORTHOGONAL_T:
  1338. case PCB_FP_DIM_RADIAL_T:
  1339. case PCB_FP_DIM_LEADER_T:
  1340. case PCB_BITMAP_T:
  1341. item->Move( delta );
  1342. break;
  1343. default:
  1344. wxMessageBox( wxT( "Draw type undefined." ) );
  1345. break;
  1346. }
  1347. }
  1348. m_cachedBoundingBox.Move( delta );
  1349. m_cachedVisibleBBox.Move( delta );
  1350. m_cachedTextExcludedBBox.Move( delta );
  1351. m_courtyard_cache_back.Move( delta );
  1352. m_courtyard_cache_front.Move( delta );
  1353. m_cachedHull.Move( delta );
  1354. }
  1355. void FOOTPRINT::MoveAnchorPosition( const VECTOR2I& aMoveVector )
  1356. {
  1357. /*
  1358. * Move the reference point of the footprint
  1359. * the footprints elements (pads, outlines, edges .. ) are moved
  1360. * but:
  1361. * - the footprint position is not modified.
  1362. * - the relative (local) coordinates of these items are modified
  1363. * - Draw coordinates are updated
  1364. */
  1365. // Update (move) the relative coordinates relative to the new anchor point.
  1366. VECTOR2I moveVector = aMoveVector;
  1367. RotatePoint( moveVector, -GetOrientation() );
  1368. // Update of the reference and value.
  1369. m_reference->SetPos0( m_reference->GetPos0() + moveVector );
  1370. m_reference->SetDrawCoord();
  1371. m_value->SetPos0( m_value->GetPos0() + moveVector );
  1372. m_value->SetDrawCoord();
  1373. // Update the pad local coordinates.
  1374. for( PAD* pad : m_pads )
  1375. {
  1376. pad->SetPos0( pad->GetPos0() + moveVector );
  1377. pad->SetDrawCoord();
  1378. }
  1379. // Update the draw element coordinates.
  1380. for( BOARD_ITEM* item : GraphicalItems() )
  1381. {
  1382. switch( item->Type() )
  1383. {
  1384. case PCB_FP_SHAPE_T:
  1385. case PCB_FP_TEXTBOX_T:
  1386. {
  1387. FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
  1388. shape->Move( moveVector );
  1389. break;
  1390. }
  1391. case PCB_FP_TEXT_T:
  1392. {
  1393. FP_TEXT* text = static_cast<FP_TEXT*>( item );
  1394. text->SetPos0( text->GetPos0() + moveVector );
  1395. text->SetDrawCoord();
  1396. break;
  1397. }
  1398. default:
  1399. break;
  1400. }
  1401. }
  1402. // Update the keepout zones
  1403. for( ZONE* zone : Zones() )
  1404. {
  1405. zone->Move( moveVector );
  1406. }
  1407. // Update the 3D models
  1408. for( FP_3DMODEL& model : Models() )
  1409. {
  1410. model.m_Offset.x += pcbIUScale.IUTomm( moveVector.x );
  1411. model.m_Offset.y -= pcbIUScale.IUTomm( moveVector.y );
  1412. }
  1413. m_cachedBoundingBox.Move( moveVector );
  1414. m_cachedVisibleBBox.Move( moveVector );
  1415. m_cachedTextExcludedBBox.Move( moveVector );
  1416. m_cachedHull.Move( moveVector );
  1417. }
  1418. void FOOTPRINT::SetOrientation( const EDA_ANGLE& aNewAngle )
  1419. {
  1420. EDA_ANGLE angleChange = aNewAngle - m_orient; // change in rotation
  1421. m_orient = aNewAngle;
  1422. m_orient.Normalize180();
  1423. for( PAD* pad : m_pads )
  1424. {
  1425. pad->SetOrientation( pad->GetOrientation() + angleChange );
  1426. pad->SetDrawCoord();
  1427. }
  1428. for( ZONE* zone : m_fp_zones )
  1429. zone->Rotate( GetPosition(), angleChange );
  1430. for( BOARD_ITEM* item : m_drawings )
  1431. {
  1432. if( PCB_DIMENSION_BASE* dimension = dynamic_cast<PCB_DIMENSION_BASE*>( item ) )
  1433. dimension->Rotate( GetPosition(), angleChange );
  1434. }
  1435. // Update of the reference and value.
  1436. m_reference->SetDrawCoord();
  1437. m_value->SetDrawCoord();
  1438. // Displace contours and text of the footprint.
  1439. for( BOARD_ITEM* item : m_drawings )
  1440. {
  1441. switch( item->Type() )
  1442. {
  1443. case PCB_FP_SHAPE_T:
  1444. case PCB_FP_TEXTBOX_T:
  1445. static_cast<FP_SHAPE*>( item )->SetDrawCoord();
  1446. break;
  1447. case PCB_FP_TEXT_T:
  1448. static_cast<FP_TEXT*>( item )->SetDrawCoord();
  1449. break;
  1450. default:
  1451. break;
  1452. }
  1453. }
  1454. m_boundingBoxCacheTimeStamp = 0;
  1455. m_visibleBBoxCacheTimeStamp = 0;
  1456. m_textExcludedBBoxCacheTimeStamp = 0;
  1457. m_courtyard_cache_timestamp = 0;
  1458. m_cachedHull.Rotate( angleChange, GetPosition() );
  1459. }
  1460. BOARD_ITEM* FOOTPRINT::Duplicate() const
  1461. {
  1462. FOOTPRINT* dupe = static_cast<FOOTPRINT*>( BOARD_ITEM::Duplicate() );
  1463. dupe->RunOnChildren( [&]( BOARD_ITEM* child )
  1464. {
  1465. const_cast<KIID&>( child->m_Uuid ) = KIID();
  1466. });
  1467. return dupe;
  1468. }
  1469. BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootprint )
  1470. {
  1471. BOARD_ITEM* new_item = nullptr;
  1472. FP_ZONE* new_zone = nullptr;
  1473. switch( aItem->Type() )
  1474. {
  1475. case PCB_PAD_T:
  1476. {
  1477. PAD* new_pad = new PAD( *static_cast<const PAD*>( aItem ) );
  1478. const_cast<KIID&>( new_pad->m_Uuid ) = KIID();
  1479. if( aAddToFootprint )
  1480. m_pads.push_back( new_pad );
  1481. new_item = new_pad;
  1482. break;
  1483. }
  1484. case PCB_FP_ZONE_T:
  1485. {
  1486. new_zone = new FP_ZONE( *static_cast<const FP_ZONE*>( aItem ) );
  1487. const_cast<KIID&>( new_zone->m_Uuid ) = KIID();
  1488. if( aAddToFootprint )
  1489. m_fp_zones.push_back( new_zone );
  1490. new_item = new_zone;
  1491. break;
  1492. }
  1493. case PCB_FP_TEXT_T:
  1494. {
  1495. FP_TEXT* new_text = new FP_TEXT( *static_cast<const FP_TEXT*>( aItem ) );
  1496. const_cast<KIID&>( new_text->m_Uuid ) = KIID();
  1497. if( new_text->GetType() == FP_TEXT::TEXT_is_REFERENCE )
  1498. {
  1499. new_text->SetText( wxT( "${REFERENCE}" ) );
  1500. new_text->SetType( FP_TEXT::TEXT_is_DIVERS );
  1501. }
  1502. else if( new_text->GetType() == FP_TEXT::TEXT_is_VALUE )
  1503. {
  1504. new_text->SetText( wxT( "${VALUE}" ) );
  1505. new_text->SetType( FP_TEXT::TEXT_is_DIVERS );
  1506. }
  1507. if( aAddToFootprint )
  1508. Add( new_text );
  1509. new_item = new_text;
  1510. break;
  1511. }
  1512. case PCB_FP_SHAPE_T:
  1513. {
  1514. FP_SHAPE* new_shape = new FP_SHAPE( *static_cast<const FP_SHAPE*>( aItem ) );
  1515. const_cast<KIID&>( new_shape->m_Uuid ) = KIID();
  1516. if( aAddToFootprint )
  1517. Add( new_shape );
  1518. new_item = new_shape;
  1519. break;
  1520. }
  1521. case PCB_FP_TEXTBOX_T:
  1522. {
  1523. FP_TEXTBOX* new_textbox = new FP_TEXTBOX( *static_cast<const FP_TEXTBOX*>( aItem ) );
  1524. const_cast<KIID&>( new_textbox->m_Uuid ) = KIID();
  1525. if( aAddToFootprint )
  1526. Add( new_textbox );
  1527. new_item = new_textbox;
  1528. break;
  1529. }
  1530. case PCB_FP_DIM_ALIGNED_T:
  1531. case PCB_FP_DIM_LEADER_T:
  1532. case PCB_FP_DIM_CENTER_T:
  1533. case PCB_FP_DIM_RADIAL_T:
  1534. case PCB_FP_DIM_ORTHOGONAL_T:
  1535. {
  1536. PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( aItem->Duplicate() );
  1537. if( aAddToFootprint )
  1538. Add( dimension );
  1539. new_item = dimension;
  1540. break;
  1541. }
  1542. case PCB_GROUP_T:
  1543. new_item = static_cast<const PCB_GROUP*>( aItem )->DeepDuplicate();
  1544. break;
  1545. case PCB_FOOTPRINT_T:
  1546. // Ignore the footprint itself
  1547. break;
  1548. default:
  1549. // Un-handled item for duplication
  1550. wxFAIL_MSG( wxT( "Duplication not supported for items of class " ) + aItem->GetClass() );
  1551. break;
  1552. }
  1553. return new_item;
  1554. }
  1555. wxString FOOTPRINT::GetNextPadNumber( const wxString& aLastPadNumber ) const
  1556. {
  1557. std::set<wxString> usedNumbers;
  1558. // Create a set of used pad numbers
  1559. for( PAD* pad : m_pads )
  1560. usedNumbers.insert( pad->GetNumber() );
  1561. // Pad numbers aren't technically reference designators, but the formatting is close enough
  1562. // for these to give us what we need.
  1563. wxString prefix = UTIL::GetRefDesPrefix( aLastPadNumber );
  1564. int num = GetTrailingInt( aLastPadNumber );
  1565. while( usedNumbers.count( wxString::Format( wxT( "%s%d" ), prefix, num ) ) )
  1566. num++;
  1567. return wxString::Format( wxT( "%s%d" ), prefix, num );
  1568. }
  1569. void FOOTPRINT::IncrementReference( int aDelta )
  1570. {
  1571. const wxString& refdes = GetReference();
  1572. SetReference( wxString::Format( wxT( "%s%i" ),
  1573. UTIL::GetRefDesPrefix( refdes ),
  1574. GetTrailingInt( refdes ) + aDelta ) );
  1575. }
  1576. // Calculate the area of a PolySet, polygons with hole are allowed.
  1577. static double polygonArea( SHAPE_POLY_SET& aPolySet )
  1578. {
  1579. // Ensure all outlines are closed, before calculating the SHAPE_POLY_SET area
  1580. for( int ii = 0; ii < aPolySet.OutlineCount(); ii++ )
  1581. {
  1582. SHAPE_LINE_CHAIN& outline = aPolySet.Outline( ii );
  1583. outline.SetClosed( true );
  1584. for( int jj = 0; jj < aPolySet.HoleCount( ii ); jj++ )
  1585. aPolySet.Hole( ii, jj ).SetClosed( true );
  1586. }
  1587. return aPolySet.Area();
  1588. }
  1589. double FOOTPRINT::GetCoverageArea( const BOARD_ITEM* aItem, const GENERAL_COLLECTOR& aCollector )
  1590. {
  1591. int textMargin = KiROUND( 5 * aCollector.GetGuide()->OnePixelInIU() );
  1592. SHAPE_POLY_SET poly;
  1593. if( aItem->Type() == PCB_MARKER_T )
  1594. {
  1595. const PCB_MARKER* marker = static_cast<const PCB_MARKER*>( aItem );
  1596. SHAPE_LINE_CHAIN markerShape;
  1597. marker->ShapeToPolygon( markerShape );
  1598. return markerShape.Area();
  1599. }
  1600. else if( aItem->Type() == PCB_GROUP_T )
  1601. {
  1602. double combinedArea = 0.0;
  1603. for( BOARD_ITEM* member : static_cast<const PCB_GROUP*>( aItem )->GetItems() )
  1604. combinedArea += GetCoverageArea( member, aCollector );
  1605. return combinedArea;
  1606. }
  1607. if( aItem->Type() == PCB_FOOTPRINT_T )
  1608. {
  1609. const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
  1610. poly = footprint->GetBoundingHull();
  1611. }
  1612. else if( aItem->Type() == PCB_FP_TEXT_T )
  1613. {
  1614. const FP_TEXT* text = static_cast<const FP_TEXT*>( aItem );
  1615. text->TransformTextToPolySet( poly, UNDEFINED_LAYER, textMargin, ARC_LOW_DEF, ERROR_OUTSIDE );
  1616. }
  1617. else if( aItem->Type() == PCB_FP_TEXTBOX_T )
  1618. {
  1619. const FP_TEXTBOX* tb = static_cast<const FP_TEXTBOX*>( aItem );
  1620. tb->TransformTextToPolySet( poly, UNDEFINED_LAYER, textMargin, ARC_LOW_DEF, ERROR_OUTSIDE );
  1621. }
  1622. else if( aItem->Type() == PCB_SHAPE_T )
  1623. {
  1624. // Approximate "linear" shapes with just their width squared, as we don't want to consider
  1625. // a linear shape as being much bigger than another for purposes of selection filtering
  1626. // just because it happens to be really long.
  1627. const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
  1628. switch( shape->GetShape() )
  1629. {
  1630. case SHAPE_T::SEGMENT:
  1631. case SHAPE_T::ARC:
  1632. case SHAPE_T::BEZIER:
  1633. return shape->GetWidth() * shape->GetWidth();
  1634. case SHAPE_T::RECT:
  1635. case SHAPE_T::CIRCLE:
  1636. case SHAPE_T::POLY:
  1637. {
  1638. if( !shape->IsFilled() )
  1639. return shape->GetWidth() * shape->GetWidth();
  1640. KI_FALLTHROUGH;
  1641. }
  1642. default:
  1643. shape->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
  1644. }
  1645. }
  1646. else if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_ARC_T )
  1647. {
  1648. double width = static_cast<const PCB_TRACK*>( aItem )->GetWidth();
  1649. return width * width;
  1650. }
  1651. else
  1652. {
  1653. aItem->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
  1654. }
  1655. return polygonArea( poly );
  1656. }
  1657. double FOOTPRINT::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
  1658. {
  1659. int textMargin = KiROUND( 5 * aCollector.GetGuide()->OnePixelInIU() );
  1660. SHAPE_POLY_SET footprintRegion( GetBoundingHull() );
  1661. SHAPE_POLY_SET coveredRegion;
  1662. TransformPadsToPolySet( coveredRegion, UNDEFINED_LAYER, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
  1663. TransformFPShapesToPolySet( coveredRegion, UNDEFINED_LAYER, textMargin, ARC_LOW_DEF,
  1664. ERROR_OUTSIDE,
  1665. true, /* include text */
  1666. false, /* include shapes */
  1667. false /* include private items */ );
  1668. for( int i = 0; i < aCollector.GetCount(); ++i )
  1669. {
  1670. const BOARD_ITEM* item = aCollector[i];
  1671. switch( item->Type() )
  1672. {
  1673. case PCB_FP_TEXT_T:
  1674. case PCB_FP_TEXTBOX_T:
  1675. case PCB_FP_SHAPE_T:
  1676. if( item->GetParent() != this )
  1677. {
  1678. item->TransformShapeToPolygon( coveredRegion, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
  1679. ERROR_OUTSIDE );
  1680. }
  1681. break;
  1682. case PCB_TEXT_T:
  1683. case PCB_TEXTBOX_T:
  1684. case PCB_SHAPE_T:
  1685. case PCB_TRACE_T:
  1686. case PCB_ARC_T:
  1687. case PCB_VIA_T:
  1688. item->TransformShapeToPolygon( coveredRegion, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
  1689. ERROR_OUTSIDE );
  1690. break;
  1691. case PCB_FOOTPRINT_T:
  1692. if( item != this )
  1693. {
  1694. const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( item );
  1695. coveredRegion.AddOutline( footprint->GetBoundingHull().Outline( 0 ) );
  1696. }
  1697. break;
  1698. default:
  1699. break;
  1700. }
  1701. }
  1702. double footprintRegionArea = polygonArea( footprintRegion );
  1703. double uncoveredRegionArea = footprintRegionArea - polygonArea( coveredRegion );
  1704. double coveredArea = footprintRegionArea - uncoveredRegionArea;
  1705. double ratio = ( coveredArea / footprintRegionArea );
  1706. // Test for negative ratio (should not occur).
  1707. // better to be conservative (this will result in the disambiguate dialog)
  1708. if( ratio < 0.0 )
  1709. return 1.0;
  1710. return std::min( ratio, 1.0 );
  1711. }
  1712. std::shared_ptr<SHAPE> FOOTPRINT::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  1713. {
  1714. std::shared_ptr<SHAPE_COMPOUND> shape = std::make_shared<SHAPE_COMPOUND>();
  1715. // There are several possible interpretations here:
  1716. // 1) the bounding box (without or without invisible items)
  1717. // 2) just the pads and "edges" (ie: non-text graphic items)
  1718. // 3) the courtyard
  1719. // We'll go with (2) for now, unless the caller is clearly looking for (3)
  1720. if( aLayer == F_CrtYd || aLayer == B_CrtYd )
  1721. {
  1722. const SHAPE_POLY_SET& courtyard = GetCourtyard( aLayer );
  1723. if( courtyard.OutlineCount() == 0 ) // malformed/empty polygon
  1724. return shape;
  1725. shape->AddShape( new SHAPE_SIMPLE( courtyard.COutline( 0 ) ) );
  1726. }
  1727. else
  1728. {
  1729. for( PAD* pad : Pads() )
  1730. shape->AddShape( pad->GetEffectiveShape( aLayer, aFlash )->Clone() );
  1731. for( BOARD_ITEM* item : GraphicalItems() )
  1732. {
  1733. if( item->Type() == PCB_FP_SHAPE_T )
  1734. shape->AddShape( item->GetEffectiveShape( aLayer, aFlash )->Clone() );
  1735. }
  1736. }
  1737. return shape;
  1738. }
  1739. const SHAPE_POLY_SET& FOOTPRINT::GetCourtyard( PCB_LAYER_ID aLayer ) const
  1740. {
  1741. if( GetBoard() && GetBoard()->GetTimeStamp() > m_courtyard_cache_timestamp )
  1742. const_cast<FOOTPRINT*>( this )->BuildCourtyardCaches();
  1743. if( IsBackLayer( aLayer ) )
  1744. return m_courtyard_cache_back;
  1745. else
  1746. return m_courtyard_cache_front;
  1747. }
  1748. void FOOTPRINT::BuildCourtyardCaches( OUTLINE_ERROR_HANDLER* aErrorHandler )
  1749. {
  1750. m_courtyard_cache_front.RemoveAllContours();
  1751. m_courtyard_cache_back.RemoveAllContours();
  1752. ClearFlags( MALFORMED_COURTYARDS );
  1753. m_courtyard_cache_timestamp = GetBoard()->GetTimeStamp();
  1754. // Build the courtyard area from graphic items on the courtyard.
  1755. // Only PCB_FP_SHAPE_T have meaning, graphic texts are ignored.
  1756. // Collect items:
  1757. std::vector<PCB_SHAPE*> list_front;
  1758. std::vector<PCB_SHAPE*> list_back;
  1759. for( BOARD_ITEM* item : GraphicalItems() )
  1760. {
  1761. if( item->GetLayer() == B_CrtYd && item->Type() == PCB_FP_SHAPE_T )
  1762. list_back.push_back( static_cast<PCB_SHAPE*>( item ) );
  1763. if( item->GetLayer() == F_CrtYd && item->Type() == PCB_FP_SHAPE_T )
  1764. list_front.push_back( static_cast<PCB_SHAPE*>( item ) );
  1765. }
  1766. if( !list_front.size() && !list_back.size() )
  1767. return;
  1768. int errorMax = pcbIUScale.mmToIU( 0.02 ); // max error for polygonization
  1769. int chainingEpsilon = pcbIUScale.mmToIU( 0.02 ); // max dist from one endPt to next startPt
  1770. if( ConvertOutlineToPolygon( list_front, m_courtyard_cache_front, errorMax, chainingEpsilon,
  1771. true, aErrorHandler ) )
  1772. {
  1773. // Touching courtyards, or courtyards -at- the clearance distance are legal.
  1774. m_courtyard_cache_front.Inflate( -1, SHAPE_POLY_SET::CHAMFER_ACUTE_CORNERS );
  1775. m_courtyard_cache_front.CacheTriangulation( false );
  1776. }
  1777. else
  1778. {
  1779. SetFlags( MALFORMED_F_COURTYARD );
  1780. }
  1781. if( ConvertOutlineToPolygon( list_back, m_courtyard_cache_back, errorMax, chainingEpsilon,
  1782. true, aErrorHandler ) )
  1783. {
  1784. // Touching courtyards, or courtyards -at- the clearance distance are legal.
  1785. m_courtyard_cache_back.Inflate( -1, SHAPE_POLY_SET::CHAMFER_ACUTE_CORNERS );
  1786. m_courtyard_cache_back.CacheTriangulation( false );
  1787. }
  1788. else
  1789. {
  1790. SetFlags( MALFORMED_B_COURTYARD );
  1791. }
  1792. }
  1793. std::map<wxString, int> FOOTPRINT::MapPadNumbersToNetTieGroups() const
  1794. {
  1795. std::map<wxString, int> padNumberToGroupIdxMap;
  1796. for( const PAD* pad : m_pads )
  1797. padNumberToGroupIdxMap[ pad->GetNumber() ] = -1;
  1798. for( size_t ii = 0; ii < m_netTiePadGroups.size(); ++ii )
  1799. {
  1800. wxStringTokenizer groupParser( m_netTiePadGroups[ ii ], "," );
  1801. std::vector<wxString> numbersInGroup;
  1802. while( groupParser.HasMoreTokens() )
  1803. padNumberToGroupIdxMap[ groupParser.GetNextToken().Trim( false ).Trim( true ) ] = ii;
  1804. }
  1805. return padNumberToGroupIdxMap;
  1806. }
  1807. std::vector<PAD*> FOOTPRINT::GetNetTiePads( PAD* aPad ) const
  1808. {
  1809. // First build a map from pad numbers to allowed-shorting-group indexes. This ends up being
  1810. // something like O(3n), but it still beats O(n^2) for large numbers of pads.
  1811. std::map<wxString, int> padToNetTieGroupMap = MapPadNumbersToNetTieGroups();
  1812. int groupIdx = padToNetTieGroupMap[ aPad->GetNumber() ];
  1813. std::vector<PAD*> otherPads;
  1814. if( groupIdx >= 0 )
  1815. {
  1816. for( PAD* pad : m_pads )
  1817. {
  1818. if( padToNetTieGroupMap[ pad->GetNumber() ] == groupIdx )
  1819. otherPads.push_back( pad );
  1820. }
  1821. }
  1822. return otherPads;
  1823. }
  1824. void FOOTPRINT::CheckFootprintAttributes( const std::function<void( const wxString& )>& aErrorHandler )
  1825. {
  1826. int likelyAttr = ( GetLikelyAttribute() & ( FP_SMD | FP_THROUGH_HOLE ) );
  1827. int setAttr = ( GetAttributes() & ( FP_SMD | FP_THROUGH_HOLE ) );
  1828. if( setAttr && likelyAttr && setAttr != likelyAttr )
  1829. {
  1830. wxString msg;
  1831. switch( likelyAttr )
  1832. {
  1833. case FP_THROUGH_HOLE:
  1834. msg.Printf( _( "(expected 'Through hole'; actual '%s')" ), GetTypeName() );
  1835. break;
  1836. case FP_SMD:
  1837. msg.Printf( _( "(expected 'SMD'; actual '%s')" ), GetTypeName() );
  1838. break;
  1839. }
  1840. if( aErrorHandler )
  1841. (aErrorHandler)( msg );
  1842. }
  1843. }
  1844. void FOOTPRINT::CheckPads( const std::function<void( const PAD*, int,
  1845. const wxString& )>& aErrorHandler )
  1846. {
  1847. if( aErrorHandler == nullptr )
  1848. return;
  1849. for( PAD* pad: Pads() )
  1850. {
  1851. if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
  1852. {
  1853. if( pad->GetDrillSizeX() < 1 || pad->GetDrillSizeY() < 1 )
  1854. (aErrorHandler)( pad, DRCE_PAD_TH_WITH_NO_HOLE, wxEmptyString );
  1855. }
  1856. if( pad->GetAttribute() == PAD_ATTRIB::PTH )
  1857. {
  1858. if( !pad->IsOnCopperLayer() )
  1859. {
  1860. (aErrorHandler)( pad, DRCE_PADSTACK, _( "(PTH pad has no copper layers)" ) );
  1861. }
  1862. else
  1863. {
  1864. LSET lset = pad->GetLayerSet() & LSET::AllCuMask();
  1865. PCB_LAYER_ID layer = lset.Seq().at( 0 );
  1866. SHAPE_POLY_SET padOutline;
  1867. pad->TransformShapeToPolygon( padOutline, layer, 0, ARC_HIGH_DEF, ERROR_INSIDE );
  1868. std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
  1869. SHAPE_POLY_SET holeOutline;
  1870. TransformOvalToPolygon( holeOutline, hole->GetSeg().A, hole->GetSeg().B,
  1871. hole->GetWidth(), ARC_HIGH_DEF, ERROR_INSIDE );
  1872. padOutline.BooleanSubtract( holeOutline, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
  1873. if( padOutline.IsEmpty() )
  1874. aErrorHandler( pad, DRCE_PADSTACK, _( "(PTH pad's hole leaves no copper)" ) );
  1875. }
  1876. }
  1877. if( pad->GetAttribute() == PAD_ATTRIB::SMD )
  1878. {
  1879. if( pad->IsOnLayer( F_Cu ) && pad->IsOnLayer( B_Cu ) )
  1880. {
  1881. aErrorHandler( pad, DRCE_PADSTACK,
  1882. _( "(SMD pad appears on both front and back copper)" ) );
  1883. }
  1884. else if( pad->IsOnLayer( F_Cu ) )
  1885. {
  1886. if( pad->IsOnLayer( B_Mask ) )
  1887. {
  1888. aErrorHandler( pad, DRCE_PADSTACK,
  1889. _( "(SMD pad copper and mask layers don't match)" ) );
  1890. }
  1891. else if( pad->IsOnLayer( B_Paste ) )
  1892. {
  1893. aErrorHandler( pad, DRCE_PADSTACK,
  1894. _( "(SMD pad copper and paste layers don't match)" ) );
  1895. }
  1896. }
  1897. else if( pad->IsOnLayer( B_Cu ) )
  1898. {
  1899. if( pad->IsOnLayer( F_Mask ) )
  1900. {
  1901. aErrorHandler( pad, DRCE_PADSTACK,
  1902. _( "(SMD pad copper and mask layers don't match)" ) );
  1903. }
  1904. else if( pad->IsOnLayer( F_Paste ) )
  1905. {
  1906. aErrorHandler( pad, DRCE_PADSTACK,
  1907. _( "(SMD pad copper and paste layers don't match)" ) );
  1908. }
  1909. }
  1910. }
  1911. }
  1912. }
  1913. void FOOTPRINT::CheckShortingPads( const std::function<void( const PAD*, const PAD*,
  1914. const VECTOR2I& )>& aErrorHandler )
  1915. {
  1916. std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
  1917. for( PAD* pad : Pads() )
  1918. {
  1919. std::vector<PAD*> netTiePads = GetNetTiePads( pad );
  1920. for( PAD* other : Pads() )
  1921. {
  1922. if( other == pad || pad->SameLogicalPadAs( other ) )
  1923. continue;
  1924. if( alg::contains( netTiePads, other ) )
  1925. continue;
  1926. if( !( ( pad->GetLayerSet() & other->GetLayerSet() ) & LSET::AllCuMask() ).any() )
  1927. continue;
  1928. // store canonical order so we don't collide in both directions (a:b and b:a)
  1929. PAD* a = pad;
  1930. PAD* b = other;
  1931. if( static_cast<void*>( a ) > static_cast<void*>( b ) )
  1932. std::swap( a, b );
  1933. if( checkedPairs.find( { a, b } ) == checkedPairs.end() )
  1934. {
  1935. checkedPairs[ { a, b } ] = 1;
  1936. if( pad->GetBoundingBox().Intersects( other->GetBoundingBox() ) )
  1937. {
  1938. VECTOR2I pos;
  1939. SHAPE* padShape = pad->GetEffectiveShape().get();
  1940. SHAPE* otherShape = other->GetEffectiveShape().get();
  1941. if( padShape->Collide( otherShape, 0, nullptr, &pos ) )
  1942. aErrorHandler( pad, other, pos );
  1943. }
  1944. }
  1945. }
  1946. }
  1947. }
  1948. void FOOTPRINT::CheckNetTies( const std::function<void( const BOARD_ITEM* aItem,
  1949. const BOARD_ITEM* bItem,
  1950. const BOARD_ITEM* cItem,
  1951. const VECTOR2I& )>& aErrorHandler )
  1952. {
  1953. // First build a map from pad numbers to allowed-shorting-group indexes. This ends up being
  1954. // something like O(3n), but it still beats O(n^2) for large numbers of pads.
  1955. std::map<wxString, int> padNumberToGroupIdxMap = MapPadNumbersToNetTieGroups();
  1956. // Now collect all the footprint items which are on copper layers
  1957. std::vector<BOARD_ITEM*> copperItems;
  1958. for( BOARD_ITEM* item : m_drawings )
  1959. {
  1960. if( item->IsOnCopperLayer() )
  1961. copperItems.push_back( item );
  1962. }
  1963. for( ZONE* zone : m_fp_zones )
  1964. {
  1965. if( !zone->GetIsRuleArea() && zone->IsOnCopperLayer() )
  1966. copperItems.push_back( zone );
  1967. }
  1968. if( m_reference->IsOnCopperLayer() )
  1969. copperItems.push_back( m_reference );
  1970. if( m_value->IsOnCopperLayer() )
  1971. copperItems.push_back( m_value );
  1972. for( PCB_LAYER_ID layer : { F_Cu, In1_Cu, B_Cu } )
  1973. {
  1974. // Next, build a polygon-set for the copper on this layer. We don't really care about
  1975. // nets here, we just want to end up with a set of outlines describing the distinct
  1976. // copper polygons of the footprint.
  1977. SHAPE_POLY_SET copperOutlines;
  1978. std::map<int, std::vector<const PAD*>> outlineIdxToPadsMap;
  1979. for( BOARD_ITEM* item : copperItems )
  1980. {
  1981. if( item->IsOnLayer( layer ) )
  1982. {
  1983. item->TransformShapeToPolygon( copperOutlines, layer, 0, ARC_HIGH_DEF,
  1984. ERROR_OUTSIDE );
  1985. }
  1986. }
  1987. copperOutlines.Simplify( SHAPE_POLY_SET::PM_FAST );
  1988. // Index each pad to the outline in the set that it is part of.
  1989. for( const PAD* pad : m_pads )
  1990. {
  1991. for( int ii = 0; ii < copperOutlines.OutlineCount(); ++ii )
  1992. {
  1993. if( pad->GetEffectiveShape( layer )->Collide( &copperOutlines.Outline( ii ), 0 ) )
  1994. outlineIdxToPadsMap[ ii ].emplace_back( pad );
  1995. }
  1996. }
  1997. // Finally, ensure that each outline which contains multiple pads has all its pads
  1998. // listed in an allowed-shorting group.
  1999. for( const auto& [ outlineIdx, pads ] : outlineIdxToPadsMap )
  2000. {
  2001. if( pads.size() > 1 )
  2002. {
  2003. const PAD* firstPad = pads[0];
  2004. int firstGroupIdx = padNumberToGroupIdxMap[ firstPad->GetNumber() ];
  2005. for( size_t ii = 1; ii < pads.size(); ++ii )
  2006. {
  2007. const PAD* thisPad = pads[ii];
  2008. int thisGroupIdx = padNumberToGroupIdxMap[ thisPad->GetNumber() ];
  2009. if( thisGroupIdx < 0 || thisGroupIdx != firstGroupIdx )
  2010. {
  2011. BOARD_ITEM* shortingItem = nullptr;
  2012. VECTOR2I pos = ( firstPad->GetPosition() + thisPad->GetPosition() ) / 2;
  2013. pos = copperOutlines.Outline( outlineIdx ).NearestPoint( pos );
  2014. for( BOARD_ITEM* item : copperItems )
  2015. {
  2016. if( item->HitTest( pos, 1 ) )
  2017. {
  2018. shortingItem = item;
  2019. break;
  2020. }
  2021. }
  2022. if( shortingItem )
  2023. aErrorHandler( shortingItem, firstPad, thisPad, pos );
  2024. else
  2025. aErrorHandler( firstPad, thisPad, nullptr, pos );
  2026. }
  2027. }
  2028. }
  2029. }
  2030. }
  2031. }
  2032. void FOOTPRINT::CheckNetTiePadGroups( const std::function<void( const wxString& )>& aErrorHandler )
  2033. {
  2034. std::set<wxString> padNumbers;
  2035. wxString msg;
  2036. for( size_t ii = 0; ii < m_netTiePadGroups.size(); ++ii )
  2037. {
  2038. wxStringTokenizer groupParser( m_netTiePadGroups[ ii ], "," );
  2039. while( groupParser.HasMoreTokens() )
  2040. {
  2041. wxString padNumber( groupParser.GetNextToken().Trim( false ).Trim( true ) );
  2042. const PAD* pad = FindPadByNumber( padNumber );
  2043. if( !pad )
  2044. {
  2045. msg.Printf( _( "(net-tie pad group contains unknown pad number %s)" ), padNumber );
  2046. aErrorHandler( msg );
  2047. }
  2048. else if( !padNumbers.insert( pad->GetNumber() ).second )
  2049. {
  2050. msg.Printf( _( "(pad %s appears in more than one net-tie pad group)" ), padNumber );
  2051. aErrorHandler( msg );
  2052. }
  2053. }
  2054. }
  2055. }
  2056. void FOOTPRINT::swapData( BOARD_ITEM* aImage )
  2057. {
  2058. wxASSERT( aImage->Type() == PCB_FOOTPRINT_T );
  2059. std::swap( *this, *static_cast<FOOTPRINT*>( aImage ) );
  2060. }
  2061. bool FOOTPRINT::HasThroughHolePads() const
  2062. {
  2063. for( PAD* pad : Pads() )
  2064. {
  2065. if( pad->GetAttribute() != PAD_ATTRIB::SMD )
  2066. return true;
  2067. }
  2068. return false;
  2069. }
  2070. #define TEST( a, b ) { if( a != b ) return a < b; }
  2071. #define TEST_PT( a, b ) { if( a.x != b.x ) return a.x < b.x; if( a.y != b.y ) return a.y < b.y; }
  2072. bool FOOTPRINT::cmp_drawings::operator()( const BOARD_ITEM* itemA, const BOARD_ITEM* itemB ) const
  2073. {
  2074. TEST( itemA->Type(), itemB->Type() );
  2075. TEST( itemA->GetLayer(), itemB->GetLayer() );
  2076. if( itemA->Type() == PCB_FP_SHAPE_T )
  2077. {
  2078. const FP_SHAPE* dwgA = static_cast<const FP_SHAPE*>( itemA );
  2079. const FP_SHAPE* dwgB = static_cast<const FP_SHAPE*>( itemB );
  2080. TEST( dwgA->GetShape(), dwgB->GetShape() );
  2081. TEST_PT( dwgA->GetStart0(), dwgB->GetStart0() );
  2082. TEST_PT( dwgA->GetEnd0(), dwgB->GetEnd0() );
  2083. if( dwgA->GetShape() == SHAPE_T::ARC )
  2084. {
  2085. TEST_PT( dwgA->GetCenter0(), dwgB->GetCenter0() );
  2086. }
  2087. else if( dwgA->GetShape() == SHAPE_T::BEZIER )
  2088. {
  2089. TEST_PT( dwgA->GetBezierC1_0(), dwgB->GetBezierC1_0() );
  2090. TEST_PT( dwgA->GetBezierC2_0(), dwgB->GetBezierC2_0() );
  2091. }
  2092. else if( dwgA->GetShape() == SHAPE_T::POLY )
  2093. {
  2094. TEST( dwgA->GetPolyShape().TotalVertices(), dwgB->GetPolyShape().TotalVertices() );
  2095. for( int ii = 0; ii < dwgA->GetPolyShape().TotalVertices(); ++ii )
  2096. TEST_PT( dwgA->GetPolyShape().CVertex( ii ), dwgB->GetPolyShape().CVertex( ii ) );
  2097. }
  2098. TEST( dwgA->GetWidth(), dwgB->GetWidth() );
  2099. }
  2100. TEST( itemA->m_Uuid, itemB->m_Uuid ); // should be always the case for valid boards
  2101. return itemA < itemB;
  2102. }
  2103. bool FOOTPRINT::cmp_pads::operator()( const PAD* aFirst, const PAD* aSecond ) const
  2104. {
  2105. if( aFirst->GetNumber() != aSecond->GetNumber() )
  2106. return StrNumCmp( aFirst->GetNumber(), aSecond->GetNumber() ) < 0;
  2107. TEST_PT( aFirst->GetPos0(), aSecond->GetPos0() );
  2108. TEST_PT( aFirst->GetSize(), aSecond->GetSize() );
  2109. TEST( aFirst->GetShape(), aSecond->GetShape() );
  2110. TEST( aFirst->GetLayerSet().Seq(), aSecond->GetLayerSet().Seq() );
  2111. TEST( aFirst->m_Uuid, aSecond->m_Uuid ); // should be always the case for valid boards
  2112. return aFirst < aSecond;
  2113. }
  2114. bool FOOTPRINT::cmp_zones::operator()( const FP_ZONE* aFirst, const FP_ZONE* aSecond ) const
  2115. {
  2116. TEST( aFirst->GetAssignedPriority(), aSecond->GetAssignedPriority() );
  2117. TEST( aFirst->GetLayerSet().Seq(), aSecond->GetLayerSet().Seq() );
  2118. TEST( aFirst->Outline()->TotalVertices(), aSecond->Outline()->TotalVertices() );
  2119. for( int ii = 0; ii < aFirst->Outline()->TotalVertices(); ++ii )
  2120. TEST_PT( aFirst->Outline()->CVertex( ii ), aSecond->Outline()->CVertex( ii ) );
  2121. TEST( aFirst->m_Uuid, aSecond->m_Uuid ); // should be always the case for valid boards
  2122. return aFirst < aSecond;
  2123. }
  2124. #undef TEST
  2125. void FOOTPRINT::TransformPadsToPolySet( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
  2126. int aClearance, int aMaxError, ERROR_LOC aErrorLoc,
  2127. bool aSkipNPTHPadsWihNoCopper, bool aSkipPlatedPads,
  2128. bool aSkipNonPlatedPads ) const
  2129. {
  2130. for( const PAD* pad : m_pads )
  2131. {
  2132. if( !pad->FlashLayer( aLayer ) )
  2133. continue;
  2134. VECTOR2I clearance( aClearance, aClearance );
  2135. switch( aLayer )
  2136. {
  2137. case F_Cu:
  2138. if( aSkipPlatedPads && pad->FlashLayer( F_Mask ) )
  2139. continue;
  2140. if( aSkipNonPlatedPads && !pad->FlashLayer( F_Mask ) )
  2141. continue;
  2142. break;
  2143. case B_Cu:
  2144. if( aSkipPlatedPads && pad->FlashLayer( B_Mask ) )
  2145. continue;
  2146. if( aSkipNonPlatedPads && !pad->FlashLayer( B_Mask ) )
  2147. continue;
  2148. break;
  2149. case F_Mask:
  2150. case B_Mask:
  2151. clearance.x += pad->GetSolderMaskExpansion();
  2152. clearance.y += pad->GetSolderMaskExpansion();
  2153. break;
  2154. case F_Paste:
  2155. case B_Paste:
  2156. clearance += pad->GetSolderPasteMargin();
  2157. break;
  2158. default:
  2159. break;
  2160. }
  2161. // Our standard TransformShapeToPolygon() routines can't handle differing x:y clearance
  2162. // values (which get generated when a relative paste margin is used with an oblong pad).
  2163. // So we apply this huge hack and fake a larger pad to run the transform on.
  2164. // Of course being a hack it falls down when dealing with custom shape pads (where the
  2165. // size is only the size of the anchor), so for those we punt and just use clearance.x.
  2166. if( ( clearance.x < 0 || clearance.x != clearance.y )
  2167. && pad->GetShape() != PAD_SHAPE::CUSTOM )
  2168. {
  2169. VECTOR2I dummySize = pad->GetSize() + clearance + clearance;
  2170. if( dummySize.x <= 0 || dummySize.y <= 0 )
  2171. continue;
  2172. PAD dummy( *pad );
  2173. dummy.SetSize( dummySize );
  2174. dummy.TransformShapeToPolygon( aBuffer, aLayer, 0, aMaxError, aErrorLoc );
  2175. }
  2176. else
  2177. {
  2178. pad->TransformShapeToPolygon( aBuffer, aLayer, clearance.x, aMaxError, aErrorLoc );
  2179. }
  2180. }
  2181. }
  2182. void FOOTPRINT::TransformFPShapesToPolySet( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
  2183. int aClearance, int aError, ERROR_LOC aErrorLoc,
  2184. bool aIncludeText, bool aIncludeShapes,
  2185. bool aIncludePrivateItems ) const
  2186. {
  2187. std::vector<FP_TEXT*> texts; // List of FP_TEXT to convert
  2188. for( BOARD_ITEM* item : GraphicalItems() )
  2189. {
  2190. if( GetPrivateLayers().test( item->GetLayer() ) && !aIncludePrivateItems )
  2191. continue;
  2192. if( item->Type() == PCB_FP_TEXT_T && aIncludeText )
  2193. {
  2194. FP_TEXT* text = static_cast<FP_TEXT*>( item );
  2195. if( aLayer != UNDEFINED_LAYER && text->GetLayer() == aLayer && text->IsVisible() )
  2196. texts.push_back( text );
  2197. }
  2198. if( item->Type() == PCB_FP_TEXTBOX_T && aIncludeText )
  2199. {
  2200. FP_TEXTBOX* textbox = static_cast<FP_TEXTBOX*>( item );
  2201. if( aLayer != UNDEFINED_LAYER && textbox->GetLayer() == aLayer && textbox->IsVisible() )
  2202. textbox->TransformShapeToPolygon( aBuffer, aLayer, 0, aError, aErrorLoc );
  2203. }
  2204. if( item->Type() == PCB_FP_SHAPE_T && aIncludeShapes )
  2205. {
  2206. const FP_SHAPE* outline = static_cast<FP_SHAPE*>( item );
  2207. if( aLayer != UNDEFINED_LAYER && outline->GetLayer() == aLayer )
  2208. outline->TransformShapeToPolygon( aBuffer, aLayer, 0, aError, aErrorLoc );
  2209. }
  2210. }
  2211. if( aIncludeText )
  2212. {
  2213. if( Reference().GetLayer() == aLayer && Reference().IsVisible() )
  2214. texts.push_back( &Reference() );
  2215. if( Value().GetLayer() == aLayer && Value().IsVisible() )
  2216. texts.push_back( &Value() );
  2217. }
  2218. for( const FP_TEXT* text : texts )
  2219. text->TransformTextToPolySet( aBuffer, aLayer, aClearance, aError, aErrorLoc );
  2220. }
  2221. static struct FOOTPRINT_DESC
  2222. {
  2223. FOOTPRINT_DESC()
  2224. {
  2225. ENUM_MAP<ZONE_CONNECTION>& zcMap = ENUM_MAP<ZONE_CONNECTION>::Instance();
  2226. if( zcMap.Choices().GetCount() == 0 )
  2227. {
  2228. zcMap.Undefined( ZONE_CONNECTION::INHERITED );
  2229. zcMap.Map( ZONE_CONNECTION::INHERITED, _HKI( "Inherited" ) )
  2230. .Map( ZONE_CONNECTION::NONE, _HKI( "None" ) )
  2231. .Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) )
  2232. .Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) )
  2233. .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
  2234. }
  2235. ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
  2236. if( layerEnum.Choices().GetCount() == 0 )
  2237. {
  2238. layerEnum.Undefined( UNDEFINED_LAYER );
  2239. for( LSEQ seq = LSET::AllLayersMask().Seq(); seq; ++seq )
  2240. layerEnum.Map( *seq, LSET::Name( *seq ) );
  2241. }
  2242. wxPGChoices fpLayers; // footprints might be placed only on F.Cu & B.Cu
  2243. fpLayers.Add( LSET::Name( F_Cu ), F_Cu );
  2244. fpLayers.Add( LSET::Name( B_Cu ), B_Cu );
  2245. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  2246. REGISTER_TYPE( FOOTPRINT );
  2247. propMgr.AddTypeCast( new TYPE_CAST<FOOTPRINT, BOARD_ITEM> );
  2248. propMgr.AddTypeCast( new TYPE_CAST<FOOTPRINT, BOARD_ITEM_CONTAINER> );
  2249. propMgr.InheritsAfter( TYPE_HASH( FOOTPRINT ), TYPE_HASH( BOARD_ITEM ) );
  2250. propMgr.InheritsAfter( TYPE_HASH( FOOTPRINT ), TYPE_HASH( BOARD_ITEM_CONTAINER ) );
  2251. auto layer = new PROPERTY_ENUM<FOOTPRINT, PCB_LAYER_ID>( _HKI( "Layer" ),
  2252. &FOOTPRINT::SetLayerAndFlip, &FOOTPRINT::GetLayer );
  2253. layer->SetChoices( fpLayers );
  2254. propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ), layer );
  2255. propMgr.AddProperty( new PROPERTY<FOOTPRINT, double>( _HKI( "Orientation" ),
  2256. &FOOTPRINT::SetOrientationDegrees, &FOOTPRINT::GetOrientationDegrees,
  2257. PROPERTY_DISPLAY::PT_DEGREE ) );
  2258. const wxString groupFootprint = _HKI( "Footprint Properties" );
  2259. propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Reference" ),
  2260. &FOOTPRINT::SetReference, &FOOTPRINT::GetReferenceAsString ),
  2261. groupFootprint );
  2262. propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Value" ),
  2263. &FOOTPRINT::SetValue, &FOOTPRINT::GetValueAsString ),
  2264. groupFootprint );
  2265. propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Library link" ),
  2266. NO_SETTER( FOOTPRINT, wxString ), &FOOTPRINT::GetFPIDAsString ),
  2267. groupFootprint );
  2268. propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Description" ),
  2269. NO_SETTER( FOOTPRINT, wxString ), &FOOTPRINT::GetDescription ),
  2270. groupFootprint );
  2271. propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Keywords" ),
  2272. NO_SETTER( FOOTPRINT, wxString ), &FOOTPRINT::GetKeywords ),
  2273. groupFootprint );
  2274. const wxString groupAttributes = _HKI( "Fabrication Attributes" );
  2275. propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Not in schematic" ),
  2276. &FOOTPRINT::SetBoardOnly, &FOOTPRINT::IsBoardOnly ), groupAttributes );
  2277. propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exclude from position files" ),
  2278. &FOOTPRINT::SetExcludedFromPosFiles, &FOOTPRINT::IsExcludedFromPosFiles ),
  2279. groupAttributes );
  2280. propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exclude from BOM" ),
  2281. &FOOTPRINT::SetExcludedFromBOM, &FOOTPRINT::IsExcludedFromBOM ),
  2282. groupAttributes );
  2283. const wxString groupOverrides = _HKI( "Overrides" );
  2284. propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>(
  2285. _HKI( "Exempt from courtyard requirement" ),
  2286. &FOOTPRINT::SetAllowMissingCourtyard, &FOOTPRINT::AllowMissingCourtyard ),
  2287. groupOverrides );
  2288. propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Clearance Override" ),
  2289. &FOOTPRINT::SetLocalClearance, &FOOTPRINT::GetLocalClearance,
  2290. PROPERTY_DISPLAY::PT_SIZE ),
  2291. groupOverrides );
  2292. propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Solderpaste Margin Override" ),
  2293. &FOOTPRINT::SetLocalSolderPasteMargin, &FOOTPRINT::GetLocalSolderPasteMargin,
  2294. PROPERTY_DISPLAY::PT_SIZE ),
  2295. groupOverrides );
  2296. propMgr.AddProperty( new PROPERTY<FOOTPRINT, double>(
  2297. _HKI( "Solderpaste Margin Ratio Override" ),
  2298. &FOOTPRINT::SetLocalSolderPasteMarginRatio,
  2299. &FOOTPRINT::GetLocalSolderPasteMarginRatio ),
  2300. groupOverrides );
  2301. propMgr.AddProperty( new PROPERTY_ENUM<FOOTPRINT, ZONE_CONNECTION>(
  2302. _HKI( "Zone Connection Style" ),
  2303. &FOOTPRINT::SetZoneConnection, &FOOTPRINT::GetZoneConnection ),
  2304. groupOverrides );
  2305. }
  2306. } _FOOTPRINT_DESC;