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.

2298 lines
62 KiB

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