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.

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