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.

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