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.

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