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.

2410 lines
65 KiB

++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
4 years ago
19 years ago
4 years ago
4 years ago
4 years ago
14 years ago
14 years ago
14 years ago
5 years ago
5 years ago
14 years ago
4 years ago
18 years ago
18 years ago
17 years ago
17 years ago
17 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
19 years ago
19 years ago
19 years ago
18 years ago
Changed the way of looking up NETINFO_ITEM using net names (using boost::unordered_map). Added a hash function (wxString) for that. Introduced NETINFO_ITEM::GetNetItem( wxString ). BOARD::FindNet() uses the map. Net codes are updated upon net list update. (BOARD::ReplaceNetlist()) Added in some places (mostly class_board.cpp) pad->SetNet() calls to synchronize net codes. On creation of NETINFO_LIST, the first NETINFO_ITEM is added (the unconnected items net). Removed COMPONENT_NET::m_netNumber, as it was not used anywhere. Added an assert to D_PAD::GetNetname(), checking if net code and net name is consistent for unconnected pads. Added an assert for NETINFO_LIST::AppendNet() to assure that appended nets are unique. It seems that at this point: - Updating net lists works fine. The only difference between the file ouput is that after changes it contains empty nets as well. - Nets are not saved in the lexical order. Still, net names and net codes are properly assigned to all items in the .kicad_pcb file. It is going to be addressed in the next commit. I believe it should not create any problems, as pads are sorted by their net names anyway (NETINFO_LIST::buildPadsFullList()) Performed tests: - Created a blank PCB, saved as pic_programmer.kicad_pcb (from demos folder). Updated net lists. .kicad_pcb file (comparing to the results from master branch) differ with net order (as mentioned before), net codes and timestamps. - Removed some of components from the above .kicad_pcb file and updated net lists. Modules reappeared. .kicad_pcb file differs in the same way as described above. - Trying to change a pad net name (via properties dialog) results in assert being fired. It is done on purpose (as there is a call to GetNetname() and net name and net code do not match). This will not happen after the next commit. - Prepared a simple project (starting with schematics). Imported net list, changed schematic, reimported net list - changes are applied. - Eagle & KiCad legacy boards seem to load without any problem.
12 years ago
18 years ago
5 years ago
18 years ago
5 years ago
5 years ago
8 years ago
5 years ago
14 years ago
5 years ago
14 years ago
14 years ago
14 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
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
  7. *
  8. * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, you may find one here:
  22. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  23. * or you may search the http://www.gnu.org website for the version 2 license,
  24. * or you may write to the Free Software Foundation, Inc.,
  25. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  26. */
  27. #include <iterator>
  28. #include <wx/log.h>
  29. #include <drc/drc_rtree.h>
  30. #include <board_design_settings.h>
  31. #include <board_commit.h>
  32. #include <board.h>
  33. #include <core/arraydim.h>
  34. #include <core/kicad_algo.h>
  35. #include <connectivity/connectivity_data.h>
  36. #include <convert_shape_list_to_polygon.h>
  37. #include <footprint.h>
  38. #include <pcb_base_frame.h>
  39. #include <pcb_track.h>
  40. #include <pcb_marker.h>
  41. #include <pcb_group.h>
  42. #include <pcb_target.h>
  43. #include <pcb_shape.h>
  44. #include <pcb_bitmap.h>
  45. #include <pcb_text.h>
  46. #include <pcb_textbox.h>
  47. #include <pcb_dimension.h>
  48. #include <pgm_base.h>
  49. #include <pcbnew_settings.h>
  50. #include <progress_reporter.h>
  51. #include <project.h>
  52. #include <project/net_settings.h>
  53. #include <project/project_file.h>
  54. #include <project/project_local_settings.h>
  55. #include <ratsnest/ratsnest_data.h>
  56. #include <reporter.h>
  57. #include <tool/selection_conditions.h>
  58. #include <string_utils.h>
  59. #include <thread_pool.h>
  60. #include <zone.h>
  61. // This is an odd place for this, but CvPcb won't link if it's in board_item.cpp like I first
  62. // tried it.
  63. VECTOR2I BOARD_ITEM::ZeroOffset( 0, 0 );
  64. BOARD::BOARD() :
  65. BOARD_ITEM_CONTAINER( (BOARD_ITEM*) nullptr, PCB_T ),
  66. m_LegacyDesignSettingsLoaded( false ),
  67. m_LegacyCopperEdgeClearanceLoaded( false ),
  68. m_LegacyNetclassesLoaded( false ),
  69. m_boardUse( BOARD_USE::NORMAL ),
  70. m_timeStamp( 1 ),
  71. m_paper( PAGE_INFO::A4 ),
  72. m_project( nullptr ),
  73. m_userUnits( EDA_UNITS::MILLIMETRES ),
  74. m_designSettings( new BOARD_DESIGN_SETTINGS( nullptr, "board.design_settings" ) ),
  75. m_NetInfo( this )
  76. {
  77. // A too small value do not allow connecting 2 shapes (i.e. segments) not exactly connected
  78. // A too large value do not allow safely connecting 2 shapes like very short segments.
  79. m_outlinesChainingEpsilon = pcbIUScale.mmToIU( DEFAULT_CHAINING_EPSILON_MM );
  80. // we have not loaded a board yet, assume latest until then.
  81. m_fileFormatVersionAtLoad = LEGACY_BOARD_FILE_VERSION;
  82. for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
  83. {
  84. m_layers[layer].m_name = GetStandardLayerName( ToLAYER_ID( layer ) );
  85. if( IsCopperLayer( layer ) )
  86. m_layers[layer].m_type = LT_SIGNAL;
  87. else
  88. m_layers[layer].m_type = LT_UNDEFINED;
  89. }
  90. m_SolderMask = new ZONE( this );
  91. m_SolderMask->SetLayerSet( LSET().set( F_Mask ).set( B_Mask ) );
  92. int infinity = ( std::numeric_limits<int>::max() / 2 ) - pcbIUScale.mmToIU( 1 );
  93. m_SolderMask->Outline()->NewOutline();
  94. m_SolderMask->Outline()->Append( VECTOR2I( -infinity, -infinity ) );
  95. m_SolderMask->Outline()->Append( VECTOR2I( -infinity, +infinity ) );
  96. m_SolderMask->Outline()->Append( VECTOR2I( +infinity, +infinity ) );
  97. m_SolderMask->Outline()->Append( VECTOR2I( +infinity, -infinity ) );
  98. m_SolderMask->SetMinThickness( 0 );
  99. BOARD_DESIGN_SETTINGS& bds = GetDesignSettings();
  100. // Initialize default netclass.
  101. bds.m_NetSettings->m_DefaultNetClass = std::make_shared<NETCLASS>( NETCLASS::Default );
  102. bds.m_NetSettings->m_DefaultNetClass->SetDescription( _( "This is the default net class." ) );
  103. bds.UseCustomTrackViaSize( false );
  104. // Initialize ratsnest
  105. m_connectivity.reset( new CONNECTIVITY_DATA() );
  106. // Set flag bits on these that will only be cleared if these are loaded from a legacy file
  107. m_LegacyVisibleLayers.reset().set( Rescue );
  108. m_LegacyVisibleItems.reset().set( GAL_LAYER_INDEX( GAL_LAYER_ID_BITMASK_END ) );
  109. }
  110. BOARD::~BOARD()
  111. {
  112. // Untangle group parents before doing any deleting
  113. for( PCB_GROUP* group : m_groups )
  114. {
  115. for( BOARD_ITEM* item : group->GetItems() )
  116. item->SetParentGroup( nullptr );
  117. }
  118. // Clean up the owned elements
  119. DeleteMARKERs();
  120. for( ZONE* zone : m_zones )
  121. delete zone;
  122. m_zones.clear();
  123. delete m_SolderMask;
  124. for( FOOTPRINT* footprint : m_footprints )
  125. delete footprint;
  126. m_footprints.clear();
  127. for( PCB_TRACK* t : m_tracks )
  128. delete t;
  129. m_tracks.clear();
  130. for( BOARD_ITEM* d : m_drawings )
  131. delete d;
  132. m_drawings.clear();
  133. for( PCB_GROUP* g : m_groups )
  134. delete g;
  135. m_groups.clear();
  136. }
  137. bool BOARD::BuildConnectivity( PROGRESS_REPORTER* aReporter )
  138. {
  139. if( !GetConnectivity()->Build( this, aReporter ) )
  140. return false;
  141. UpdateRatsnestExclusions();
  142. return true;
  143. }
  144. void BOARD::SetProject( PROJECT* aProject, bool aReferenceOnly )
  145. {
  146. if( m_project )
  147. ClearProject();
  148. m_project = aProject;
  149. if( aProject && !aReferenceOnly )
  150. {
  151. PROJECT_FILE& project = aProject->GetProjectFile();
  152. // Link the design settings object to the project file
  153. project.m_BoardSettings = &GetDesignSettings();
  154. // Set parent, which also will load the values from JSON stored in the project if we don't
  155. // have legacy design settings loaded already
  156. project.m_BoardSettings->SetParent( &project, !m_LegacyDesignSettingsLoaded );
  157. // The DesignSettings' netclasses pointer will be pointing to its internal netclasses
  158. // list at this point. If we loaded anything into it from a legacy board file then we
  159. // want to transfer it over to the project netclasses list.
  160. if( m_LegacyNetclassesLoaded )
  161. {
  162. std::shared_ptr<NET_SETTINGS> legacySettings = GetDesignSettings().m_NetSettings;
  163. std::shared_ptr<NET_SETTINGS>& projectSettings = project.NetSettings();
  164. projectSettings->m_DefaultNetClass = legacySettings->m_DefaultNetClass;
  165. projectSettings->m_NetClasses = legacySettings->m_NetClasses;
  166. projectSettings->m_NetClassPatternAssignments =
  167. std::move( legacySettings->m_NetClassPatternAssignments );
  168. projectSettings->m_NetClassPatternAssignmentCache.clear();
  169. }
  170. // Now update the DesignSettings' netclass pointer to point into the project.
  171. GetDesignSettings().m_NetSettings = project.NetSettings();
  172. }
  173. }
  174. void BOARD::ClearProject()
  175. {
  176. if( !m_project )
  177. return;
  178. PROJECT_FILE& project = m_project->GetProjectFile();
  179. // Owned by the BOARD
  180. if( project.m_BoardSettings )
  181. {
  182. project.ReleaseNestedSettings( project.m_BoardSettings );
  183. project.m_BoardSettings = nullptr;
  184. }
  185. GetDesignSettings().m_NetSettings = nullptr;
  186. GetDesignSettings().SetParent( nullptr );
  187. m_project = nullptr;
  188. }
  189. void BOARD::IncrementTimeStamp()
  190. {
  191. m_timeStamp++;
  192. if( !m_IntersectsAreaCache.empty()
  193. || !m_EnclosedByAreaCache.empty()
  194. || !m_IntersectsCourtyardCache.empty()
  195. || !m_IntersectsFCourtyardCache.empty()
  196. || !m_IntersectsBCourtyardCache.empty()
  197. || !m_LayerExpressionCache.empty()
  198. || !m_ZoneBBoxCache.empty() )
  199. {
  200. std::unique_lock<std::mutex> cacheLock( m_CachesMutex );
  201. m_IntersectsAreaCache.clear();
  202. m_EnclosedByAreaCache.clear();
  203. m_IntersectsCourtyardCache.clear();
  204. m_IntersectsFCourtyardCache.clear();
  205. m_IntersectsBCourtyardCache.clear();
  206. m_LayerExpressionCache.clear();
  207. m_ZoneBBoxCache.clear();
  208. // These are always regenerated before use, but still probably safer to clear them
  209. // while we're here.
  210. m_DRCMaxClearance = 0;
  211. m_DRCMaxPhysicalClearance = 0;
  212. m_DRCZones.clear();
  213. m_DRCCopperZones.clear();
  214. m_ZoneIsolatedIslandsMap.clear();
  215. m_CopperZoneRTreeCache.clear();
  216. m_CopperItemRTreeCache = std::make_unique<DRC_RTREE>();
  217. }
  218. else if( !m_CopperItemRTreeCache )
  219. {
  220. std::unique_lock<std::mutex> cacheLock( m_CachesMutex );
  221. m_CopperItemRTreeCache = std::make_unique<DRC_RTREE>();
  222. }
  223. }
  224. void BOARD::UpdateRatsnestExclusions()
  225. {
  226. std::set<std::pair<KIID, KIID>> m_ratsnestExclusions;
  227. for( PCB_MARKER* marker : GetBoard()->Markers() )
  228. {
  229. if( marker->GetMarkerType() == MARKER_BASE::MARKER_RATSNEST && marker->IsExcluded() )
  230. {
  231. const std::shared_ptr<RC_ITEM>& rcItem = marker->GetRCItem();
  232. m_ratsnestExclusions.emplace( rcItem->GetMainItemID(), rcItem->GetAuxItemID() );
  233. m_ratsnestExclusions.emplace( rcItem->GetAuxItemID(), rcItem->GetMainItemID() );
  234. }
  235. }
  236. GetConnectivity()->RunOnUnconnectedEdges(
  237. [&]( CN_EDGE& aEdge )
  238. {
  239. if( aEdge.GetSourceNode() && aEdge.GetTargetNode()
  240. && !aEdge.GetSourceNode()->Dirty() && !aEdge.GetTargetNode()->Dirty() )
  241. {
  242. std::pair<KIID, KIID> ids = { aEdge.GetSourceNode()->Parent()->m_Uuid,
  243. aEdge.GetTargetNode()->Parent()->m_Uuid };
  244. aEdge.SetVisible( m_ratsnestExclusions.count( ids ) == 0 );
  245. }
  246. return true;
  247. } );
  248. }
  249. std::vector<PCB_MARKER*> BOARD::ResolveDRCExclusions( bool aCreateMarkers )
  250. {
  251. for( PCB_MARKER* marker : GetBoard()->Markers() )
  252. {
  253. auto i = m_designSettings->m_DrcExclusions.find( marker->Serialize() );
  254. if( i != m_designSettings->m_DrcExclusions.end() )
  255. {
  256. marker->SetExcluded( true );
  257. m_designSettings->m_DrcExclusions.erase( i );
  258. }
  259. }
  260. std::vector<PCB_MARKER*> newMarkers;
  261. if( aCreateMarkers )
  262. {
  263. for( const wxString& exclusionData : m_designSettings->m_DrcExclusions )
  264. {
  265. PCB_MARKER* marker = PCB_MARKER::Deserialize( exclusionData );
  266. if( !marker )
  267. continue;
  268. // Check to see if items still exist
  269. for( const KIID& guid : marker->GetRCItem()->GetIDs() )
  270. {
  271. if( GetItem( guid ) == DELETED_BOARD_ITEM::GetInstance() )
  272. {
  273. delete marker;
  274. marker = nullptr;
  275. break;
  276. }
  277. }
  278. if( marker )
  279. {
  280. marker->SetExcluded( true );
  281. newMarkers.push_back( marker );
  282. }
  283. }
  284. }
  285. m_designSettings->m_DrcExclusions.clear();
  286. return newMarkers;
  287. }
  288. bool BOARD::ResolveTextVar( wxString* token, int aDepth ) const
  289. {
  290. if( token->Contains( ':' ) )
  291. {
  292. wxString remainder;
  293. wxString ref = token->BeforeFirst( ':', &remainder );
  294. BOARD_ITEM* refItem = GetItem( KIID( ref ) );
  295. if( refItem && refItem->Type() == PCB_FOOTPRINT_T )
  296. {
  297. FOOTPRINT* refFP = static_cast<FOOTPRINT*>( refItem );
  298. if( refFP->ResolveTextVar( &remainder, aDepth + 1 ) )
  299. {
  300. *token = remainder;
  301. return true;
  302. }
  303. }
  304. }
  305. wxString var = *token;
  306. if( GetTitleBlock().TextVarResolver( token, m_project ) )
  307. {
  308. return true;
  309. }
  310. else if( m_properties.count( var ) )
  311. {
  312. *token = m_properties.at( var );
  313. return true;
  314. }
  315. if( GetProject() && GetProject()->TextVarResolver( token ) )
  316. return true;
  317. return false;
  318. }
  319. VECTOR2I BOARD::GetPosition() const
  320. {
  321. return ZeroOffset;
  322. }
  323. void BOARD::SetPosition( const VECTOR2I& aPos )
  324. {
  325. wxLogWarning( wxT( "This should not be called on the BOARD object") );
  326. }
  327. void BOARD::Move( const VECTOR2I& aMoveVector ) // overload
  328. {
  329. INSPECTOR_FUNC inspector =
  330. [&] ( EDA_ITEM* item, void* testData )
  331. {
  332. BOARD_ITEM* brdItem = static_cast<BOARD_ITEM*>( item );
  333. // aMoveVector was snapshotted, don't need "data".
  334. // Only move the top level group
  335. if( brdItem->GetParentGroup() == nullptr
  336. && brdItem->GetParentFootprint() == nullptr )
  337. brdItem->Move( aMoveVector );
  338. return INSPECT_RESULT::CONTINUE;
  339. };
  340. Visit( inspector, nullptr, GENERAL_COLLECTOR::BoardLevelItems );
  341. }
  342. TRACKS BOARD::TracksInNet( int aNetCode )
  343. {
  344. TRACKS ret;
  345. INSPECTOR_FUNC inspector = [aNetCode, &ret]( EDA_ITEM* item, void* testData )
  346. {
  347. PCB_TRACK* t = static_cast<PCB_TRACK*>( item );
  348. if( t->GetNetCode() == aNetCode )
  349. ret.push_back( t );
  350. return INSPECT_RESULT::CONTINUE;
  351. };
  352. // visit this BOARD's PCB_TRACKs and PCB_VIAs with above TRACK INSPECTOR which
  353. // appends all in aNetCode to ret.
  354. Visit( inspector, nullptr, GENERAL_COLLECTOR::Tracks );
  355. return ret;
  356. }
  357. bool BOARD::SetLayerDescr( PCB_LAYER_ID aIndex, const LAYER& aLayer )
  358. {
  359. if( unsigned( aIndex ) < arrayDim( m_layers ) )
  360. {
  361. m_layers[ aIndex ] = aLayer;
  362. return true;
  363. }
  364. return false;
  365. }
  366. PCB_LAYER_ID BOARD::GetLayerID( const wxString& aLayerName ) const
  367. {
  368. // Check the BOARD physical layer names.
  369. for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
  370. {
  371. if ( m_layers[ layer ].m_name == aLayerName || m_layers[ layer ].m_userName == aLayerName )
  372. return ToLAYER_ID( layer );
  373. }
  374. // Otherwise fall back to the system standard layer names for virtual layers.
  375. for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
  376. {
  377. if( GetStandardLayerName( ToLAYER_ID( layer ) ) == aLayerName )
  378. return ToLAYER_ID( layer );
  379. }
  380. return UNDEFINED_LAYER;
  381. }
  382. const wxString BOARD::GetLayerName( PCB_LAYER_ID aLayer ) const
  383. {
  384. // All layer names are stored in the BOARD.
  385. if( IsLayerEnabled( aLayer ) )
  386. {
  387. // Standard names were set in BOARD::BOARD() but they may be over-ridden by
  388. // BOARD::SetLayerName(). For copper layers, return the user defined layer name,
  389. // if it was set. Otherwise return the Standard English layer name.
  390. if( !m_layers[aLayer].m_userName.IsEmpty() )
  391. return m_layers[aLayer].m_userName;
  392. }
  393. return GetStandardLayerName( aLayer );
  394. }
  395. bool BOARD::SetLayerName( PCB_LAYER_ID aLayer, const wxString& aLayerName )
  396. {
  397. wxCHECK( !aLayerName.IsEmpty(), false );
  398. // no quote chars in the name allowed
  399. if( aLayerName.Find( wxChar( '"' ) ) != wxNOT_FOUND )
  400. return false;
  401. if( IsLayerEnabled( aLayer ) )
  402. {
  403. m_layers[aLayer].m_userName = aLayerName;
  404. return true;
  405. }
  406. return false;
  407. }
  408. LAYER_T BOARD::GetLayerType( PCB_LAYER_ID aLayer ) const
  409. {
  410. if( !IsCopperLayer( aLayer ) )
  411. return LT_SIGNAL;
  412. if( IsLayerEnabled( aLayer ) )
  413. return m_layers[aLayer].m_type;
  414. return LT_SIGNAL;
  415. }
  416. bool BOARD::SetLayerType( PCB_LAYER_ID aLayer, LAYER_T aLayerType )
  417. {
  418. if( !IsCopperLayer( aLayer ) )
  419. return false;
  420. if( IsLayerEnabled( aLayer ) )
  421. {
  422. m_layers[aLayer].m_type = aLayerType;
  423. return true;
  424. }
  425. return false;
  426. }
  427. const char* LAYER::ShowType( LAYER_T aType )
  428. {
  429. switch( aType )
  430. {
  431. default:
  432. case LT_SIGNAL: return "signal";
  433. case LT_POWER: return "power";
  434. case LT_MIXED: return "mixed";
  435. case LT_JUMPER: return "jumper";
  436. }
  437. }
  438. LAYER_T LAYER::ParseType( const char* aType )
  439. {
  440. if( strcmp( aType, "signal" ) == 0 )
  441. return LT_SIGNAL;
  442. else if( strcmp( aType, "power" ) == 0 )
  443. return LT_POWER;
  444. else if( strcmp( aType, "mixed" ) == 0 )
  445. return LT_MIXED;
  446. else if( strcmp( aType, "jumper" ) == 0 )
  447. return LT_JUMPER;
  448. else
  449. return LT_UNDEFINED;
  450. }
  451. int BOARD::GetCopperLayerCount() const
  452. {
  453. return GetDesignSettings().GetCopperLayerCount();
  454. }
  455. void BOARD::SetCopperLayerCount( int aCount )
  456. {
  457. GetDesignSettings().SetCopperLayerCount( aCount );
  458. }
  459. int BOARD::LayerDepth( PCB_LAYER_ID aStartLayer, PCB_LAYER_ID aEndLayer ) const
  460. {
  461. if( aStartLayer > aEndLayer )
  462. std::swap( aStartLayer, aEndLayer );
  463. if( aEndLayer == B_Cu )
  464. aEndLayer = ToLAYER_ID( F_Cu + GetCopperLayerCount() - 1 );
  465. return aEndLayer - aStartLayer;
  466. }
  467. LSET BOARD::GetEnabledLayers() const
  468. {
  469. return GetDesignSettings().GetEnabledLayers();
  470. }
  471. bool BOARD::IsLayerVisible( PCB_LAYER_ID aLayer ) const
  472. {
  473. // If there is no project, assume layer is visible always
  474. return GetDesignSettings().IsLayerEnabled( aLayer )
  475. && ( !m_project || m_project->GetLocalSettings().m_VisibleLayers[aLayer] );
  476. }
  477. LSET BOARD::GetVisibleLayers() const
  478. {
  479. return m_project ? m_project->GetLocalSettings().m_VisibleLayers : LSET::AllLayersMask();
  480. }
  481. void BOARD::SetEnabledLayers( LSET aLayerSet )
  482. {
  483. GetDesignSettings().SetEnabledLayers( aLayerSet );
  484. }
  485. bool BOARD::IsLayerEnabled( PCB_LAYER_ID aLayer ) const
  486. {
  487. return GetDesignSettings().IsLayerEnabled( aLayer );
  488. }
  489. void BOARD::SetVisibleLayers( LSET aLayerSet )
  490. {
  491. if( m_project )
  492. m_project->GetLocalSettings().m_VisibleLayers = aLayerSet;
  493. }
  494. void BOARD::SetVisibleElements( const GAL_SET& aSet )
  495. {
  496. // Call SetElementVisibility for each item
  497. // to ensure specific calculations that can be needed by some items,
  498. // just changing the visibility flags could be not sufficient.
  499. for( size_t i = 0; i < aSet.size(); i++ )
  500. SetElementVisibility( GAL_LAYER_ID_START + static_cast<int>( i ), aSet[i] );
  501. }
  502. void BOARD::SetVisibleAlls()
  503. {
  504. SetVisibleLayers( LSET().set() );
  505. // Call SetElementVisibility for each item,
  506. // to ensure specific calculations that can be needed by some items
  507. for( GAL_LAYER_ID ii = GAL_LAYER_ID_START; ii < GAL_LAYER_ID_BITMASK_END; ++ii )
  508. SetElementVisibility( ii, true );
  509. }
  510. GAL_SET BOARD::GetVisibleElements() const
  511. {
  512. return m_project ? m_project->GetLocalSettings().m_VisibleItems : GAL_SET().set();
  513. }
  514. bool BOARD::IsElementVisible( GAL_LAYER_ID aLayer ) const
  515. {
  516. return !m_project || m_project->GetLocalSettings().m_VisibleItems[aLayer - GAL_LAYER_ID_START];
  517. }
  518. void BOARD::SetElementVisibility( GAL_LAYER_ID aLayer, bool isEnabled )
  519. {
  520. if( m_project )
  521. m_project->GetLocalSettings().m_VisibleItems.set( aLayer - GAL_LAYER_ID_START, isEnabled );
  522. switch( aLayer )
  523. {
  524. case LAYER_RATSNEST:
  525. {
  526. // because we have a tool to show/hide ratsnest relative to a pad or a footprint
  527. // so the hide/show option is a per item selection
  528. for( PCB_TRACK* track : Tracks() )
  529. track->SetLocalRatsnestVisible( isEnabled );
  530. for( FOOTPRINT* footprint : Footprints() )
  531. {
  532. for( PAD* pad : footprint->Pads() )
  533. pad->SetLocalRatsnestVisible( isEnabled );
  534. }
  535. for( ZONE* zone : Zones() )
  536. zone->SetLocalRatsnestVisible( isEnabled );
  537. break;
  538. }
  539. default:
  540. ;
  541. }
  542. }
  543. bool BOARD::IsFootprintLayerVisible( PCB_LAYER_ID aLayer ) const
  544. {
  545. switch( aLayer )
  546. {
  547. case F_Cu: return IsElementVisible( LAYER_MOD_FR );
  548. case B_Cu: return IsElementVisible( LAYER_MOD_BK );
  549. default: wxFAIL_MSG( wxT( "BOARD::IsModuleLayerVisible(): bad layer" ) ); return true;
  550. }
  551. }
  552. BOARD_DESIGN_SETTINGS& BOARD::GetDesignSettings() const
  553. {
  554. return *m_designSettings;
  555. }
  556. const ZONE_SETTINGS& BOARD::GetZoneSettings() const
  557. {
  558. return GetDesignSettings().GetDefaultZoneSettings();
  559. }
  560. void BOARD::SetZoneSettings( const ZONE_SETTINGS& aSettings )
  561. {
  562. GetDesignSettings().SetDefaultZoneSettings( aSettings );
  563. }
  564. void BOARD::CacheTriangulation( PROGRESS_REPORTER* aReporter, const std::vector<ZONE*>& aZones )
  565. {
  566. std::vector<ZONE*> zones = aZones;
  567. if( zones.empty() )
  568. zones = m_zones;
  569. if( zones.empty() )
  570. return;
  571. if( aReporter )
  572. aReporter->Report( _( "Tessellating copper zones..." ) );
  573. thread_pool& tp = GetKiCadThreadPool();
  574. std::vector<std::future<size_t>> returns;
  575. returns.reserve( zones.size() );
  576. auto cache_zones = [aReporter]( ZONE* aZone ) -> size_t
  577. {
  578. if( aReporter && aReporter->IsCancelled() )
  579. return 0;
  580. aZone->CacheTriangulation();
  581. if( aReporter )
  582. aReporter->AdvanceProgress();
  583. return 1;
  584. };
  585. for( ZONE* zone : zones )
  586. returns.emplace_back( tp.submit( cache_zones, zone ) );
  587. // Finalize the triangulation threads
  588. for( const std::future<size_t>& ret : returns )
  589. {
  590. std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  591. while( status != std::future_status::ready )
  592. {
  593. if( aReporter )
  594. aReporter->KeepRefreshing();
  595. status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  596. }
  597. }
  598. }
  599. void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode, bool aSkipConnectivity )
  600. {
  601. if( aBoardItem == nullptr )
  602. {
  603. wxFAIL_MSG( wxT( "BOARD::Add() param error: aBoardItem nullptr" ) );
  604. return;
  605. }
  606. switch( aBoardItem->Type() )
  607. {
  608. case PCB_NETINFO_T:
  609. m_NetInfo.AppendNet( (NETINFO_ITEM*) aBoardItem );
  610. break;
  611. // this one uses a vector
  612. case PCB_MARKER_T:
  613. m_markers.push_back( (PCB_MARKER*) aBoardItem );
  614. break;
  615. // this one uses a vector
  616. case PCB_GROUP_T:
  617. m_groups.push_back( (PCB_GROUP*) aBoardItem );
  618. break;
  619. // this one uses a vector
  620. case PCB_ZONE_T:
  621. m_zones.push_back( (ZONE*) aBoardItem );
  622. break;
  623. case PCB_TRACE_T:
  624. case PCB_VIA_T:
  625. case PCB_ARC_T:
  626. // N.B. This inserts a small memory leak as we lose the
  627. if( !IsCopperLayer( aBoardItem->GetLayer() ) )
  628. {
  629. wxFAIL_MSG( wxT( "BOARD::Add() Cannot place Track on non-copper layer" ) );
  630. return;
  631. }
  632. if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
  633. m_tracks.push_back( static_cast<PCB_TRACK*>( aBoardItem ) );
  634. else
  635. m_tracks.push_front( static_cast<PCB_TRACK*>( aBoardItem ) );
  636. break;
  637. case PCB_FOOTPRINT_T:
  638. if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
  639. m_footprints.push_back( static_cast<FOOTPRINT*>( aBoardItem ) );
  640. else
  641. m_footprints.push_front( static_cast<FOOTPRINT*>( aBoardItem ) );
  642. break;
  643. case PCB_DIM_ALIGNED_T:
  644. case PCB_DIM_CENTER_T:
  645. case PCB_DIM_RADIAL_T:
  646. case PCB_DIM_ORTHOGONAL_T:
  647. case PCB_DIM_LEADER_T:
  648. case PCB_SHAPE_T:
  649. case PCB_BITMAP_T:
  650. case PCB_TEXT_T:
  651. case PCB_TEXTBOX_T:
  652. case PCB_TARGET_T:
  653. if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
  654. m_drawings.push_back( aBoardItem );
  655. else
  656. m_drawings.push_front( aBoardItem );
  657. break;
  658. // other types may use linked list
  659. default:
  660. {
  661. wxString msg;
  662. msg.Printf( wxT( "BOARD::Add() needs work: BOARD_ITEM type (%d) not handled" ),
  663. aBoardItem->Type() );
  664. wxFAIL_MSG( msg );
  665. return;
  666. }
  667. break;
  668. }
  669. aBoardItem->SetParent( this );
  670. aBoardItem->ClearEditFlags();
  671. if( !aSkipConnectivity )
  672. m_connectivity->Add( aBoardItem );
  673. if( aMode != ADD_MODE::BULK_INSERT && aMode != ADD_MODE::BULK_APPEND )
  674. {
  675. InvokeListeners( &BOARD_LISTENER::OnBoardItemAdded, *this, aBoardItem );
  676. }
  677. }
  678. void BOARD::FinalizeBulkAdd( std::vector<BOARD_ITEM*>& aNewItems )
  679. {
  680. InvokeListeners( &BOARD_LISTENER::OnBoardItemsAdded, *this, aNewItems );
  681. }
  682. void BOARD::FinalizeBulkRemove( std::vector<BOARD_ITEM*>& aRemovedItems )
  683. {
  684. InvokeListeners( &BOARD_LISTENER::OnBoardItemsRemoved, *this, aRemovedItems );
  685. }
  686. void BOARD::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aRemoveMode )
  687. {
  688. // find these calls and fix them! Don't send me no stinking' nullptr.
  689. wxASSERT( aBoardItem );
  690. switch( aBoardItem->Type() )
  691. {
  692. case PCB_NETINFO_T:
  693. {
  694. NETINFO_ITEM* item = static_cast<NETINFO_ITEM*>( aBoardItem );
  695. NETINFO_ITEM* unconnected = m_NetInfo.GetNetItem( NETINFO_LIST::UNCONNECTED );
  696. for( FOOTPRINT* fp : m_footprints )
  697. {
  698. for( PAD* pad : fp->Pads() )
  699. {
  700. if( pad->GetNet() == item )
  701. pad->SetNet( unconnected );
  702. }
  703. }
  704. for( ZONE* zone : m_zones )
  705. {
  706. if( zone->GetNet() == item )
  707. zone->SetNet( unconnected );
  708. }
  709. for( PCB_TRACK* track : m_tracks )
  710. {
  711. if( track->GetNet() == item )
  712. track->SetNet( unconnected );
  713. }
  714. m_NetInfo.RemoveNet( item );
  715. break;
  716. }
  717. case PCB_MARKER_T:
  718. alg::delete_matching( m_markers, aBoardItem );
  719. break;
  720. case PCB_GROUP_T:
  721. alg::delete_matching( m_groups, aBoardItem );
  722. break;
  723. case PCB_ZONE_T:
  724. alg::delete_matching( m_zones, aBoardItem );
  725. break;
  726. case PCB_FOOTPRINT_T:
  727. alg::delete_matching( m_footprints, aBoardItem );
  728. break;
  729. case PCB_TRACE_T:
  730. case PCB_ARC_T:
  731. case PCB_VIA_T:
  732. alg::delete_matching( m_tracks, aBoardItem );
  733. break;
  734. case PCB_DIM_ALIGNED_T:
  735. case PCB_DIM_CENTER_T:
  736. case PCB_DIM_RADIAL_T:
  737. case PCB_DIM_ORTHOGONAL_T:
  738. case PCB_DIM_LEADER_T:
  739. case PCB_SHAPE_T:
  740. case PCB_BITMAP_T:
  741. case PCB_TEXT_T:
  742. case PCB_TEXTBOX_T:
  743. case PCB_TARGET_T:
  744. alg::delete_matching( m_drawings, aBoardItem );
  745. break;
  746. // other types may use linked list
  747. default:
  748. wxFAIL_MSG( wxT( "BOARD::Remove() needs more ::Type() support" ) );
  749. }
  750. aBoardItem->SetFlags( STRUCT_DELETED );
  751. PCB_GROUP* parentGroup = aBoardItem->GetParentGroup();
  752. if( parentGroup && !( parentGroup->GetFlags() & STRUCT_DELETED ) )
  753. parentGroup->RemoveItem( aBoardItem );
  754. m_connectivity->Remove( aBoardItem );
  755. if( aRemoveMode != REMOVE_MODE::BULK )
  756. InvokeListeners( &BOARD_LISTENER::OnBoardItemRemoved, *this, aBoardItem );
  757. }
  758. wxString BOARD::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  759. {
  760. return wxString::Format( _( "PCB" ) );
  761. }
  762. void BOARD::UpdateUserUnits( BOARD_ITEM* aItem, KIGFX::VIEW* aView )
  763. {
  764. INSPECTOR_FUNC inspector =
  765. [&]( EDA_ITEM* descendant, void* aTestData )
  766. {
  767. PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( descendant );
  768. if( dimension->GetUnitsMode() == DIM_UNITS_MODE::AUTOMATIC )
  769. {
  770. dimension->UpdateUnits();
  771. if( aView )
  772. aView->Update( dimension );
  773. }
  774. return INSPECT_RESULT::CONTINUE;
  775. };
  776. aItem->Visit( inspector, nullptr, { PCB_DIM_ALIGNED_T,
  777. PCB_DIM_LEADER_T,
  778. PCB_DIM_ORTHOGONAL_T,
  779. PCB_DIM_CENTER_T,
  780. PCB_DIM_RADIAL_T } );
  781. }
  782. void BOARD::DeleteMARKERs()
  783. {
  784. // the vector does not know how to delete the PCB_MARKER, it holds pointers
  785. for( PCB_MARKER* marker : m_markers )
  786. delete marker;
  787. m_markers.clear();
  788. }
  789. void BOARD::DeleteMARKERs( bool aWarningsAndErrors, bool aExclusions )
  790. {
  791. // Deleting lots of items from a vector can be very slow. Copy remaining items instead.
  792. MARKERS remaining;
  793. for( PCB_MARKER* marker : m_markers )
  794. {
  795. if( ( marker->GetSeverity() == RPT_SEVERITY_EXCLUSION && aExclusions )
  796. || ( marker->GetSeverity() != RPT_SEVERITY_EXCLUSION && aWarningsAndErrors ) )
  797. {
  798. delete marker;
  799. }
  800. else
  801. {
  802. remaining.push_back( marker );
  803. }
  804. }
  805. m_markers = remaining;
  806. }
  807. void BOARD::DeleteAllFootprints()
  808. {
  809. for( FOOTPRINT* footprint : m_footprints )
  810. delete footprint;
  811. m_footprints.clear();
  812. }
  813. BOARD_ITEM* BOARD::GetItem( const KIID& aID ) const
  814. {
  815. if( aID == niluuid )
  816. return nullptr;
  817. for( PCB_TRACK* track : Tracks() )
  818. {
  819. if( track->m_Uuid == aID )
  820. return track;
  821. }
  822. for( FOOTPRINT* footprint : Footprints() )
  823. {
  824. if( footprint->m_Uuid == aID )
  825. return footprint;
  826. for( PAD* pad : footprint->Pads() )
  827. {
  828. if( pad->m_Uuid == aID )
  829. return pad;
  830. }
  831. if( footprint->Reference().m_Uuid == aID )
  832. return &footprint->Reference();
  833. if( footprint->Value().m_Uuid == aID )
  834. return &footprint->Value();
  835. for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
  836. {
  837. if( drawing->m_Uuid == aID )
  838. return drawing;
  839. }
  840. for( BOARD_ITEM* zone : footprint->Zones() )
  841. {
  842. if( zone->m_Uuid == aID )
  843. return zone;
  844. }
  845. for( PCB_GROUP* group : footprint->Groups() )
  846. {
  847. if( group->m_Uuid == aID )
  848. return group;
  849. }
  850. }
  851. for( ZONE* zone : Zones() )
  852. {
  853. if( zone->m_Uuid == aID )
  854. return zone;
  855. }
  856. for( BOARD_ITEM* drawing : Drawings() )
  857. {
  858. if( drawing->m_Uuid == aID )
  859. return drawing;
  860. }
  861. for( PCB_MARKER* marker : m_markers )
  862. {
  863. if( marker->m_Uuid == aID )
  864. return marker;
  865. }
  866. for( PCB_GROUP* group : m_groups )
  867. {
  868. if( group->m_Uuid == aID )
  869. return group;
  870. }
  871. if( m_Uuid == aID )
  872. return const_cast<BOARD*>( this );
  873. // Not found; weak reference has been deleted.
  874. return DELETED_BOARD_ITEM::GetInstance();
  875. }
  876. void BOARD::FillItemMap( std::map<KIID, EDA_ITEM*>& aMap )
  877. {
  878. // the board itself
  879. aMap[ m_Uuid ] = this;
  880. for( PCB_TRACK* track : Tracks() )
  881. aMap[ track->m_Uuid ] = track;
  882. for( FOOTPRINT* footprint : Footprints() )
  883. {
  884. aMap[ footprint->m_Uuid ] = footprint;
  885. for( PAD* pad : footprint->Pads() )
  886. aMap[ pad->m_Uuid ] = pad;
  887. aMap[ footprint->Reference().m_Uuid ] = &footprint->Reference();
  888. aMap[ footprint->Value().m_Uuid ] = &footprint->Value();
  889. for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
  890. aMap[ drawing->m_Uuid ] = drawing;
  891. }
  892. for( ZONE* zone : Zones() )
  893. aMap[ zone->m_Uuid ] = zone;
  894. for( BOARD_ITEM* drawing : Drawings() )
  895. aMap[ drawing->m_Uuid ] = drawing;
  896. for( PCB_MARKER* marker : m_markers )
  897. aMap[ marker->m_Uuid ] = marker;
  898. for( PCB_GROUP* group : m_groups )
  899. aMap[ group->m_Uuid ] = group;
  900. }
  901. wxString BOARD::ConvertCrossReferencesToKIIDs( const wxString& aSource ) const
  902. {
  903. wxString newbuf;
  904. size_t sourceLen = aSource.length();
  905. for( size_t i = 0; i < sourceLen; ++i )
  906. {
  907. if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
  908. {
  909. wxString token;
  910. bool isCrossRef = false;
  911. for( i = i + 2; i < sourceLen; ++i )
  912. {
  913. if( aSource[i] == '}' )
  914. break;
  915. if( aSource[i] == ':' )
  916. isCrossRef = true;
  917. token.append( aSource[i] );
  918. }
  919. if( isCrossRef )
  920. {
  921. wxString remainder;
  922. wxString ref = token.BeforeFirst( ':', &remainder );
  923. for( const FOOTPRINT* footprint : Footprints() )
  924. {
  925. if( footprint->GetReference().CmpNoCase( ref ) == 0 )
  926. {
  927. wxString test( remainder );
  928. if( footprint->ResolveTextVar( &test ) )
  929. token = footprint->m_Uuid.AsString() + wxT( ":" ) + remainder;
  930. break;
  931. }
  932. }
  933. }
  934. newbuf.append( wxT( "${" ) + token + wxT( "}" ) );
  935. }
  936. else
  937. {
  938. newbuf.append( aSource[i] );
  939. }
  940. }
  941. return newbuf;
  942. }
  943. wxString BOARD::ConvertKIIDsToCrossReferences( const wxString& aSource ) const
  944. {
  945. wxString newbuf;
  946. size_t sourceLen = aSource.length();
  947. for( size_t i = 0; i < sourceLen; ++i )
  948. {
  949. if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
  950. {
  951. wxString token;
  952. bool isCrossRef = false;
  953. for( i = i + 2; i < sourceLen; ++i )
  954. {
  955. if( aSource[i] == '}' )
  956. break;
  957. if( aSource[i] == ':' )
  958. isCrossRef = true;
  959. token.append( aSource[i] );
  960. }
  961. if( isCrossRef )
  962. {
  963. wxString remainder;
  964. wxString ref = token.BeforeFirst( ':', &remainder );
  965. BOARD_ITEM* refItem = GetItem( KIID( ref ) );
  966. if( refItem && refItem->Type() == PCB_FOOTPRINT_T )
  967. {
  968. token = static_cast<FOOTPRINT*>( refItem )->GetReference() + wxT( ":" )
  969. + remainder;
  970. }
  971. }
  972. newbuf.append( wxT( "${" ) + token + wxT( "}" ) );
  973. }
  974. else
  975. {
  976. newbuf.append( aSource[i] );
  977. }
  978. }
  979. return newbuf;
  980. }
  981. unsigned BOARD::GetNodesCount( int aNet ) const
  982. {
  983. unsigned retval = 0;
  984. for( FOOTPRINT* footprint : Footprints() )
  985. {
  986. for( PAD* pad : footprint->Pads() )
  987. {
  988. if( ( aNet == -1 && pad->GetNetCode() > 0 ) || aNet == pad->GetNetCode() )
  989. retval++;
  990. }
  991. }
  992. return retval;
  993. }
  994. BOX2I BOARD::ComputeBoundingBox( bool aBoardEdgesOnly ) const
  995. {
  996. BOX2I bbox;
  997. LSET visible = GetVisibleLayers();
  998. bool showInvisibleText = IsElementVisible( LAYER_MOD_TEXT_INVISIBLE )
  999. && PgmOrNull() && !PgmOrNull()->m_Printing;
  1000. // If the board is just showing a footprint, we want all footprint layers
  1001. // included in the bounding box
  1002. if( IsFootprintHolder() )
  1003. visible.set();
  1004. if( aBoardEdgesOnly )
  1005. visible.set( Edge_Cuts );
  1006. // Check shapes, dimensions, texts, and fiducials
  1007. for( BOARD_ITEM* item : m_drawings )
  1008. {
  1009. if( aBoardEdgesOnly && ( item->GetLayer() != Edge_Cuts || item->Type() != PCB_SHAPE_T ) )
  1010. continue;
  1011. if( ( item->GetLayerSet() & visible ).any() )
  1012. bbox.Merge( item->GetBoundingBox() );
  1013. }
  1014. // Check footprints
  1015. for( FOOTPRINT* footprint : m_footprints )
  1016. {
  1017. if( !( footprint->GetLayerSet() & visible ).any() )
  1018. continue;
  1019. if( aBoardEdgesOnly )
  1020. {
  1021. for( const BOARD_ITEM* edge : footprint->GraphicalItems() )
  1022. {
  1023. if( edge->GetLayer() == Edge_Cuts && edge->Type() == PCB_SHAPE_T )
  1024. bbox.Merge( edge->GetBoundingBox() );
  1025. }
  1026. }
  1027. else
  1028. {
  1029. bbox.Merge( footprint->GetBoundingBox( true, showInvisibleText ) );
  1030. }
  1031. }
  1032. if( !aBoardEdgesOnly )
  1033. {
  1034. // Check tracks
  1035. for( PCB_TRACK* track : m_tracks )
  1036. {
  1037. if( ( track->GetLayerSet() & visible ).any() )
  1038. bbox.Merge( track->GetBoundingBox() );
  1039. }
  1040. // Check zones
  1041. for( ZONE* aZone : m_zones )
  1042. {
  1043. if( ( aZone->GetLayerSet() & visible ).any() )
  1044. bbox.Merge( aZone->GetBoundingBox() );
  1045. }
  1046. }
  1047. return bbox;
  1048. }
  1049. void BOARD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  1050. {
  1051. int padCount = 0;
  1052. int viaCount = 0;
  1053. int trackSegmentCount = 0;
  1054. std::set<int> netCodes;
  1055. int unconnected = GetConnectivity()->GetUnconnectedCount( true );
  1056. for( PCB_TRACK* item : m_tracks )
  1057. {
  1058. if( item->Type() == PCB_VIA_T )
  1059. viaCount++;
  1060. else
  1061. trackSegmentCount++;
  1062. if( item->GetNetCode() > 0 )
  1063. netCodes.insert( item->GetNetCode() );
  1064. }
  1065. for( FOOTPRINT* footprint : Footprints() )
  1066. {
  1067. for( PAD* pad : footprint->Pads() )
  1068. {
  1069. padCount++;
  1070. if( pad->GetNetCode() > 0 )
  1071. netCodes.insert( pad->GetNetCode() );
  1072. }
  1073. }
  1074. aList.emplace_back( _( "Pads" ), wxString::Format( wxT( "%d" ), padCount ) );
  1075. aList.emplace_back( _( "Vias" ), wxString::Format( wxT( "%d" ), viaCount ) );
  1076. aList.emplace_back( _( "Track Segments" ), wxString::Format( wxT( "%d" ), trackSegmentCount ) );
  1077. aList.emplace_back( _( "Nets" ), wxString::Format( wxT( "%d" ), (int) netCodes.size() ) );
  1078. aList.emplace_back( _( "Unrouted" ), wxString::Format( wxT( "%d" ), unconnected ) );
  1079. }
  1080. INSPECT_RESULT BOARD::Visit( INSPECTOR inspector, void* testData,
  1081. const std::vector<KICAD_T>& scanTypes )
  1082. {
  1083. #if 0 && defined(DEBUG)
  1084. std::cout << GetClass().mb_str() << ' ';
  1085. #endif
  1086. bool footprintsScanned = false;
  1087. bool drawingsScanned = false;
  1088. bool tracksScanned = false;
  1089. for( KICAD_T scanType : scanTypes )
  1090. {
  1091. switch( scanType )
  1092. {
  1093. case PCB_T:
  1094. if( inspector( this, testData ) == INSPECT_RESULT::QUIT )
  1095. return INSPECT_RESULT::QUIT;
  1096. break;
  1097. /*
  1098. * Instances of the requested KICAD_T live in a list, either one that I manage, or one
  1099. * that my footprints manage. If it's a type managed by class FOOTPRINT, then simply
  1100. * pass it on to each footprint's Visit() function via IterateForward( m_footprints, ... ).
  1101. */
  1102. case PCB_FOOTPRINT_T:
  1103. case PCB_PAD_T:
  1104. case PCB_SHAPE_T:
  1105. case PCB_BITMAP_T:
  1106. case PCB_TEXT_T:
  1107. case PCB_TEXTBOX_T:
  1108. case PCB_DIM_ALIGNED_T:
  1109. case PCB_DIM_CENTER_T:
  1110. case PCB_DIM_RADIAL_T:
  1111. case PCB_DIM_ORTHOGONAL_T:
  1112. case PCB_DIM_LEADER_T:
  1113. case PCB_TARGET_T:
  1114. if( !footprintsScanned )
  1115. {
  1116. if( IterateForward<FOOTPRINT*>( m_footprints, inspector, testData, scanTypes )
  1117. == INSPECT_RESULT::QUIT )
  1118. {
  1119. return INSPECT_RESULT::QUIT;
  1120. }
  1121. footprintsScanned = true;
  1122. }
  1123. if( !drawingsScanned )
  1124. {
  1125. if( IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, scanTypes )
  1126. == INSPECT_RESULT::QUIT )
  1127. {
  1128. return INSPECT_RESULT::QUIT;
  1129. }
  1130. drawingsScanned = true;
  1131. }
  1132. break;
  1133. case PCB_VIA_T:
  1134. case PCB_TRACE_T:
  1135. case PCB_ARC_T:
  1136. if( !tracksScanned )
  1137. {
  1138. if( IterateForward<PCB_TRACK*>( m_tracks, inspector, testData, scanTypes )
  1139. == INSPECT_RESULT::QUIT )
  1140. {
  1141. return INSPECT_RESULT::QUIT;
  1142. }
  1143. tracksScanned = true;
  1144. }
  1145. break;
  1146. case PCB_MARKER_T:
  1147. for( PCB_MARKER* marker : m_markers )
  1148. {
  1149. if( marker->Visit( inspector, testData, { scanType } ) == INSPECT_RESULT::QUIT )
  1150. return INSPECT_RESULT::QUIT;
  1151. }
  1152. break;
  1153. case PCB_ZONE_T:
  1154. if( !footprintsScanned )
  1155. {
  1156. if( IterateForward<FOOTPRINT*>( m_footprints, inspector, testData, scanTypes )
  1157. == INSPECT_RESULT::QUIT )
  1158. {
  1159. return INSPECT_RESULT::QUIT;
  1160. }
  1161. footprintsScanned = true;
  1162. }
  1163. for( ZONE* zone : m_zones)
  1164. {
  1165. if( zone->Visit( inspector, testData, { scanType } ) == INSPECT_RESULT::QUIT )
  1166. return INSPECT_RESULT::QUIT;
  1167. }
  1168. break;
  1169. case PCB_GROUP_T:
  1170. if( IterateForward<PCB_GROUP*>( m_groups, inspector, testData, { scanType } )
  1171. == INSPECT_RESULT::QUIT )
  1172. {
  1173. return INSPECT_RESULT::QUIT;
  1174. }
  1175. break;
  1176. default:
  1177. break;
  1178. }
  1179. }
  1180. return INSPECT_RESULT::CONTINUE;
  1181. }
  1182. NETINFO_ITEM* BOARD::FindNet( int aNetcode ) const
  1183. {
  1184. // the first valid netcode is 1 and the last is m_NetInfo.GetCount()-1.
  1185. // zero is reserved for "no connection" and is not actually a net.
  1186. // nullptr is returned for non valid netcodes
  1187. wxASSERT( m_NetInfo.GetNetCount() > 0 );
  1188. if( aNetcode == NETINFO_LIST::UNCONNECTED && m_NetInfo.GetNetCount() == 0 )
  1189. return NETINFO_LIST::OrphanedItem();
  1190. else
  1191. return m_NetInfo.GetNetItem( aNetcode );
  1192. }
  1193. NETINFO_ITEM* BOARD::FindNet( const wxString& aNetname ) const
  1194. {
  1195. return m_NetInfo.GetNetItem( aNetname );
  1196. }
  1197. FOOTPRINT* BOARD::FindFootprintByReference( const wxString& aReference ) const
  1198. {
  1199. for( FOOTPRINT* footprint : m_footprints )
  1200. {
  1201. if( aReference == footprint->GetReference() )
  1202. return footprint;
  1203. }
  1204. return nullptr;
  1205. }
  1206. FOOTPRINT* BOARD::FindFootprintByPath( const KIID_PATH& aPath ) const
  1207. {
  1208. for( FOOTPRINT* footprint : m_footprints )
  1209. {
  1210. if( footprint->GetPath() == aPath )
  1211. return footprint;
  1212. }
  1213. return nullptr;
  1214. }
  1215. std::set<wxString> BOARD::GetNetClassAssignmentCandidates() const
  1216. {
  1217. std::set<wxString> names;
  1218. for( const NETINFO_ITEM* net : m_NetInfo )
  1219. {
  1220. if( !net->GetNetname().IsEmpty() )
  1221. names.insert( net->GetNetname() );
  1222. }
  1223. return names;
  1224. }
  1225. void BOARD::SynchronizeProperties()
  1226. {
  1227. wxCHECK( m_project, /*void*/ );
  1228. if( !m_project->IsNullProject() )
  1229. SetProperties( m_project->GetTextVars() );
  1230. }
  1231. void BOARD::SynchronizeNetsAndNetClasses( bool aResetTrackAndViaSizes )
  1232. {
  1233. if( !m_project )
  1234. return;
  1235. BOARD_DESIGN_SETTINGS& bds = GetDesignSettings();
  1236. std::shared_ptr<NETCLASS>& defaultNetClass = bds.m_NetSettings->m_DefaultNetClass;
  1237. for( NETINFO_ITEM* net : m_NetInfo )
  1238. net->SetNetClass( bds.m_NetSettings->GetEffectiveNetClass( net->GetNetname() ) );
  1239. if( aResetTrackAndViaSizes )
  1240. {
  1241. // Set initial values for custom track width & via size to match the default
  1242. // netclass settings
  1243. bds.UseCustomTrackViaSize( false );
  1244. bds.SetCustomTrackWidth( defaultNetClass->GetTrackWidth() );
  1245. bds.SetCustomViaSize( defaultNetClass->GetViaDiameter() );
  1246. bds.SetCustomViaDrill( defaultNetClass->GetViaDrill() );
  1247. bds.SetCustomDiffPairWidth( defaultNetClass->GetDiffPairWidth() );
  1248. bds.SetCustomDiffPairGap( defaultNetClass->GetDiffPairGap() );
  1249. bds.SetCustomDiffPairViaGap( defaultNetClass->GetDiffPairViaGap() );
  1250. }
  1251. InvokeListeners( &BOARD_LISTENER::OnBoardNetSettingsChanged, *this );
  1252. }
  1253. int BOARD::SetAreasNetCodesFromNetNames()
  1254. {
  1255. int error_count = 0;
  1256. for( ZONE* zone : Zones() )
  1257. {
  1258. if( !zone->IsOnCopperLayer() )
  1259. {
  1260. zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
  1261. continue;
  1262. }
  1263. if( zone->GetNetCode() != 0 ) // i.e. if this zone is connected to a net
  1264. {
  1265. const NETINFO_ITEM* net = zone->GetNet();
  1266. if( net )
  1267. {
  1268. zone->SetNetCode( net->GetNetCode() );
  1269. }
  1270. else
  1271. {
  1272. error_count++;
  1273. // keep Net Name and set m_NetCode to -1 : error flag.
  1274. zone->SetNetCode( -1 );
  1275. }
  1276. }
  1277. }
  1278. return error_count;
  1279. }
  1280. PAD* BOARD::GetPad( const VECTOR2I& aPosition, LSET aLayerSet ) const
  1281. {
  1282. if( !aLayerSet.any() )
  1283. aLayerSet = LSET::AllCuMask();
  1284. for( FOOTPRINT* footprint : m_footprints )
  1285. {
  1286. PAD* pad = nullptr;
  1287. if( footprint->HitTest( aPosition ) )
  1288. pad = footprint->GetPad( aPosition, aLayerSet );
  1289. if( pad )
  1290. return pad;
  1291. }
  1292. return nullptr;
  1293. }
  1294. PAD* BOARD::GetPad( const PCB_TRACK* aTrace, ENDPOINT_T aEndPoint ) const
  1295. {
  1296. const VECTOR2I& aPosition = aTrace->GetEndPoint( aEndPoint );
  1297. LSET lset( aTrace->GetLayer() );
  1298. return GetPad( aPosition, lset );
  1299. }
  1300. PAD* BOARD::GetPadFast( const VECTOR2I& aPosition, LSET aLayerSet ) const
  1301. {
  1302. for( FOOTPRINT* footprint : Footprints() )
  1303. {
  1304. for( PAD* pad : footprint->Pads() )
  1305. {
  1306. if( pad->GetPosition() != aPosition )
  1307. continue;
  1308. // Pad found, it must be on the correct layer
  1309. if( ( pad->GetLayerSet() & aLayerSet ).any() )
  1310. return pad;
  1311. }
  1312. }
  1313. return nullptr;
  1314. }
  1315. PAD* BOARD::GetPad( std::vector<PAD*>& aPadList, const VECTOR2I& aPosition, LSET aLayerSet ) const
  1316. {
  1317. // Search aPadList for aPosition
  1318. // aPadList is sorted by X then Y values, and a fast binary search is used
  1319. int idxmax = aPadList.size() - 1;
  1320. int delta = aPadList.size();
  1321. int idx = 0; // Starting index is the beginning of list
  1322. while( delta )
  1323. {
  1324. // Calculate half size of remaining interval to test.
  1325. // Ensure the computed value is not truncated (too small)
  1326. if( (delta & 1) && ( delta > 1 ) )
  1327. delta++;
  1328. delta /= 2;
  1329. PAD* pad = aPadList[idx];
  1330. if( pad->GetPosition() == aPosition ) // candidate found
  1331. {
  1332. // The pad must match the layer mask:
  1333. if( ( aLayerSet & pad->GetLayerSet() ).any() )
  1334. return pad;
  1335. // More than one pad can be at aPosition
  1336. // search for a pad at aPosition that matched this mask
  1337. // search next
  1338. for( int ii = idx+1; ii <= idxmax; ii++ )
  1339. {
  1340. pad = aPadList[ii];
  1341. if( pad->GetPosition() != aPosition )
  1342. break;
  1343. if( ( aLayerSet & pad->GetLayerSet() ).any() )
  1344. return pad;
  1345. }
  1346. // search previous
  1347. for( int ii = idx - 1 ;ii >=0; ii-- )
  1348. {
  1349. pad = aPadList[ii];
  1350. if( pad->GetPosition() != aPosition )
  1351. break;
  1352. if( ( aLayerSet & pad->GetLayerSet() ).any() )
  1353. return pad;
  1354. }
  1355. // Not found:
  1356. return nullptr;
  1357. }
  1358. if( pad->GetPosition().x == aPosition.x ) // Must search considering Y coordinate
  1359. {
  1360. if( pad->GetPosition().y < aPosition.y ) // Must search after this item
  1361. {
  1362. idx += delta;
  1363. if( idx > idxmax )
  1364. idx = idxmax;
  1365. }
  1366. else // Must search before this item
  1367. {
  1368. idx -= delta;
  1369. if( idx < 0 )
  1370. idx = 0;
  1371. }
  1372. }
  1373. else if( pad->GetPosition().x < aPosition.x ) // Must search after this item
  1374. {
  1375. idx += delta;
  1376. if( idx > idxmax )
  1377. idx = idxmax;
  1378. }
  1379. else // Must search before this item
  1380. {
  1381. idx -= delta;
  1382. if( idx < 0 )
  1383. idx = 0;
  1384. }
  1385. }
  1386. return nullptr;
  1387. }
  1388. /**
  1389. * Used by #GetSortedPadListByXCoord to sort a pad list by X coordinate value.
  1390. *
  1391. * This function is used to build ordered pads lists
  1392. */
  1393. bool sortPadsByXthenYCoord( PAD* const & aLH, PAD* const & aRH )
  1394. {
  1395. if( aLH->GetPosition().x == aRH->GetPosition().x )
  1396. return aLH->GetPosition().y < aRH->GetPosition().y;
  1397. return aLH->GetPosition().x < aRH->GetPosition().x;
  1398. }
  1399. void BOARD::GetSortedPadListByXthenYCoord( std::vector<PAD*>& aVector, int aNetCode ) const
  1400. {
  1401. for( FOOTPRINT* footprint : Footprints() )
  1402. {
  1403. for( PAD* pad : footprint->Pads( ) )
  1404. {
  1405. if( aNetCode < 0 || pad->GetNetCode() == aNetCode )
  1406. aVector.push_back( pad );
  1407. }
  1408. }
  1409. std::sort( aVector.begin(), aVector.end(), sortPadsByXthenYCoord );
  1410. }
  1411. std::tuple<int, double, double> BOARD::GetTrackLength( const PCB_TRACK& aTrack ) const
  1412. {
  1413. int count = 0;
  1414. double length = 0.0;
  1415. double package_length = 0.0;
  1416. auto connectivity = GetBoard()->GetConnectivity();
  1417. BOARD_STACKUP& stackup = GetDesignSettings().GetStackupDescriptor();
  1418. bool useHeight = GetDesignSettings().m_UseHeightForLengthCalcs;
  1419. for( BOARD_CONNECTED_ITEM* item : connectivity->GetConnectedItems(
  1420. static_cast<const BOARD_CONNECTED_ITEM*>( &aTrack ),
  1421. { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T } ) )
  1422. {
  1423. count++;
  1424. if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
  1425. {
  1426. if( track->Type() == PCB_VIA_T && useHeight )
  1427. {
  1428. PCB_VIA* via = static_cast<PCB_VIA*>( track );
  1429. length += stackup.GetLayerDistance( via->TopLayer(), via->BottomLayer() );
  1430. continue;
  1431. }
  1432. else if( track->Type() == PCB_ARC_T )
  1433. {
  1434. // Note: we don't apply the clip-to-pad optimization if an arc ends in a pad
  1435. // Room for future improvement.
  1436. length += track->GetLength();
  1437. continue;
  1438. }
  1439. bool inPad = false;
  1440. SEG trackSeg( track->GetStart(), track->GetEnd() );
  1441. double segLen = trackSeg.Length();
  1442. double segInPadLen = 0;
  1443. for( auto pad_it : connectivity->GetConnectedPads( item ) )
  1444. {
  1445. PAD* pad = static_cast<PAD*>( pad_it );
  1446. bool hitStart = pad->HitTest( track->GetStart(), track->GetWidth() / 2 );
  1447. bool hitEnd = pad->HitTest( track->GetEnd(), track->GetWidth() / 2 );
  1448. if( hitStart && hitEnd )
  1449. {
  1450. inPad = true;
  1451. break;
  1452. }
  1453. else if( hitStart || hitEnd )
  1454. {
  1455. VECTOR2I loc;
  1456. // We may not collide even if we passed the bounding-box hit test
  1457. if( pad->GetEffectivePolygon()->Collide( trackSeg, 0, nullptr, &loc ) )
  1458. {
  1459. // Part 1: length of the seg to the intersection with the pad poly
  1460. if( hitStart )
  1461. trackSeg.A = loc;
  1462. else
  1463. trackSeg.B = loc;
  1464. segLen = trackSeg.Length();
  1465. // Part 2: length from the intersection to the pad anchor
  1466. segInPadLen += ( loc - pad->GetPosition() ).EuclideanNorm();
  1467. }
  1468. }
  1469. }
  1470. if( !inPad )
  1471. length += segLen + segInPadLen;
  1472. }
  1473. else if( PAD* pad = dyn_cast<PAD*>( item ) )
  1474. {
  1475. package_length += pad->GetPadToDieLength();
  1476. }
  1477. }
  1478. return std::make_tuple( count, length, package_length );
  1479. }
  1480. FOOTPRINT* BOARD::GetFootprint( const VECTOR2I& aPosition, PCB_LAYER_ID aActiveLayer,
  1481. bool aVisibleOnly, bool aIgnoreLocked ) const
  1482. {
  1483. FOOTPRINT* footprint = nullptr;
  1484. FOOTPRINT* alt_footprint = nullptr;
  1485. int min_dim = 0x7FFFFFFF;
  1486. int alt_min_dim = 0x7FFFFFFF;
  1487. bool current_layer_back = IsBackLayer( aActiveLayer );
  1488. for( FOOTPRINT* candidate : m_footprints )
  1489. {
  1490. // is the ref point within the footprint's bounds?
  1491. if( !candidate->HitTest( aPosition ) )
  1492. continue;
  1493. // if caller wants to ignore locked footprints, and this one is locked, skip it.
  1494. if( aIgnoreLocked && candidate->IsLocked() )
  1495. continue;
  1496. PCB_LAYER_ID layer = candidate->GetLayer();
  1497. // Filter non visible footprints if requested
  1498. if( !aVisibleOnly || IsFootprintLayerVisible( layer ) )
  1499. {
  1500. BOX2I bb = candidate->GetBoundingBox( false, false );
  1501. int offx = bb.GetX() + bb.GetWidth() / 2;
  1502. int offy = bb.GetY() + bb.GetHeight() / 2;
  1503. // off x & offy point to the middle of the box.
  1504. int dist = ( aPosition.x - offx ) * ( aPosition.x - offx ) +
  1505. ( aPosition.y - offy ) * ( aPosition.y - offy );
  1506. if( current_layer_back == IsBackLayer( layer ) )
  1507. {
  1508. if( dist <= min_dim )
  1509. {
  1510. // better footprint shown on the active side
  1511. footprint = candidate;
  1512. min_dim = dist;
  1513. }
  1514. }
  1515. else if( aVisibleOnly && IsFootprintLayerVisible( layer ) )
  1516. {
  1517. if( dist <= alt_min_dim )
  1518. {
  1519. // better footprint shown on the other side
  1520. alt_footprint = candidate;
  1521. alt_min_dim = dist;
  1522. }
  1523. }
  1524. }
  1525. }
  1526. if( footprint )
  1527. return footprint;
  1528. if( alt_footprint)
  1529. return alt_footprint;
  1530. return nullptr;
  1531. }
  1532. std::list<ZONE*> BOARD::GetZoneList( bool aIncludeZonesInFootprints ) const
  1533. {
  1534. std::list<ZONE*> zones;
  1535. for( ZONE* zone : Zones() )
  1536. zones.push_back( zone );
  1537. if( aIncludeZonesInFootprints )
  1538. {
  1539. for( FOOTPRINT* footprint : m_footprints )
  1540. {
  1541. for( ZONE* zone : footprint->Zones() )
  1542. zones.push_back( zone );
  1543. }
  1544. }
  1545. return zones;
  1546. }
  1547. ZONE* BOARD::AddArea( PICKED_ITEMS_LIST* aNewZonesList, int aNetcode, PCB_LAYER_ID aLayer,
  1548. VECTOR2I aStartPointPosition, ZONE_BORDER_DISPLAY_STYLE aHatch )
  1549. {
  1550. ZONE* new_area = new ZONE( this );
  1551. new_area->SetNetCode( aNetcode );
  1552. new_area->SetLayer( aLayer );
  1553. m_zones.push_back( new_area );
  1554. new_area->SetHatchStyle( (ZONE_BORDER_DISPLAY_STYLE) aHatch );
  1555. // Add the first corner to the new zone
  1556. new_area->AppendCorner( aStartPointPosition, -1 );
  1557. if( aNewZonesList )
  1558. {
  1559. ITEM_PICKER picker( nullptr, new_area, UNDO_REDO::NEWITEM );
  1560. aNewZonesList->PushItem( picker );
  1561. }
  1562. return new_area;
  1563. }
  1564. bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
  1565. OUTLINE_ERROR_HANDLER* aErrorHandler,
  1566. bool aAllowUseArcsInPolygons )
  1567. {
  1568. // max dist from one endPt to next startPt: use the current value
  1569. int chainingEpsilon = GetOutlinesChainingEpsilon();
  1570. bool success = BuildBoardPolygonOutlines( this, aOutlines, GetDesignSettings().m_MaxError,
  1571. chainingEpsilon, aErrorHandler, aAllowUseArcsInPolygons );
  1572. // Make polygon strictly simple to avoid issues (especially in 3D viewer)
  1573. aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  1574. return success;
  1575. }
  1576. const std::vector<PAD*> BOARD::GetPads() const
  1577. {
  1578. std::vector<PAD*> allPads;
  1579. for( FOOTPRINT* footprint : Footprints() )
  1580. {
  1581. for( PAD* pad : footprint->Pads() )
  1582. allPads.push_back( pad );
  1583. }
  1584. return allPads;
  1585. }
  1586. const std::vector<BOARD_CONNECTED_ITEM*> BOARD::AllConnectedItems()
  1587. {
  1588. std::vector<BOARD_CONNECTED_ITEM*> items;
  1589. for( PCB_TRACK* track : Tracks() )
  1590. items.push_back( track );
  1591. for( FOOTPRINT* footprint : Footprints() )
  1592. {
  1593. for( PAD* pad : footprint->Pads() )
  1594. items.push_back( pad );
  1595. }
  1596. for( ZONE* zone : Zones() )
  1597. items.push_back( zone );
  1598. return items;
  1599. }
  1600. void BOARD::MapNets( const BOARD* aDestBoard )
  1601. {
  1602. for( BOARD_CONNECTED_ITEM* item : AllConnectedItems() )
  1603. {
  1604. NETINFO_ITEM* netInfo = aDestBoard->FindNet( item->GetNetname() );
  1605. if( netInfo )
  1606. item->SetNet( netInfo );
  1607. else
  1608. item->SetNetCode( 0 );
  1609. }
  1610. }
  1611. void BOARD::SanitizeNetcodes()
  1612. {
  1613. for ( BOARD_CONNECTED_ITEM* item : AllConnectedItems() )
  1614. {
  1615. if( FindNet( item->GetNetCode() ) == nullptr )
  1616. item->SetNetCode( NETINFO_LIST::ORPHANED );
  1617. }
  1618. }
  1619. void BOARD::AddListener( BOARD_LISTENER* aListener )
  1620. {
  1621. if( !alg::contains( m_listeners, aListener ) )
  1622. m_listeners.push_back( aListener );
  1623. }
  1624. void BOARD::RemoveListener( BOARD_LISTENER* aListener )
  1625. {
  1626. auto i = std::find( m_listeners.begin(), m_listeners.end(), aListener );
  1627. if( i != m_listeners.end() )
  1628. {
  1629. std::iter_swap( i, m_listeners.end() - 1 );
  1630. m_listeners.pop_back();
  1631. }
  1632. }
  1633. void BOARD::RemoveAllListeners()
  1634. {
  1635. m_listeners.clear();
  1636. }
  1637. void BOARD::OnItemChanged( BOARD_ITEM* aItem )
  1638. {
  1639. InvokeListeners( &BOARD_LISTENER::OnBoardItemChanged, *this, aItem );
  1640. }
  1641. void BOARD::OnItemsChanged( std::vector<BOARD_ITEM*>& aItems )
  1642. {
  1643. InvokeListeners( &BOARD_LISTENER::OnBoardItemsChanged, *this, aItems );
  1644. }
  1645. void BOARD::ResetNetHighLight()
  1646. {
  1647. m_highLight.Clear();
  1648. m_highLightPrevious.Clear();
  1649. InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
  1650. }
  1651. void BOARD::SetHighLightNet( int aNetCode, bool aMulti )
  1652. {
  1653. if( !m_highLight.m_netCodes.count( aNetCode ) )
  1654. {
  1655. if( !aMulti )
  1656. m_highLight.m_netCodes.clear();
  1657. m_highLight.m_netCodes.insert( aNetCode );
  1658. InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
  1659. }
  1660. }
  1661. void BOARD::HighLightON( bool aValue )
  1662. {
  1663. if( m_highLight.m_highLightOn != aValue )
  1664. {
  1665. m_highLight.m_highLightOn = aValue;
  1666. InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
  1667. }
  1668. }
  1669. wxString BOARD::GroupsSanityCheck( bool repair )
  1670. {
  1671. if( repair )
  1672. {
  1673. while( GroupsSanityCheckInternal( repair ) != wxEmptyString )
  1674. {};
  1675. return wxEmptyString;
  1676. }
  1677. return GroupsSanityCheckInternal( repair );
  1678. }
  1679. wxString BOARD::GroupsSanityCheckInternal( bool repair )
  1680. {
  1681. // Cycle detection
  1682. //
  1683. // Each group has at most one parent group.
  1684. // So we start at group 0 and traverse the parent chain, marking groups seen along the way.
  1685. // If we ever see a group that we've already marked, that's a cycle.
  1686. // If we reach the end of the chain, we know all groups in that chain are not part of any cycle.
  1687. //
  1688. // Algorithm below is linear in the # of groups because each group is visited only once.
  1689. // There may be extra time taken due to the container access calls and iterators.
  1690. //
  1691. // Groups we know are cycle free
  1692. std::unordered_set<PCB_GROUP*> knownCycleFreeGroups;
  1693. // Groups in the current chain we're exploring.
  1694. std::unordered_set<PCB_GROUP*> currentChainGroups;
  1695. // Groups we haven't checked yet.
  1696. std::unordered_set<PCB_GROUP*> toCheckGroups;
  1697. // Initialize set of groups to check that could participate in a cycle.
  1698. for( PCB_GROUP* group : Groups() )
  1699. toCheckGroups.insert( group);
  1700. while( !toCheckGroups.empty() )
  1701. {
  1702. currentChainGroups.clear();
  1703. PCB_GROUP* group = *toCheckGroups.begin();
  1704. while( true )
  1705. {
  1706. if( currentChainGroups.find( group ) != currentChainGroups.end() )
  1707. {
  1708. if( repair )
  1709. Remove( group );
  1710. return "Cycle detected in group membership";
  1711. }
  1712. else if( knownCycleFreeGroups.find( group ) != knownCycleFreeGroups.end() )
  1713. {
  1714. // Parent is a group we know does not lead to a cycle
  1715. break;
  1716. }
  1717. currentChainGroups.insert( group );
  1718. // We haven't visited currIdx yet, so it must be in toCheckGroups
  1719. toCheckGroups.erase( group );
  1720. group = group->GetParentGroup();
  1721. if( !group )
  1722. {
  1723. // end of chain and no cycles found in this chain
  1724. break;
  1725. }
  1726. }
  1727. // No cycles found in chain, so add it to set of groups we know don't participate
  1728. // in a cycle.
  1729. knownCycleFreeGroups.insert( currentChainGroups.begin(), currentChainGroups.end() );
  1730. }
  1731. // Success
  1732. return "";
  1733. }
  1734. BOARD::GroupLegalOpsField BOARD::GroupLegalOps( const PCB_SELECTION& selection ) const
  1735. {
  1736. bool hasGroup = false;
  1737. bool hasMember = false;
  1738. for( EDA_ITEM* item : selection )
  1739. {
  1740. if( item->Type() == PCB_GROUP_T )
  1741. hasGroup = true;
  1742. if( static_cast<BOARD_ITEM*>( item )->GetParentGroup() )
  1743. hasMember = true;
  1744. }
  1745. GroupLegalOpsField legalOps;
  1746. legalOps.create = true;
  1747. legalOps.removeItems = hasMember;
  1748. legalOps.ungroup = hasGroup;
  1749. return legalOps;
  1750. }
  1751. bool BOARD::cmp_items::operator() ( const BOARD_ITEM* a, const BOARD_ITEM* b ) const
  1752. {
  1753. if( a->Type() != b->Type() )
  1754. return a->Type() < b->Type();
  1755. if( a->GetLayer() != b->GetLayer() )
  1756. return a->GetLayer() < b->GetLayer();
  1757. if( a->GetPosition().x != b->GetPosition().x )
  1758. return a->GetPosition().x < b->GetPosition().x;
  1759. if( a->GetPosition().y != b->GetPosition().y )
  1760. return a->GetPosition().y < b->GetPosition().y;
  1761. if( a->m_Uuid != b->m_Uuid ) // shopuld be always the case foer valid boards
  1762. return a->m_Uuid < b->m_Uuid;
  1763. return a < b;
  1764. }
  1765. bool BOARD::cmp_drawings::operator()( const BOARD_ITEM* aFirst,
  1766. const BOARD_ITEM* aSecond ) const
  1767. {
  1768. if( aFirst->Type() != aSecond->Type() )
  1769. return aFirst->Type() < aSecond->Type();
  1770. if( aFirst->GetLayer() != aSecond->GetLayer() )
  1771. return aFirst->GetLayer() < aSecond->GetLayer();
  1772. if( aFirst->Type() == PCB_SHAPE_T )
  1773. {
  1774. const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aFirst );
  1775. const PCB_SHAPE* other = static_cast<const PCB_SHAPE*>( aSecond );
  1776. return shape->Compare( other );
  1777. }
  1778. else if( aFirst->Type() == PCB_TEXT_T )
  1779. {
  1780. const PCB_TEXT* text = static_cast<const PCB_TEXT*>( aFirst );
  1781. const PCB_TEXT* other = static_cast<const PCB_TEXT*>( aSecond );
  1782. return text->Compare( other );
  1783. }
  1784. else if( aFirst->Type() == PCB_TEXTBOX_T )
  1785. {
  1786. const PCB_TEXTBOX* textbox = static_cast<const PCB_TEXTBOX*>( aFirst );
  1787. const PCB_TEXTBOX* other = static_cast<const PCB_TEXTBOX*>( aSecond );
  1788. return textbox->PCB_SHAPE::Compare( other ) && textbox->EDA_TEXT::Compare( other );
  1789. }
  1790. return aFirst->m_Uuid < aSecond->m_Uuid;
  1791. }
  1792. void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer,
  1793. SHAPE_POLY_SET& aOutlines ) const
  1794. {
  1795. int maxError = GetDesignSettings().m_MaxError;
  1796. // convert tracks and vias:
  1797. for( const PCB_TRACK* track : m_tracks )
  1798. {
  1799. if( !track->IsOnLayer( aLayer ) )
  1800. continue;
  1801. track->TransformShapeToPolygon( aOutlines, aLayer, 0, maxError, ERROR_INSIDE );
  1802. }
  1803. // convert pads and other copper items in footprints
  1804. for( const FOOTPRINT* footprint : m_footprints )
  1805. {
  1806. footprint->TransformPadsToPolySet( aOutlines, aLayer, 0, maxError, ERROR_INSIDE );
  1807. // Microwave footprints may have items on copper layers
  1808. footprint->TransformFPShapesToPolySet( aOutlines, aLayer, 0, maxError, ERROR_INSIDE,
  1809. true, /* include text */
  1810. true, /* include shapes */
  1811. false /* include private items */ );
  1812. for( const ZONE* zone : footprint->Zones() )
  1813. {
  1814. if( zone->GetLayerSet().test( aLayer ) )
  1815. zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
  1816. }
  1817. }
  1818. // convert copper zones
  1819. for( const ZONE* zone : Zones() )
  1820. {
  1821. if( zone->GetLayerSet().test( aLayer ) )
  1822. zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
  1823. }
  1824. // convert graphic items on copper layers (texts)
  1825. for( const BOARD_ITEM* item : m_drawings )
  1826. {
  1827. if( !item->IsOnLayer( aLayer ) )
  1828. continue;
  1829. switch( item->Type() )
  1830. {
  1831. case PCB_SHAPE_T:
  1832. {
  1833. const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
  1834. shape->TransformShapeToPolygon( aOutlines, aLayer, 0, maxError, ERROR_INSIDE );
  1835. break;
  1836. }
  1837. case PCB_TEXT_T:
  1838. {
  1839. const PCB_TEXT* text = static_cast<const PCB_TEXT*>( item );
  1840. text->TransformTextToPolySet( aOutlines, aLayer, 0, maxError, ERROR_INSIDE );
  1841. break;
  1842. }
  1843. case PCB_TEXTBOX_T:
  1844. {
  1845. const PCB_TEXTBOX* textbox = static_cast<const PCB_TEXTBOX*>( item );
  1846. // plot border
  1847. textbox->PCB_SHAPE::TransformShapeToPolygon( aOutlines, aLayer, 0, maxError,
  1848. ERROR_INSIDE );
  1849. // plot text
  1850. textbox->TransformTextToPolySet( aOutlines, aLayer, 0, maxError, ERROR_INSIDE );
  1851. break;
  1852. }
  1853. default:
  1854. break;
  1855. }
  1856. }
  1857. }
  1858. const std::set<BOARD_ITEM*> BOARD::GetItemSet()
  1859. {
  1860. std::set<BOARD_ITEM*> items;
  1861. #define INSERT_ITEMS( collection ) \
  1862. for( BOARD_ITEM * item : collection ) \
  1863. items.insert( item );
  1864. INSERT_ITEMS( m_tracks )
  1865. INSERT_ITEMS( m_footprints )
  1866. INSERT_ITEMS( m_drawings )
  1867. INSERT_ITEMS( m_zones )
  1868. INSERT_ITEMS( m_markers )
  1869. #undef INSERT_ITEMS
  1870. return items;
  1871. }