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.

3193 lines
90 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 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 years ago
1 year ago
1 year ago
7 months ago
7 months ago
14 years ago
14 years ago
14 years ago
5 years ago
5 years ago
14 years ago
4 years ago
4 months ago
18 years ago
18 years ago
17 years ago
17 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
4 months ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
18 years ago
18 years ago
18 years ago
18 years ago
Changed the way of looking up NETINFO_ITEM using net names (using boost::unordered_map). Added a hash function (wxString) for that. Introduced NETINFO_ITEM::GetNetItem( wxString ). BOARD::FindNet() uses the map. Net codes are updated upon net list update. (BOARD::ReplaceNetlist()) Added in some places (mostly class_board.cpp) pad->SetNet() calls to synchronize net codes. On creation of NETINFO_LIST, the first NETINFO_ITEM is added (the unconnected items net). Removed COMPONENT_NET::m_netNumber, as it was not used anywhere. Added an assert to D_PAD::GetNetname(), checking if net code and net name is consistent for unconnected pads. Added an assert for NETINFO_LIST::AppendNet() to assure that appended nets are unique. It seems that at this point: - Updating net lists works fine. The only difference between the file ouput is that after changes it contains empty nets as well. - Nets are not saved in the lexical order. Still, net names and net codes are properly assigned to all items in the .kicad_pcb file. It is going to be addressed in the next commit. I believe it should not create any problems, as pads are sorted by their net names anyway (NETINFO_LIST::buildPadsFullList()) Performed tests: - Created a blank PCB, saved as pic_programmer.kicad_pcb (from demos folder). Updated net lists. .kicad_pcb file (comparing to the results from master branch) differ with net order (as mentioned before), net codes and timestamps. - Removed some of components from the above .kicad_pcb file and updated net lists. Modules reappeared. .kicad_pcb file differs in the same way as described above. - Trying to change a pad net name (via properties dialog) results in assert being fired. It is done on purpose (as there is a call to GetNetname() and net name and net code do not match). This will not happen after the next commit. - Prepared a simple project (starting with schematics). Imported net list, changed schematic, reimported net list - changes are applied. - Eagle & KiCad legacy boards seem to load without any problem.
12 years ago
18 years ago
5 years ago
18 years ago
5 years ago
7 months ago
5 years ago
7 months ago
8 years ago
7 months ago
5 years ago
14 years ago
14 years ago
14 years ago
14 years ago
5 years ago
4 months ago
4 months ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 months 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 The KiCad Developers, see AUTHORS.txt for contributors.
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, you may find one here:
  22. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  23. * or you may search the http://www.gnu.org website for the version 2 license,
  24. * or you may write to the Free Software Foundation, Inc.,
  25. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  26. */
  27. #include <iterator>
  28. #include <wx/log.h>
  29. #include <drc/drc_rtree.h>
  30. #include <board_design_settings.h>
  31. #include <board_commit.h>
  32. #include <board.h>
  33. #include <core/arraydim.h>
  34. #include <core/kicad_algo.h>
  35. #include <connectivity/connectivity_data.h>
  36. #include <convert_shape_list_to_polygon.h>
  37. #include <footprint.h>
  38. #include <font/outline_font.h>
  39. #include <length_delay_calculation/length_delay_calculation.h>
  40. #include <lset.h>
  41. #include <pcb_base_frame.h>
  42. #include <pcb_track.h>
  43. #include <pcb_marker.h>
  44. #include <pcb_group.h>
  45. #include <pcb_generator.h>
  46. #include <pcb_target.h>
  47. #include <pcb_shape.h>
  48. #include <pcb_text.h>
  49. #include <pcb_textbox.h>
  50. #include <pcb_table.h>
  51. #include <pcb_dimension.h>
  52. #include <pgm_base.h>
  53. #include <pcbnew_settings.h>
  54. #include <progress_reporter.h>
  55. #include <project.h>
  56. #include <project/component_class_settings.h>
  57. #include <project/net_settings.h>
  58. #include <project/project_file.h>
  59. #include <project/project_local_settings.h>
  60. #include <ratsnest/ratsnest_data.h>
  61. #include <reporter.h>
  62. #include <tool/tool_manager.h>
  63. #include <tool/selection_conditions.h>
  64. #include <string_utils.h>
  65. #include <thread_pool.h>
  66. #include <zone.h>
  67. #include <mutex>
  68. #include <pcb_board_outline.h>
  69. // This is an odd place for this, but CvPcb won't link if it's in board_item.cpp like I first
  70. // tried it.
  71. VECTOR2I BOARD_ITEM::ZeroOffset( 0, 0 );
  72. BOARD::BOARD() :
  73. BOARD_ITEM_CONTAINER( (BOARD_ITEM*) nullptr, PCB_T ),
  74. m_LegacyDesignSettingsLoaded( false ),
  75. m_LegacyCopperEdgeClearanceLoaded( false ),
  76. m_LegacyNetclassesLoaded( false ),
  77. m_boardUse( BOARD_USE::NORMAL ),
  78. m_timeStamp( 1 ),
  79. m_paper( PAGE_INFO::A4 ),
  80. m_project( nullptr ),
  81. m_userUnits( EDA_UNITS::MM ),
  82. m_designSettings( new BOARD_DESIGN_SETTINGS( nullptr, "board.design_settings" ) ),
  83. m_NetInfo( this ),
  84. m_embedFonts( false ),
  85. m_embeddedFilesDelegate( nullptr ),
  86. m_componentClassManager( std::make_unique<COMPONENT_CLASS_MANAGER>( this ) ),
  87. m_lengthDelayCalc( std::make_unique<LENGTH_DELAY_CALCULATION>( this ) )
  88. {
  89. // A too small value do not allow connecting 2 shapes (i.e. segments) not exactly connected
  90. // A too large value do not allow safely connecting 2 shapes like very short segments.
  91. m_outlinesChainingEpsilon = pcbIUScale.mmToIU( DEFAULT_CHAINING_EPSILON_MM );
  92. m_DRCMaxClearance = 0;
  93. m_DRCMaxPhysicalClearance = 0;
  94. m_boardOutline = new PCB_BOARD_OUTLINE( this );
  95. // we have not loaded a board yet, assume latest until then.
  96. m_fileFormatVersionAtLoad = LEGACY_BOARD_FILE_VERSION;
  97. for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
  98. {
  99. m_layers[layer].m_name = GetStandardLayerName( ToLAYER_ID( layer ) );
  100. if( IsCopperLayer( layer ) )
  101. m_layers[layer].m_type = LT_SIGNAL;
  102. else if( layer >= User_1 && layer & 1 )
  103. m_layers[layer].m_type = LT_AUX;
  104. else
  105. m_layers[layer].m_type = LT_UNDEFINED;
  106. }
  107. recalcOpposites();
  108. // Creates a zone to show sloder mask bridges created by a min web value
  109. // it it just to show them
  110. m_SolderMaskBridges = new ZONE( this );
  111. m_SolderMaskBridges->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER );
  112. m_SolderMaskBridges->SetLayerSet( LSET().set( F_Mask ).set( B_Mask ) );
  113. int infinity = ( std::numeric_limits<int>::max() / 2 ) - pcbIUScale.mmToIU( 1 );
  114. m_SolderMaskBridges->Outline()->NewOutline();
  115. m_SolderMaskBridges->Outline()->Append( VECTOR2I( -infinity, -infinity ) );
  116. m_SolderMaskBridges->Outline()->Append( VECTOR2I( -infinity, +infinity ) );
  117. m_SolderMaskBridges->Outline()->Append( VECTOR2I( +infinity, +infinity ) );
  118. m_SolderMaskBridges->Outline()->Append( VECTOR2I( +infinity, -infinity ) );
  119. m_SolderMaskBridges->SetMinThickness( 0 );
  120. BOARD_DESIGN_SETTINGS& bds = GetDesignSettings();
  121. // Initialize default netclass.
  122. bds.m_NetSettings->SetDefaultNetclass( std::make_shared<NETCLASS>( NETCLASS::Default ) );
  123. bds.m_NetSettings->GetDefaultNetclass()->SetDescription(
  124. _( "This is the default net class." ) );
  125. bds.UseCustomTrackViaSize( false );
  126. // Initialize ratsnest
  127. m_connectivity.reset( new CONNECTIVITY_DATA() );
  128. // Set flag bits on these that will only be cleared if these are loaded from a legacy file
  129. m_LegacyVisibleLayers.reset().set( Rescue );
  130. m_LegacyVisibleItems.reset().set( GAL_LAYER_INDEX( GAL_LAYER_ID_BITMASK_END ) );
  131. }
  132. BOARD::~BOARD()
  133. {
  134. m_itemByIdCache.clear();
  135. // Clean up the owned elements
  136. DeleteMARKERs();
  137. delete m_SolderMaskBridges;
  138. BOARD_ITEM_SET ownedItems = GetItemSet();
  139. m_zones.clear();
  140. m_footprints.clear();
  141. m_tracks.clear();
  142. m_drawings.clear();
  143. m_groups.clear();
  144. // Generators not currently returned by GetItemSet
  145. for( PCB_GENERATOR* g : m_generators )
  146. ownedItems.insert( g );
  147. delete m_boardOutline;
  148. m_generators.clear();
  149. // Delete the owned items after clearing the containers, because some item dtors
  150. // cause call chains that query the containers
  151. for( BOARD_ITEM* item : ownedItems )
  152. delete item;
  153. // Remove any listeners
  154. RemoveAllListeners();
  155. }
  156. bool BOARD::BuildConnectivity( PROGRESS_REPORTER* aReporter )
  157. {
  158. if( !GetConnectivity()->Build( this, aReporter ) )
  159. return false;
  160. UpdateRatsnestExclusions();
  161. return true;
  162. }
  163. void BOARD::SetProject( PROJECT* aProject, bool aReferenceOnly )
  164. {
  165. if( m_project )
  166. ClearProject();
  167. m_project = aProject;
  168. if( aProject && !aReferenceOnly )
  169. {
  170. PROJECT_FILE& project = aProject->GetProjectFile();
  171. // Link the design settings object to the project file
  172. project.m_BoardSettings = &GetDesignSettings();
  173. // Set parent, which also will load the values from JSON stored in the project if we don't
  174. // have legacy design settings loaded already
  175. project.m_BoardSettings->SetParent( &project, !m_LegacyDesignSettingsLoaded );
  176. // The DesignSettings' netclasses pointer will be pointing to its internal netclasses
  177. // list at this point. If we loaded anything into it from a legacy board file then we
  178. // want to transfer it over to the project netclasses list.
  179. if( m_LegacyNetclassesLoaded )
  180. {
  181. std::shared_ptr<NET_SETTINGS> legacySettings = GetDesignSettings().m_NetSettings;
  182. std::shared_ptr<NET_SETTINGS>& projectSettings = project.NetSettings();
  183. projectSettings->SetDefaultNetclass( legacySettings->GetDefaultNetclass() );
  184. projectSettings->SetNetclasses( legacySettings->GetNetclasses() );
  185. projectSettings->SetNetclassPatternAssignments(
  186. std::move( legacySettings->GetNetclassPatternAssignments() ) );
  187. }
  188. // Now update the DesignSettings' netclass pointer to point into the project.
  189. GetDesignSettings().m_NetSettings = project.NetSettings();
  190. }
  191. }
  192. void BOARD::ClearProject()
  193. {
  194. if( !m_project )
  195. return;
  196. PROJECT_FILE& project = m_project->GetProjectFile();
  197. // Owned by the BOARD
  198. if( project.m_BoardSettings )
  199. {
  200. project.ReleaseNestedSettings( project.m_BoardSettings );
  201. project.m_BoardSettings = nullptr;
  202. }
  203. GetDesignSettings().m_NetSettings = nullptr;
  204. GetDesignSettings().SetParent( nullptr );
  205. m_project = nullptr;
  206. }
  207. void BOARD::IncrementTimeStamp()
  208. {
  209. std::unique_lock<std::shared_mutex> writeLock( m_CachesMutex );
  210. m_timeStamp++;
  211. if( !m_IntersectsAreaCache.empty()
  212. || !m_EnclosedByAreaCache.empty()
  213. || !m_IntersectsCourtyardCache.empty()
  214. || !m_IntersectsFCourtyardCache.empty()
  215. || !m_IntersectsBCourtyardCache.empty()
  216. || !m_LayerExpressionCache.empty()
  217. || !m_ZoneBBoxCache.empty()
  218. || m_CopperItemRTreeCache
  219. || m_maxClearanceValue.has_value()
  220. || !m_itemByIdCache.empty() )
  221. {
  222. m_IntersectsAreaCache.clear();
  223. m_EnclosedByAreaCache.clear();
  224. m_IntersectsCourtyardCache.clear();
  225. m_IntersectsFCourtyardCache.clear();
  226. m_IntersectsBCourtyardCache.clear();
  227. m_LayerExpressionCache.clear();
  228. m_ZoneBBoxCache.clear();
  229. m_CopperItemRTreeCache = nullptr;
  230. // These are always regenerated before use, but still probably safer to clear them
  231. // while we're here.
  232. m_DRCMaxClearance = 0;
  233. m_DRCMaxPhysicalClearance = 0;
  234. m_DRCZones.clear();
  235. m_DRCCopperZones.clear();
  236. m_ZoneIsolatedIslandsMap.clear();
  237. m_CopperZoneRTreeCache.clear();
  238. m_maxClearanceValue.reset();
  239. m_itemByIdCache.clear();
  240. }
  241. }
  242. void BOARD::UpdateRatsnestExclusions()
  243. {
  244. std::set<std::pair<KIID, KIID>> m_ratsnestExclusions;
  245. for( PCB_MARKER* marker : GetBoard()->Markers() )
  246. {
  247. if( marker->GetMarkerType() == MARKER_BASE::MARKER_RATSNEST && marker->IsExcluded() )
  248. {
  249. const std::shared_ptr<RC_ITEM>& rcItem = marker->GetRCItem();
  250. m_ratsnestExclusions.emplace( rcItem->GetMainItemID(), rcItem->GetAuxItemID() );
  251. m_ratsnestExclusions.emplace( rcItem->GetAuxItemID(), rcItem->GetMainItemID() );
  252. }
  253. }
  254. GetConnectivity()->RunOnUnconnectedEdges(
  255. [&]( CN_EDGE& aEdge )
  256. {
  257. if( aEdge.GetSourceNode() && aEdge.GetTargetNode()
  258. && !aEdge.GetSourceNode()->Dirty() && !aEdge.GetTargetNode()->Dirty() )
  259. {
  260. std::pair<KIID, KIID> ids = { aEdge.GetSourceNode()->Parent()->m_Uuid,
  261. aEdge.GetTargetNode()->Parent()->m_Uuid };
  262. aEdge.SetVisible( m_ratsnestExclusions.count( ids ) == 0 );
  263. }
  264. return true;
  265. } );
  266. }
  267. void BOARD::RecordDRCExclusions()
  268. {
  269. m_designSettings->m_DrcExclusions.clear();
  270. m_designSettings->m_DrcExclusionComments.clear();
  271. for( PCB_MARKER* marker : m_markers )
  272. {
  273. if( marker->IsExcluded() )
  274. {
  275. wxString serialized = marker->SerializeToString();
  276. m_designSettings->m_DrcExclusions.insert( serialized );
  277. m_designSettings->m_DrcExclusionComments[ serialized ] = marker->GetComment();
  278. }
  279. }
  280. if( m_project )
  281. {
  282. if( PROJECT_FILE* projectFile = &m_project->GetProjectFile() )
  283. {
  284. if( BOARD_DESIGN_SETTINGS* prjSettings = projectFile->m_BoardSettings )
  285. {
  286. prjSettings->m_DrcExclusions = m_designSettings->m_DrcExclusions;
  287. prjSettings->m_DrcExclusionComments = m_designSettings->m_DrcExclusionComments;
  288. }
  289. }
  290. }
  291. }
  292. std::set<wxString>::iterator FindByFirstNFields( std::set<wxString>& strSet, const wxString& searchStr, char delimiter,
  293. int n )
  294. {
  295. wxString searchPrefix = searchStr;
  296. // Extract first n fields from the search string
  297. int delimiterCount = 0;
  298. size_t pos = 0;
  299. while( pos < searchPrefix.length() && delimiterCount < n )
  300. {
  301. if( searchPrefix[pos] == delimiter )
  302. delimiterCount++;
  303. pos++;
  304. }
  305. if( delimiterCount == n )
  306. searchPrefix = searchPrefix.Left( pos - 1 ); // Exclude the nth delimiter
  307. for( auto it = strSet.begin(); it != strSet.end(); ++it )
  308. {
  309. if( it->StartsWith( searchPrefix + delimiter ) || *it == searchPrefix )
  310. return it;
  311. }
  312. return strSet.end();
  313. }
  314. std::vector<PCB_MARKER*> BOARD::ResolveDRCExclusions( bool aCreateMarkers )
  315. {
  316. std::set<wxString> exclusions = m_designSettings->m_DrcExclusions;
  317. std::map<wxString, wxString> comments = m_designSettings->m_DrcExclusionComments;
  318. m_designSettings->m_DrcExclusions.clear();
  319. m_designSettings->m_DrcExclusionComments.clear();
  320. for( PCB_MARKER* marker : GetBoard()->Markers() )
  321. {
  322. std::set<wxString>::iterator it;
  323. wxString serialized = marker->SerializeToString();
  324. wxString matchedExclusion;
  325. if( !serialized.Contains( "unconnected_items" ) )
  326. {
  327. it = exclusions.find( serialized );
  328. if( it != exclusions.end() )
  329. matchedExclusion = *it;
  330. }
  331. else
  332. {
  333. const int numberOfFieldsExcludingIds = 3;
  334. const char delimiter = '|';
  335. it = FindByFirstNFields( exclusions, serialized, delimiter, numberOfFieldsExcludingIds );
  336. if( it != exclusions.end() )
  337. matchedExclusion = *it;
  338. }
  339. if( it != exclusions.end() )
  340. {
  341. marker->SetExcluded( true, comments[matchedExclusion] );
  342. // Exclusion still valid; store back to BOARD_DESIGN_SETTINGS
  343. m_designSettings->m_DrcExclusions.insert( matchedExclusion );
  344. m_designSettings->m_DrcExclusionComments[matchedExclusion] = comments[matchedExclusion];
  345. exclusions.erase( it );
  346. }
  347. }
  348. std::vector<PCB_MARKER*> newMarkers;
  349. if( aCreateMarkers )
  350. {
  351. for( const wxString& serialized : exclusions )
  352. {
  353. PCB_MARKER* marker = PCB_MARKER::DeserializeFromString( serialized );
  354. if( !marker )
  355. continue;
  356. std::vector<KIID> ids = marker->GetRCItem()->GetIDs();
  357. int uuidCount = 0;
  358. for( const KIID& uuid : ids )
  359. {
  360. if( uuidCount < 1 || uuid != niluuid )
  361. {
  362. if( !ResolveItem( uuid, true ) )
  363. {
  364. delete marker;
  365. marker = nullptr;
  366. break;
  367. }
  368. }
  369. uuidCount++;
  370. }
  371. if( marker )
  372. {
  373. marker->SetExcluded( true, comments[ serialized ] );
  374. newMarkers.push_back( marker );
  375. // Exclusion still valid; store back to BOARD_DESIGN_SETTINGS
  376. m_designSettings->m_DrcExclusions.insert( serialized );
  377. m_designSettings->m_DrcExclusionComments[ serialized ] = comments[ serialized ];
  378. }
  379. }
  380. }
  381. return newMarkers;
  382. }
  383. void BOARD::GetContextualTextVars( wxArrayString* aVars ) const
  384. {
  385. auto add =
  386. [&]( const wxString& aVar )
  387. {
  388. if( !alg::contains( *aVars, aVar ) )
  389. aVars->push_back( aVar );
  390. };
  391. add( wxT( "LAYER" ) );
  392. add( wxT( "FILENAME" ) );
  393. add( wxT( "FILEPATH" ) );
  394. add( wxT( "PROJECTNAME" ) );
  395. add( wxT( "DRC_ERROR <message_text>" ) );
  396. add( wxT( "DRC_WARNING <message_text>" ) );
  397. GetTitleBlock().GetContextualTextVars( aVars );
  398. if( GetProject() )
  399. {
  400. for( std::pair<wxString, wxString> entry : GetProject()->GetTextVars() )
  401. add( entry.first );
  402. }
  403. }
  404. bool BOARD::ResolveTextVar( wxString* token, int aDepth ) const
  405. {
  406. if( token->Contains( ':' ) )
  407. {
  408. wxString remainder;
  409. wxString ref = token->BeforeFirst( ':', &remainder );
  410. BOARD_ITEM* refItem = ResolveItem( KIID( ref ), true );
  411. if( refItem && refItem->Type() == PCB_FOOTPRINT_T )
  412. {
  413. FOOTPRINT* refFP = static_cast<FOOTPRINT*>( refItem );
  414. if( refFP->ResolveTextVar( &remainder, aDepth + 1 ) )
  415. {
  416. *token = std::move( remainder );
  417. return true;
  418. }
  419. }
  420. }
  421. if( token->IsSameAs( wxT( "FILENAME" ) ) )
  422. {
  423. wxFileName fn( GetFileName() );
  424. *token = fn.GetFullName();
  425. return true;
  426. }
  427. else if( token->IsSameAs( wxT( "FILEPATH" ) ) )
  428. {
  429. wxFileName fn( GetFileName() );
  430. *token = fn.GetFullPath();
  431. return true;
  432. }
  433. else if( token->IsSameAs( wxT( "PROJECTNAME" ) ) && GetProject() )
  434. {
  435. *token = GetProject()->GetProjectName();
  436. return true;
  437. }
  438. wxString var = *token;
  439. if( m_properties.count( var ) )
  440. {
  441. *token = m_properties.at( var );
  442. return true;
  443. }
  444. else if( GetTitleBlock().TextVarResolver( token, m_project ) )
  445. {
  446. return true;
  447. }
  448. if( GetProject() && GetProject()->TextVarResolver( token ) )
  449. return true;
  450. return false;
  451. }
  452. VECTOR2I BOARD::GetPosition() const
  453. {
  454. return ZeroOffset;
  455. }
  456. void BOARD::SetPosition( const VECTOR2I& aPos )
  457. {
  458. wxLogWarning( wxT( "This should not be called on the BOARD object") );
  459. }
  460. void BOARD::Move( const VECTOR2I& aMoveVector ) // overload
  461. {
  462. INSPECTOR_FUNC inspector =
  463. [&] ( EDA_ITEM* item, void* testData )
  464. {
  465. if( item->IsBOARD_ITEM() )
  466. {
  467. BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
  468. // aMoveVector was snapshotted, don't need "data".
  469. // Only move the top level group
  470. if( !board_item->GetParentGroup() && !board_item->GetParentFootprint() )
  471. board_item->Move( aMoveVector );
  472. }
  473. return INSPECT_RESULT::CONTINUE;
  474. };
  475. Visit( inspector, nullptr, GENERAL_COLLECTOR::BoardLevelItems );
  476. }
  477. void BOARD::RunOnChildren( const std::function<void( BOARD_ITEM* )>& aFunction, RECURSE_MODE aMode ) const
  478. {
  479. try
  480. {
  481. for( PCB_TRACK* track : m_tracks )
  482. aFunction( track );
  483. for( ZONE* zone : m_zones )
  484. aFunction( zone );
  485. for( PCB_MARKER* marker : m_markers )
  486. aFunction( marker );
  487. for( PCB_GROUP* group : m_groups )
  488. aFunction( group );
  489. for( FOOTPRINT* footprint : m_footprints )
  490. {
  491. aFunction( footprint );
  492. if( aMode == RECURSE_MODE::RECURSE )
  493. footprint->RunOnChildren( aFunction, RECURSE_MODE::RECURSE );
  494. }
  495. for( BOARD_ITEM* drawing : m_drawings )
  496. {
  497. aFunction( drawing );
  498. if( aMode == RECURSE_MODE::RECURSE )
  499. drawing->RunOnChildren( aFunction, RECURSE_MODE::RECURSE );
  500. }
  501. }
  502. catch( std::bad_function_call& )
  503. {
  504. wxFAIL_MSG( wxT( "Error running BOARD::RunOnChildren" ) );
  505. }
  506. }
  507. TRACKS BOARD::TracksInNet( int aNetCode )
  508. {
  509. TRACKS ret;
  510. INSPECTOR_FUNC inspector = [aNetCode, &ret]( EDA_ITEM* item, void* testData )
  511. {
  512. PCB_TRACK* t = static_cast<PCB_TRACK*>( item );
  513. if( t->GetNetCode() == aNetCode )
  514. ret.push_back( t );
  515. return INSPECT_RESULT::CONTINUE;
  516. };
  517. // visit this BOARD's PCB_TRACKs and PCB_VIAs with above TRACK INSPECTOR which
  518. // appends all in aNetCode to ret.
  519. Visit( inspector, nullptr, GENERAL_COLLECTOR::Tracks );
  520. return ret;
  521. }
  522. bool BOARD::SetLayerDescr( PCB_LAYER_ID aIndex, const LAYER& aLayer )
  523. {
  524. m_layers[ aIndex ] = aLayer;
  525. recalcOpposites();
  526. return true;
  527. }
  528. PCB_LAYER_ID BOARD::GetLayerID( const wxString& aLayerName ) const
  529. {
  530. // Check the BOARD physical layer names.
  531. for( auto& [ layer_id, layer ] : m_layers )
  532. {
  533. if( layer.m_name == aLayerName || layer.m_userName == aLayerName )
  534. return ToLAYER_ID( layer_id );
  535. }
  536. // Otherwise fall back to the system standard layer names for virtual layers.
  537. for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
  538. {
  539. if( GetStandardLayerName( ToLAYER_ID( layer ) ) == aLayerName )
  540. return ToLAYER_ID( layer );
  541. }
  542. return UNDEFINED_LAYER;
  543. }
  544. const wxString BOARD::GetLayerName( PCB_LAYER_ID aLayer ) const
  545. {
  546. // All layer names are stored in the BOARD.
  547. if( IsLayerEnabled( aLayer ) )
  548. {
  549. auto it = m_layers.find( aLayer );
  550. // Standard names were set in BOARD::BOARD() but they may be over-ridden by
  551. // BOARD::SetLayerName(). For copper layers, return the user defined layer name,
  552. // if it was set. Otherwise return the Standard English layer name.
  553. if( it != m_layers.end() && !it->second.m_userName.IsEmpty() )
  554. return it->second.m_userName;
  555. }
  556. return GetStandardLayerName( aLayer );
  557. }
  558. bool BOARD::SetLayerName( PCB_LAYER_ID aLayer, const wxString& aLayerName )
  559. {
  560. if( !aLayerName.IsEmpty() )
  561. {
  562. // no quote chars in the name allowed
  563. if( aLayerName.Find( wxChar( '"' ) ) != wxNOT_FOUND )
  564. return false;
  565. if( IsLayerEnabled( aLayer ) )
  566. {
  567. m_layers[aLayer].m_userName = aLayerName;
  568. recalcOpposites();
  569. return true;
  570. }
  571. }
  572. return false;
  573. }
  574. bool BOARD::IsFrontLayer( PCB_LAYER_ID aLayer ) const
  575. {
  576. return ::IsFrontLayer( aLayer ) || GetLayerType( aLayer ) == LT_FRONT;
  577. }
  578. bool BOARD::IsBackLayer( PCB_LAYER_ID aLayer ) const
  579. {
  580. return ::IsBackLayer( aLayer ) || GetLayerType( aLayer ) == LT_BACK;
  581. }
  582. LAYER_T BOARD::GetLayerType( PCB_LAYER_ID aLayer ) const
  583. {
  584. if( IsLayerEnabled( aLayer ) )
  585. {
  586. auto it = m_layers.find( aLayer );
  587. if( it != m_layers.end() )
  588. return it->second.m_type;
  589. }
  590. if( aLayer >= User_1 && !IsCopperLayer( aLayer ) )
  591. return LT_AUX;
  592. else if( IsCopperLayer( aLayer ) )
  593. return LT_SIGNAL;
  594. else
  595. return LT_UNDEFINED;
  596. }
  597. bool BOARD::SetLayerType( PCB_LAYER_ID aLayer, LAYER_T aLayerType )
  598. {
  599. if( IsLayerEnabled( aLayer ) )
  600. {
  601. m_layers[aLayer].m_type = aLayerType;
  602. recalcOpposites();
  603. return true;
  604. }
  605. return false;
  606. }
  607. const char* LAYER::ShowType( LAYER_T aType )
  608. {
  609. switch( aType )
  610. {
  611. default:
  612. case LT_SIGNAL: return "signal";
  613. case LT_POWER: return "power";
  614. case LT_MIXED: return "mixed";
  615. case LT_JUMPER: return "jumper";
  616. case LT_AUX: return "auxiliary";
  617. case LT_FRONT: return "front";
  618. case LT_BACK: return "back";
  619. }
  620. }
  621. LAYER_T LAYER::ParseType( const char* aType )
  622. {
  623. if( strcmp( aType, "signal" ) == 0 ) return LT_SIGNAL;
  624. else if( strcmp( aType, "power" ) == 0 ) return LT_POWER;
  625. else if( strcmp( aType, "mixed" ) == 0 ) return LT_MIXED;
  626. else if( strcmp( aType, "jumper" ) == 0 ) return LT_JUMPER;
  627. else if( strcmp( aType, "auxiliary" ) == 0 ) return LT_AUX;
  628. else if( strcmp( aType, "front" ) == 0 ) return LT_FRONT;
  629. else if( strcmp( aType, "back" ) == 0 ) return LT_BACK;
  630. else return LT_UNDEFINED;
  631. }
  632. void BOARD::recalcOpposites()
  633. {
  634. for( int layer = F_Cu; layer < PCB_LAYER_ID_COUNT; ++layer )
  635. m_layers[layer].m_opposite = ::FlipLayer( ToLAYER_ID( layer ), GetCopperLayerCount() );
  636. // Match up similary-named front/back user layers
  637. for( int layer = User_1; layer <= PCB_LAYER_ID_COUNT; layer += 2 )
  638. {
  639. if( m_layers[layer].m_opposite != layer ) // already paired
  640. continue;
  641. if( m_layers[layer].m_type != LT_FRONT && m_layers[layer].m_type != LT_BACK )
  642. continue;
  643. wxString principalName = m_layers[layer].m_userName.AfterFirst( '.' );
  644. for( int ii = layer + 2; ii <= PCB_LAYER_ID_COUNT; ii += 2 )
  645. {
  646. if( m_layers[ii].m_opposite != ii ) // already paired
  647. continue;
  648. if( m_layers[ii].m_type != LT_FRONT && m_layers[ii].m_type != LT_BACK )
  649. continue;
  650. if( m_layers[layer].m_type == m_layers[ii].m_type )
  651. continue;
  652. wxString candidate = m_layers[ii].m_userName.AfterFirst( '.' );
  653. if( !candidate.IsEmpty() && candidate == principalName )
  654. {
  655. m_layers[layer].m_opposite = ii;
  656. m_layers[ii].m_opposite = layer;
  657. break;
  658. }
  659. }
  660. }
  661. // Match up non-custom-named consecutive front/back user layer pairs
  662. for( int layer = User_1; layer < PCB_LAYER_ID_COUNT - 2; layer += 2 )
  663. {
  664. int next = layer + 2;
  665. // ignore already-matched layers
  666. if( m_layers[layer].m_opposite != layer || m_layers[next].m_opposite != next )
  667. continue;
  668. // ignore layer pairs that aren't consecutive front/back
  669. if( m_layers[layer].m_type != LT_FRONT || m_layers[next].m_type != LT_BACK )
  670. continue;
  671. if( m_layers[layer].m_userName != m_layers[layer].m_name
  672. && m_layers[next].m_userName != m_layers[next].m_name )
  673. {
  674. m_layers[layer].m_opposite = next;
  675. m_layers[next].m_opposite = layer;
  676. }
  677. }
  678. }
  679. PCB_LAYER_ID BOARD::FlipLayer( PCB_LAYER_ID aLayer ) const
  680. {
  681. auto it = m_layers.find( aLayer );
  682. return it == m_layers.end() ? aLayer : ToLAYER_ID( it->second.m_opposite );
  683. }
  684. int BOARD::GetCopperLayerCount() const
  685. {
  686. return GetDesignSettings().GetCopperLayerCount();
  687. }
  688. void BOARD::SetCopperLayerCount( int aCount )
  689. {
  690. GetDesignSettings().SetCopperLayerCount( aCount );
  691. }
  692. int BOARD::GetUserDefinedLayerCount() const
  693. {
  694. return GetDesignSettings().GetUserDefinedLayerCount();
  695. }
  696. void BOARD::SetUserDefinedLayerCount( int aCount )
  697. {
  698. return GetDesignSettings().SetUserDefinedLayerCount( aCount );
  699. }
  700. PCB_LAYER_ID BOARD::GetCopperLayerStackMaxId() const
  701. {
  702. int imax = GetCopperLayerCount();
  703. // layers IDs are F_Cu, B_Cu, and even IDs values (imax values)
  704. if( imax <= 2 ) // at least 2 layers are expected
  705. return B_Cu;
  706. // For a 4 layer, last ID is In2_Cu = 6 (IDs are 0, 2, 4, 6)
  707. return static_cast<PCB_LAYER_ID>( (imax-1) * 2 );
  708. }
  709. int BOARD::LayerDepth( PCB_LAYER_ID aStartLayer, PCB_LAYER_ID aEndLayer ) const
  710. {
  711. if( aStartLayer > aEndLayer )
  712. std::swap( aStartLayer, aEndLayer );
  713. if( aEndLayer == B_Cu )
  714. aEndLayer = ToLAYER_ID( F_Cu + GetCopperLayerCount() - 1 );
  715. return aEndLayer - aStartLayer;
  716. }
  717. const LSET& BOARD::GetEnabledLayers() const
  718. {
  719. return GetDesignSettings().GetEnabledLayers();
  720. }
  721. bool BOARD::IsLayerVisible( PCB_LAYER_ID aLayer ) const
  722. {
  723. // If there is no project, assume layer is visible always
  724. return GetDesignSettings().IsLayerEnabled( aLayer )
  725. && ( !m_project || m_project->GetLocalSettings().m_VisibleLayers[aLayer] );
  726. }
  727. const LSET& BOARD::GetVisibleLayers() const
  728. {
  729. return m_project ? m_project->GetLocalSettings().m_VisibleLayers : LSET::AllLayersMask();
  730. }
  731. void BOARD::SetEnabledLayers( const LSET& aLayerSet )
  732. {
  733. GetDesignSettings().SetEnabledLayers( aLayerSet );
  734. }
  735. bool BOARD::IsLayerEnabled( PCB_LAYER_ID aLayer ) const
  736. {
  737. return GetDesignSettings().IsLayerEnabled( aLayer );
  738. }
  739. void BOARD::SetVisibleLayers( const LSET& aLayerSet )
  740. {
  741. if( m_project )
  742. m_project->GetLocalSettings().m_VisibleLayers = aLayerSet;
  743. }
  744. void BOARD::SetVisibleElements( const GAL_SET& aSet )
  745. {
  746. // Call SetElementVisibility for each item
  747. // to ensure specific calculations that can be needed by some items,
  748. // just changing the visibility flags could be not sufficient.
  749. for( size_t i = 0; i < aSet.size(); i++ )
  750. SetElementVisibility( GAL_LAYER_ID_START + static_cast<int>( i ), aSet[i] );
  751. }
  752. void BOARD::SetVisibleAlls()
  753. {
  754. SetVisibleLayers( LSET().set() );
  755. // Call SetElementVisibility for each item,
  756. // to ensure specific calculations that can be needed by some items
  757. for( GAL_LAYER_ID ii = GAL_LAYER_ID_START; ii < GAL_LAYER_ID_BITMASK_END; ++ii )
  758. SetElementVisibility( ii, true );
  759. }
  760. GAL_SET BOARD::GetVisibleElements() const
  761. {
  762. return m_project ? m_project->GetLocalSettings().m_VisibleItems : GAL_SET::DefaultVisible();
  763. }
  764. bool BOARD::IsElementVisible( GAL_LAYER_ID aLayer ) const
  765. {
  766. return !m_project || m_project->GetLocalSettings().m_VisibleItems[aLayer - GAL_LAYER_ID_START];
  767. }
  768. void BOARD::SetElementVisibility( GAL_LAYER_ID aLayer, bool isEnabled )
  769. {
  770. if( m_project )
  771. m_project->GetLocalSettings().m_VisibleItems.set( aLayer - GAL_LAYER_ID_START, isEnabled );
  772. switch( aLayer )
  773. {
  774. case LAYER_RATSNEST:
  775. {
  776. // because we have a tool to show/hide ratsnest relative to a pad or a footprint
  777. // so the hide/show option is a per item selection
  778. for( PCB_TRACK* track : Tracks() )
  779. track->SetLocalRatsnestVisible( isEnabled );
  780. for( FOOTPRINT* footprint : Footprints() )
  781. {
  782. for( PAD* pad : footprint->Pads() )
  783. pad->SetLocalRatsnestVisible( isEnabled );
  784. }
  785. for( ZONE* zone : Zones() )
  786. zone->SetLocalRatsnestVisible( isEnabled );
  787. break;
  788. }
  789. default:
  790. ;
  791. }
  792. }
  793. bool BOARD::IsFootprintLayerVisible( PCB_LAYER_ID aLayer ) const
  794. {
  795. switch( aLayer )
  796. {
  797. case F_Cu: return IsElementVisible( LAYER_FOOTPRINTS_FR );
  798. case B_Cu: return IsElementVisible( LAYER_FOOTPRINTS_BK );
  799. default: wxFAIL_MSG( wxT( "BOARD::IsModuleLayerVisible(): bad layer" ) ); return true;
  800. }
  801. }
  802. BOARD_DESIGN_SETTINGS& BOARD::GetDesignSettings() const
  803. {
  804. return *m_designSettings;
  805. }
  806. void BOARD::SetDesignSettings( const BOARD_DESIGN_SETTINGS& aSettings )
  807. {
  808. *m_designSettings = aSettings;
  809. }
  810. int BOARD::GetMaxClearanceValue() const
  811. {
  812. if( !m_maxClearanceValue.has_value() )
  813. {
  814. std::unique_lock<std::shared_mutex> writeLock( m_CachesMutex );
  815. int worstClearance = m_designSettings->GetBiggestClearanceValue();
  816. for( ZONE* zone : m_zones )
  817. worstClearance = std::max( worstClearance, zone->GetLocalClearance().value() );
  818. for( FOOTPRINT* footprint : m_footprints )
  819. {
  820. for( PAD* pad : footprint->Pads() )
  821. {
  822. std::optional<int> override = pad->GetClearanceOverrides( nullptr );
  823. if( override.has_value() )
  824. worstClearance = std::max( worstClearance, override.value() );
  825. }
  826. for( ZONE* zone : footprint->Zones() )
  827. worstClearance = std::max( worstClearance, zone->GetLocalClearance().value() );
  828. }
  829. m_maxClearanceValue = worstClearance;
  830. }
  831. return m_maxClearanceValue.value_or( 0 );
  832. };
  833. void BOARD::CacheTriangulation( PROGRESS_REPORTER* aReporter, const std::vector<ZONE*>& aZones )
  834. {
  835. std::vector<ZONE*> zones = aZones;
  836. if( zones.empty() )
  837. zones = m_zones;
  838. if( zones.empty() )
  839. return;
  840. if( aReporter )
  841. aReporter->Report( _( "Tessellating copper zones..." ) );
  842. thread_pool& tp = GetKiCadThreadPool();
  843. std::vector<std::future<size_t>> returns;
  844. returns.reserve( zones.size() );
  845. auto cache_zones =
  846. [aReporter]( ZONE* aZone ) -> size_t
  847. {
  848. if( aReporter && aReporter->IsCancelled() )
  849. return 0;
  850. aZone->CacheTriangulation();
  851. if( aReporter )
  852. aReporter->AdvanceProgress();
  853. return 1;
  854. };
  855. for( ZONE* zone : zones )
  856. returns.emplace_back( tp.submit( cache_zones, zone ) );
  857. // Finalize the triangulation threads
  858. for( const std::future<size_t>& ret : returns )
  859. {
  860. std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  861. while( status != std::future_status::ready )
  862. {
  863. if( aReporter )
  864. aReporter->KeepRefreshing();
  865. status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  866. }
  867. }
  868. }
  869. void BOARD::FixupEmbeddedData()
  870. {
  871. for( FOOTPRINT* footprint : m_footprints )
  872. {
  873. for( auto& [filename, embeddedFile] : footprint->EmbeddedFileMap() )
  874. {
  875. EMBEDDED_FILES::EMBEDDED_FILE* file = GetEmbeddedFile( filename );
  876. if( file )
  877. {
  878. embeddedFile->compressedEncodedData = file->compressedEncodedData;
  879. embeddedFile->decompressedData = file->decompressedData;
  880. embeddedFile->data_hash = file->data_hash;
  881. embeddedFile->is_valid = file->is_valid;
  882. }
  883. }
  884. }
  885. }
  886. void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode, bool aSkipConnectivity )
  887. {
  888. if( aBoardItem == nullptr )
  889. {
  890. wxFAIL_MSG( wxT( "BOARD::Add() param error: aBoardItem nullptr" ) );
  891. return;
  892. }
  893. m_itemByIdCache.insert( { aBoardItem->m_Uuid, aBoardItem } );
  894. switch( aBoardItem->Type() )
  895. {
  896. case PCB_NETINFO_T:
  897. m_NetInfo.AppendNet( (NETINFO_ITEM*) aBoardItem );
  898. break;
  899. // this one uses a vector
  900. case PCB_MARKER_T:
  901. m_markers.push_back( (PCB_MARKER*) aBoardItem );
  902. break;
  903. // this one uses a vector
  904. case PCB_GROUP_T:
  905. m_groups.push_back( (PCB_GROUP*) aBoardItem );
  906. break;
  907. // this one uses a vector
  908. case PCB_GENERATOR_T:
  909. m_generators.push_back( (PCB_GENERATOR*) aBoardItem );
  910. break;
  911. // this one uses a vector
  912. case PCB_ZONE_T:
  913. m_zones.push_back( (ZONE*) aBoardItem );
  914. break;
  915. case PCB_VIA_T:
  916. if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
  917. m_tracks.push_back( static_cast<PCB_VIA*>( aBoardItem ) );
  918. else
  919. m_tracks.push_front( static_cast<PCB_VIA*>( aBoardItem ) );
  920. break;
  921. case PCB_TRACE_T:
  922. case PCB_ARC_T:
  923. if( !IsCopperLayer( aBoardItem->GetLayer() ) )
  924. {
  925. // The only current known source of these is SWIG (KICAD-BY7, et al).
  926. // N.B. This inserts a small memory leak as we lose the track/via/arc.
  927. wxFAIL_MSG( wxString::Format( "BOARD::Add() Cannot place Track on non-copper layer: %d = %s",
  928. static_cast<int>( aBoardItem->GetLayer() ),
  929. GetLayerName( aBoardItem->GetLayer() ) ) );
  930. return;
  931. }
  932. if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
  933. m_tracks.push_back( static_cast<PCB_TRACK*>( aBoardItem ) );
  934. else
  935. m_tracks.push_front( static_cast<PCB_TRACK*>( aBoardItem ) );
  936. break;
  937. case PCB_FOOTPRINT_T:
  938. {
  939. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aBoardItem );
  940. if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
  941. m_footprints.push_back( footprint );
  942. else
  943. m_footprints.push_front( footprint );
  944. footprint->RunOnChildren( [&]( BOARD_ITEM* aChild )
  945. {
  946. m_itemByIdCache.insert( { aChild->m_Uuid, aChild } );
  947. },
  948. RECURSE_MODE::NO_RECURSE );
  949. break;
  950. }
  951. case PCB_DIM_ALIGNED_T:
  952. case PCB_DIM_CENTER_T:
  953. case PCB_DIM_RADIAL_T:
  954. case PCB_DIM_ORTHOGONAL_T:
  955. case PCB_DIM_LEADER_T:
  956. case PCB_SHAPE_T:
  957. case PCB_REFERENCE_IMAGE_T:
  958. case PCB_FIELD_T:
  959. case PCB_TEXT_T:
  960. case PCB_TEXTBOX_T:
  961. case PCB_TABLE_T:
  962. case PCB_TARGET_T:
  963. {
  964. if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
  965. m_drawings.push_back( aBoardItem );
  966. else
  967. m_drawings.push_front( aBoardItem );
  968. if( aBoardItem->Type() == PCB_TABLE_T )
  969. {
  970. PCB_TABLE* table = static_cast<PCB_TABLE*>( aBoardItem );
  971. table->RunOnChildren( [&]( BOARD_ITEM* aChild )
  972. {
  973. m_itemByIdCache.insert( { aChild->m_Uuid, aChild } );
  974. },
  975. RECURSE_MODE::NO_RECURSE );
  976. }
  977. break;
  978. }
  979. case PCB_TABLECELL_T:
  980. // Handled by parent table
  981. break;
  982. default:
  983. wxFAIL_MSG( wxString::Format( wxT( "BOARD::Add() item type %s not handled" ),
  984. aBoardItem->GetClass() ) );
  985. return;
  986. }
  987. aBoardItem->SetParent( this );
  988. aBoardItem->ClearEditFlags();
  989. if( !aSkipConnectivity )
  990. m_connectivity->Add( aBoardItem );
  991. if( aMode != ADD_MODE::BULK_INSERT && aMode != ADD_MODE::BULK_APPEND )
  992. InvokeListeners( &BOARD_LISTENER::OnBoardItemAdded, *this, aBoardItem );
  993. }
  994. void BOARD::FinalizeBulkAdd( std::vector<BOARD_ITEM*>& aNewItems )
  995. {
  996. InvokeListeners( &BOARD_LISTENER::OnBoardItemsAdded, *this, aNewItems );
  997. }
  998. void BOARD::FinalizeBulkRemove( std::vector<BOARD_ITEM*>& aRemovedItems )
  999. {
  1000. InvokeListeners( &BOARD_LISTENER::OnBoardItemsRemoved, *this, aRemovedItems );
  1001. }
  1002. void BOARD::BulkRemoveStaleTeardrops( BOARD_COMMIT& aCommit )
  1003. {
  1004. for( int ii = (int) m_zones.size() - 1; ii >= 0; --ii )
  1005. {
  1006. ZONE* zone = m_zones[ii];
  1007. if( zone->IsTeardropArea() && zone->HasFlag( STRUCT_DELETED ) )
  1008. {
  1009. m_itemByIdCache.erase( zone->m_Uuid );
  1010. m_zones.erase( m_zones.begin() + ii );
  1011. m_connectivity->Remove( zone );
  1012. aCommit.Removed( zone );
  1013. }
  1014. }
  1015. }
  1016. void BOARD::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aRemoveMode )
  1017. {
  1018. // find these calls and fix them! Don't send me no stinking' nullptr.
  1019. wxASSERT( aBoardItem );
  1020. m_itemByIdCache.erase( aBoardItem->m_Uuid );
  1021. switch( aBoardItem->Type() )
  1022. {
  1023. case PCB_NETINFO_T:
  1024. {
  1025. NETINFO_ITEM* netItem = static_cast<NETINFO_ITEM*>( aBoardItem );
  1026. NETINFO_ITEM* unconnected = m_NetInfo.GetNetItem( NETINFO_LIST::UNCONNECTED );
  1027. for( BOARD_CONNECTED_ITEM* boardItem : AllConnectedItems() )
  1028. {
  1029. if( boardItem->GetNet() == netItem )
  1030. boardItem->SetNet( unconnected );
  1031. }
  1032. m_NetInfo.RemoveNet( netItem );
  1033. break;
  1034. }
  1035. case PCB_MARKER_T:
  1036. alg::delete_matching( m_markers, aBoardItem );
  1037. break;
  1038. case PCB_GROUP_T:
  1039. alg::delete_matching( m_groups, aBoardItem );
  1040. break;
  1041. case PCB_ZONE_T:
  1042. alg::delete_matching( m_zones, aBoardItem );
  1043. break;
  1044. case PCB_GENERATOR_T:
  1045. alg::delete_matching( m_generators, aBoardItem );
  1046. break;
  1047. case PCB_FOOTPRINT_T:
  1048. {
  1049. alg::delete_matching( m_footprints, aBoardItem );
  1050. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aBoardItem );
  1051. footprint->RunOnChildren( [&]( BOARD_ITEM* aChild )
  1052. {
  1053. m_itemByIdCache.erase( aChild->m_Uuid );
  1054. },
  1055. RECURSE_MODE::NO_RECURSE );
  1056. break;
  1057. }
  1058. case PCB_TRACE_T:
  1059. case PCB_ARC_T:
  1060. case PCB_VIA_T:
  1061. alg::delete_matching( m_tracks, aBoardItem );
  1062. break;
  1063. case PCB_DIM_ALIGNED_T:
  1064. case PCB_DIM_CENTER_T:
  1065. case PCB_DIM_RADIAL_T:
  1066. case PCB_DIM_ORTHOGONAL_T:
  1067. case PCB_DIM_LEADER_T:
  1068. case PCB_SHAPE_T:
  1069. case PCB_REFERENCE_IMAGE_T:
  1070. case PCB_FIELD_T:
  1071. case PCB_TEXT_T:
  1072. case PCB_TEXTBOX_T:
  1073. case PCB_TABLE_T:
  1074. case PCB_TARGET_T:
  1075. {
  1076. alg::delete_matching( m_drawings, aBoardItem );
  1077. if( aBoardItem->Type() == PCB_TABLE_T )
  1078. {
  1079. PCB_TABLE* table = static_cast<PCB_TABLE*>( aBoardItem );
  1080. table->RunOnChildren( [&]( BOARD_ITEM* aChild )
  1081. {
  1082. m_itemByIdCache.erase( aChild->m_Uuid );
  1083. },
  1084. RECURSE_MODE::NO_RECURSE );
  1085. }
  1086. break;
  1087. }
  1088. case PCB_TABLECELL_T:
  1089. // Handled by parent table
  1090. break;
  1091. // other types may use linked list
  1092. default:
  1093. wxFAIL_MSG( wxString::Format( wxT( "BOARD::Remove() item type %s not handled" ),
  1094. aBoardItem->GetClass() ) );
  1095. }
  1096. aBoardItem->SetFlags( STRUCT_DELETED );
  1097. m_connectivity->Remove( aBoardItem );
  1098. if( aRemoveMode != REMOVE_MODE::BULK )
  1099. InvokeListeners( &BOARD_LISTENER::OnBoardItemRemoved, *this, aBoardItem );
  1100. }
  1101. void BOARD::RemoveAll( std::initializer_list<KICAD_T> aTypes )
  1102. {
  1103. std::vector<BOARD_ITEM*> removed;
  1104. for( const KICAD_T& type : aTypes )
  1105. {
  1106. switch( type )
  1107. {
  1108. case PCB_NETINFO_T:
  1109. for( NETINFO_ITEM* item : m_NetInfo )
  1110. removed.emplace_back( item );
  1111. m_NetInfo.clear();
  1112. break;
  1113. case PCB_MARKER_T:
  1114. std::copy( m_markers.begin(), m_markers.end(), std::back_inserter( removed ) );
  1115. m_markers.clear();
  1116. break;
  1117. case PCB_GROUP_T:
  1118. std::copy( m_groups.begin(), m_groups.end(), std::back_inserter( removed ) );
  1119. m_groups.clear();
  1120. break;
  1121. case PCB_ZONE_T:
  1122. std::copy( m_zones.begin(), m_zones.end(), std::back_inserter( removed ) );
  1123. m_zones.clear();
  1124. break;
  1125. case PCB_GENERATOR_T:
  1126. std::copy( m_generators.begin(), m_generators.end(), std::back_inserter( removed ) );
  1127. m_generators.clear();
  1128. break;
  1129. case PCB_FOOTPRINT_T:
  1130. std::copy( m_footprints.begin(), m_footprints.end(), std::back_inserter( removed ) );
  1131. m_footprints.clear();
  1132. break;
  1133. case PCB_TRACE_T:
  1134. std::copy( m_tracks.begin(), m_tracks.end(), std::back_inserter( removed ) );
  1135. m_tracks.clear();
  1136. break;
  1137. case PCB_ARC_T:
  1138. case PCB_VIA_T:
  1139. wxFAIL_MSG( wxT( "Use PCB_TRACE_T to remove all tracks, arcs, and vias" ) );
  1140. break;
  1141. case PCB_SHAPE_T:
  1142. std::copy( m_drawings.begin(), m_drawings.end(), std::back_inserter( removed ) );
  1143. m_drawings.clear();
  1144. break;
  1145. case PCB_DIM_ALIGNED_T:
  1146. case PCB_DIM_CENTER_T:
  1147. case PCB_DIM_RADIAL_T:
  1148. case PCB_DIM_ORTHOGONAL_T:
  1149. case PCB_DIM_LEADER_T:
  1150. case PCB_REFERENCE_IMAGE_T:
  1151. case PCB_FIELD_T:
  1152. case PCB_TEXT_T:
  1153. case PCB_TEXTBOX_T:
  1154. case PCB_TABLE_T:
  1155. case PCB_TARGET_T:
  1156. wxFAIL_MSG( wxT( "Use PCB_SHAPE_T to remove all graphics and text" ) );
  1157. break;
  1158. default:
  1159. wxFAIL_MSG( wxT( "BOARD::RemoveAll() needs more ::Type() support" ) );
  1160. }
  1161. }
  1162. IncrementTimeStamp();
  1163. FinalizeBulkRemove( removed );
  1164. }
  1165. wxString BOARD::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  1166. {
  1167. return wxString::Format( _( "PCB" ) );
  1168. }
  1169. void BOARD::UpdateUserUnits( BOARD_ITEM* aItem, KIGFX::VIEW* aView )
  1170. {
  1171. INSPECTOR_FUNC inspector =
  1172. [&]( EDA_ITEM* descendant, void* aTestData )
  1173. {
  1174. PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( descendant );
  1175. if( dimension->GetUnitsMode() == DIM_UNITS_MODE::AUTOMATIC )
  1176. {
  1177. dimension->UpdateUnits();
  1178. if( aView )
  1179. aView->Update( dimension );
  1180. }
  1181. return INSPECT_RESULT::CONTINUE;
  1182. };
  1183. aItem->Visit( inspector, nullptr, { PCB_DIM_ALIGNED_T,
  1184. PCB_DIM_LEADER_T,
  1185. PCB_DIM_ORTHOGONAL_T,
  1186. PCB_DIM_CENTER_T,
  1187. PCB_DIM_RADIAL_T } );
  1188. }
  1189. void BOARD::DeleteMARKERs()
  1190. {
  1191. for( PCB_MARKER* marker : m_markers )
  1192. delete marker;
  1193. m_markers.clear();
  1194. IncrementTimeStamp();
  1195. }
  1196. void BOARD::DeleteMARKERs( bool aWarningsAndErrors, bool aExclusions )
  1197. {
  1198. // Deleting lots of items from a vector can be very slow. Copy remaining items instead.
  1199. std::vector<PCB_MARKER*> remaining;
  1200. for( PCB_MARKER* marker : m_markers )
  1201. {
  1202. if( ( marker->GetSeverity() == RPT_SEVERITY_EXCLUSION && aExclusions )
  1203. || ( marker->GetSeverity() != RPT_SEVERITY_EXCLUSION && aWarningsAndErrors ) )
  1204. {
  1205. delete marker;
  1206. }
  1207. else
  1208. {
  1209. remaining.push_back( marker );
  1210. }
  1211. }
  1212. m_markers = std::move( remaining );
  1213. IncrementTimeStamp();
  1214. }
  1215. void BOARD::DeleteAllFootprints()
  1216. {
  1217. for( FOOTPRINT* footprint : m_footprints )
  1218. delete footprint;
  1219. m_footprints.clear();
  1220. IncrementTimeStamp();
  1221. }
  1222. void BOARD::DetachAllFootprints()
  1223. {
  1224. for( FOOTPRINT* footprint : m_footprints )
  1225. footprint->SetParent( nullptr );
  1226. m_footprints.clear();
  1227. IncrementTimeStamp();
  1228. }
  1229. BOARD_ITEM* BOARD::ResolveItem( const KIID& aID, bool aAllowNullptrReturn ) const
  1230. {
  1231. if( aID == niluuid )
  1232. return nullptr;
  1233. if( m_itemByIdCache.count( aID ) )
  1234. return m_itemByIdCache.at( aID );
  1235. // Main clients include highlighting, group undo/redo and DRC items. Since
  1236. // everything but group undo/redo will be spread over all object types, we
  1237. // might as well prioritize group undo/redo and search them first.
  1238. for( PCB_GROUP* group : m_groups )
  1239. {
  1240. if( group->m_Uuid == aID )
  1241. return group;
  1242. }
  1243. for( PCB_GENERATOR* generator : m_generators )
  1244. {
  1245. if( generator->m_Uuid == aID )
  1246. return generator;
  1247. }
  1248. for( PCB_TRACK* track : Tracks() )
  1249. {
  1250. if( track->m_Uuid == aID )
  1251. return track;
  1252. }
  1253. for( FOOTPRINT* footprint : Footprints() )
  1254. {
  1255. if( footprint->m_Uuid == aID )
  1256. return footprint;
  1257. for( PAD* pad : footprint->Pads() )
  1258. {
  1259. if( pad->m_Uuid == aID )
  1260. return pad;
  1261. }
  1262. for( PCB_FIELD* field : footprint->GetFields() )
  1263. {
  1264. if( field && field->m_Uuid == aID )
  1265. return field;
  1266. }
  1267. for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
  1268. {
  1269. if( drawing->m_Uuid == aID )
  1270. return drawing;
  1271. }
  1272. for( BOARD_ITEM* zone : footprint->Zones() )
  1273. {
  1274. if( zone->m_Uuid == aID )
  1275. return zone;
  1276. }
  1277. for( PCB_GROUP* group : footprint->Groups() )
  1278. {
  1279. if( group->m_Uuid == aID )
  1280. return group;
  1281. }
  1282. }
  1283. for( ZONE* zone : Zones() )
  1284. {
  1285. if( zone->m_Uuid == aID )
  1286. return zone;
  1287. }
  1288. for( BOARD_ITEM* drawing : Drawings() )
  1289. {
  1290. if( drawing->Type() == PCB_TABLE_T )
  1291. {
  1292. for( PCB_TABLECELL* cell : static_cast<PCB_TABLE*>( drawing )->GetCells() )
  1293. {
  1294. if( cell->m_Uuid == aID )
  1295. return drawing;
  1296. }
  1297. }
  1298. if( drawing->m_Uuid == aID )
  1299. return drawing;
  1300. }
  1301. for( PCB_MARKER* marker : m_markers )
  1302. {
  1303. if( marker->m_Uuid == aID )
  1304. return marker;
  1305. }
  1306. for( NETINFO_ITEM* netInfo : m_NetInfo )
  1307. {
  1308. if( netInfo->m_Uuid == aID )
  1309. return netInfo;
  1310. }
  1311. if( m_Uuid == aID )
  1312. return const_cast<BOARD*>( this );
  1313. // Not found; weak reference has been deleted.
  1314. if( aAllowNullptrReturn )
  1315. return nullptr;
  1316. return DELETED_BOARD_ITEM::GetInstance();
  1317. }
  1318. void BOARD::FillItemMap( std::map<KIID, EDA_ITEM*>& aMap )
  1319. {
  1320. // the board itself
  1321. aMap[ m_Uuid ] = this;
  1322. for( PCB_TRACK* track : Tracks() )
  1323. aMap[ track->m_Uuid ] = track;
  1324. for( FOOTPRINT* footprint : Footprints() )
  1325. {
  1326. aMap[ footprint->m_Uuid ] = footprint;
  1327. for( PAD* pad : footprint->Pads() )
  1328. aMap[ pad->m_Uuid ] = pad;
  1329. aMap[ footprint->Reference().m_Uuid ] = &footprint->Reference();
  1330. aMap[ footprint->Value().m_Uuid ] = &footprint->Value();
  1331. for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
  1332. aMap[ drawing->m_Uuid ] = drawing;
  1333. }
  1334. for( ZONE* zone : Zones() )
  1335. aMap[ zone->m_Uuid ] = zone;
  1336. for( BOARD_ITEM* drawing : Drawings() )
  1337. aMap[ drawing->m_Uuid ] = drawing;
  1338. for( PCB_MARKER* marker : m_markers )
  1339. aMap[ marker->m_Uuid ] = marker;
  1340. for( PCB_GROUP* group : m_groups )
  1341. aMap[ group->m_Uuid ] = group;
  1342. for( PCB_GENERATOR* generator : m_generators )
  1343. aMap[ generator->m_Uuid ] = generator;
  1344. }
  1345. wxString BOARD::ConvertCrossReferencesToKIIDs( const wxString& aSource ) const
  1346. {
  1347. wxString newbuf;
  1348. size_t sourceLen = aSource.length();
  1349. for( size_t i = 0; i < sourceLen; ++i )
  1350. {
  1351. if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
  1352. {
  1353. wxString token;
  1354. bool isCrossRef = false;
  1355. for( i = i + 2; i < sourceLen; ++i )
  1356. {
  1357. if( aSource[i] == '}' )
  1358. break;
  1359. if( aSource[i] == ':' )
  1360. isCrossRef = true;
  1361. token.append( aSource[i] );
  1362. }
  1363. if( isCrossRef )
  1364. {
  1365. wxString remainder;
  1366. wxString ref = token.BeforeFirst( ':', &remainder );
  1367. for( const FOOTPRINT* footprint : Footprints() )
  1368. {
  1369. if( footprint->GetReference().CmpNoCase( ref ) == 0 )
  1370. {
  1371. wxString test( remainder );
  1372. if( footprint->ResolveTextVar( &test ) )
  1373. token = footprint->m_Uuid.AsString() + wxT( ":" ) + remainder;
  1374. break;
  1375. }
  1376. }
  1377. }
  1378. newbuf.append( wxT( "${" ) + token + wxT( "}" ) );
  1379. }
  1380. else
  1381. {
  1382. newbuf.append( aSource[i] );
  1383. }
  1384. }
  1385. return newbuf;
  1386. }
  1387. wxString BOARD::ConvertKIIDsToCrossReferences( const wxString& aSource ) const
  1388. {
  1389. wxString newbuf;
  1390. size_t sourceLen = aSource.length();
  1391. for( size_t i = 0; i < sourceLen; ++i )
  1392. {
  1393. if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
  1394. {
  1395. wxString token;
  1396. bool isCrossRef = false;
  1397. for( i = i + 2; i < sourceLen; ++i )
  1398. {
  1399. if( aSource[i] == '}' )
  1400. break;
  1401. if( aSource[i] == ':' )
  1402. isCrossRef = true;
  1403. token.append( aSource[i] );
  1404. }
  1405. if( isCrossRef )
  1406. {
  1407. wxString remainder;
  1408. wxString ref = token.BeforeFirst( ':', &remainder );
  1409. BOARD_ITEM* refItem = ResolveItem( KIID( ref ), true );
  1410. if( refItem && refItem->Type() == PCB_FOOTPRINT_T )
  1411. {
  1412. token = static_cast<FOOTPRINT*>( refItem )->GetReference() + wxT( ":" )
  1413. + remainder;
  1414. }
  1415. }
  1416. newbuf.append( wxT( "${" ) + token + wxT( "}" ) );
  1417. }
  1418. else
  1419. {
  1420. newbuf.append( aSource[i] );
  1421. }
  1422. }
  1423. return newbuf;
  1424. }
  1425. unsigned BOARD::GetNodesCount( int aNet ) const
  1426. {
  1427. unsigned retval = 0;
  1428. for( FOOTPRINT* footprint : Footprints() )
  1429. {
  1430. for( PAD* pad : footprint->Pads() )
  1431. {
  1432. if( ( aNet == -1 && pad->GetNetCode() > 0 ) || aNet == pad->GetNetCode() )
  1433. retval++;
  1434. }
  1435. }
  1436. return retval;
  1437. }
  1438. BOX2I BOARD::ComputeBoundingBox( bool aBoardEdgesOnly ) const
  1439. {
  1440. BOX2I bbox;
  1441. LSET visible = GetVisibleLayers();
  1442. // If the board is just showing a footprint, we want all footprint layers included in the
  1443. // bounding box
  1444. if( IsFootprintHolder() )
  1445. visible.set();
  1446. if( aBoardEdgesOnly )
  1447. visible.set( Edge_Cuts );
  1448. // Check shapes, dimensions, texts, and fiducials
  1449. for( BOARD_ITEM* item : m_drawings )
  1450. {
  1451. if( aBoardEdgesOnly && ( item->GetLayer() != Edge_Cuts || item->Type() != PCB_SHAPE_T ) )
  1452. continue;
  1453. if( ( item->GetLayerSet() & visible ).any() )
  1454. bbox.Merge( item->GetBoundingBox() );
  1455. }
  1456. // Check footprints
  1457. for( FOOTPRINT* footprint : m_footprints )
  1458. {
  1459. if( aBoardEdgesOnly )
  1460. {
  1461. for( const BOARD_ITEM* edge : footprint->GraphicalItems() )
  1462. {
  1463. if( edge->GetLayer() == Edge_Cuts && edge->Type() == PCB_SHAPE_T )
  1464. bbox.Merge( edge->GetBoundingBox() );
  1465. }
  1466. }
  1467. else if( ( footprint->GetLayerSet() & visible ).any() )
  1468. {
  1469. bbox.Merge( footprint->GetBoundingBox( true ) );
  1470. }
  1471. }
  1472. if( !aBoardEdgesOnly )
  1473. {
  1474. // Check tracks
  1475. for( PCB_TRACK* track : m_tracks )
  1476. {
  1477. if( ( track->GetLayerSet() & visible ).any() )
  1478. bbox.Merge( track->GetBoundingBox() );
  1479. }
  1480. // Check zones
  1481. for( ZONE* aZone : m_zones )
  1482. {
  1483. if( ( aZone->GetLayerSet() & visible ).any() )
  1484. bbox.Merge( aZone->GetBoundingBox() );
  1485. }
  1486. }
  1487. return bbox;
  1488. }
  1489. void BOARD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  1490. {
  1491. int padCount = 0;
  1492. int viaCount = 0;
  1493. int trackSegmentCount = 0;
  1494. std::set<int> netCodes;
  1495. int unconnected = GetConnectivity()->GetUnconnectedCount( true );
  1496. for( PCB_TRACK* item : m_tracks )
  1497. {
  1498. if( item->Type() == PCB_VIA_T )
  1499. viaCount++;
  1500. else
  1501. trackSegmentCount++;
  1502. if( item->GetNetCode() > 0 )
  1503. netCodes.insert( item->GetNetCode() );
  1504. }
  1505. for( FOOTPRINT* footprint : Footprints() )
  1506. {
  1507. for( PAD* pad : footprint->Pads() )
  1508. {
  1509. padCount++;
  1510. if( pad->GetNetCode() > 0 )
  1511. netCodes.insert( pad->GetNetCode() );
  1512. }
  1513. }
  1514. aList.emplace_back( _( "Pads" ), wxString::Format( wxT( "%d" ), padCount ) );
  1515. aList.emplace_back( _( "Vias" ), wxString::Format( wxT( "%d" ), viaCount ) );
  1516. aList.emplace_back( _( "Track Segments" ), wxString::Format( wxT( "%d" ), trackSegmentCount ) );
  1517. aList.emplace_back( _( "Nets" ), wxString::Format( wxT( "%d" ), (int) netCodes.size() ) );
  1518. aList.emplace_back( _( "Unrouted" ), wxString::Format( wxT( "%d" ), unconnected ) );
  1519. }
  1520. INSPECT_RESULT BOARD::Visit( INSPECTOR inspector, void* testData,
  1521. const std::vector<KICAD_T>& scanTypes )
  1522. {
  1523. #if 0 && defined(DEBUG)
  1524. std::cout << GetClass().mb_str() << ' ';
  1525. #endif
  1526. bool footprintsScanned = false;
  1527. bool drawingsScanned = false;
  1528. bool tracksScanned = false;
  1529. for( KICAD_T scanType : scanTypes )
  1530. {
  1531. switch( scanType )
  1532. {
  1533. case PCB_T:
  1534. if( inspector( this, testData ) == INSPECT_RESULT::QUIT )
  1535. return INSPECT_RESULT::QUIT;
  1536. break;
  1537. /*
  1538. * Instances of the requested KICAD_T live in a list, either one that I manage, or one
  1539. * that my footprints manage. If it's a type managed by class FOOTPRINT, then simply
  1540. * pass it on to each footprint's Visit() function via IterateForward( m_footprints, ... ).
  1541. */
  1542. case PCB_FOOTPRINT_T:
  1543. case PCB_PAD_T:
  1544. case PCB_SHAPE_T:
  1545. case PCB_REFERENCE_IMAGE_T:
  1546. case PCB_FIELD_T:
  1547. case PCB_TEXT_T:
  1548. case PCB_TEXTBOX_T:
  1549. case PCB_TABLE_T:
  1550. case PCB_TABLECELL_T:
  1551. case PCB_DIM_ALIGNED_T:
  1552. case PCB_DIM_CENTER_T:
  1553. case PCB_DIM_RADIAL_T:
  1554. case PCB_DIM_ORTHOGONAL_T:
  1555. case PCB_DIM_LEADER_T:
  1556. case PCB_TARGET_T:
  1557. if( !footprintsScanned )
  1558. {
  1559. if( IterateForward<FOOTPRINT*>( m_footprints, inspector, testData, scanTypes )
  1560. == INSPECT_RESULT::QUIT )
  1561. {
  1562. return INSPECT_RESULT::QUIT;
  1563. }
  1564. footprintsScanned = true;
  1565. }
  1566. if( !drawingsScanned )
  1567. {
  1568. if( IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, scanTypes )
  1569. == INSPECT_RESULT::QUIT )
  1570. {
  1571. return INSPECT_RESULT::QUIT;
  1572. }
  1573. drawingsScanned = true;
  1574. }
  1575. break;
  1576. case PCB_VIA_T:
  1577. case PCB_TRACE_T:
  1578. case PCB_ARC_T:
  1579. if( !tracksScanned )
  1580. {
  1581. if( IterateForward<PCB_TRACK*>( m_tracks, inspector, testData, scanTypes )
  1582. == INSPECT_RESULT::QUIT )
  1583. {
  1584. return INSPECT_RESULT::QUIT;
  1585. }
  1586. tracksScanned = true;
  1587. }
  1588. break;
  1589. case PCB_MARKER_T:
  1590. for( PCB_MARKER* marker : m_markers )
  1591. {
  1592. if( marker->Visit( inspector, testData, { scanType } ) == INSPECT_RESULT::QUIT )
  1593. return INSPECT_RESULT::QUIT;
  1594. }
  1595. break;
  1596. case PCB_ZONE_T:
  1597. if( !footprintsScanned )
  1598. {
  1599. if( IterateForward<FOOTPRINT*>( m_footprints, inspector, testData, scanTypes )
  1600. == INSPECT_RESULT::QUIT )
  1601. {
  1602. return INSPECT_RESULT::QUIT;
  1603. }
  1604. footprintsScanned = true;
  1605. }
  1606. for( ZONE* zone : m_zones)
  1607. {
  1608. if( zone->Visit( inspector, testData, { scanType } ) == INSPECT_RESULT::QUIT )
  1609. return INSPECT_RESULT::QUIT;
  1610. }
  1611. break;
  1612. case PCB_GENERATOR_T:
  1613. if( !footprintsScanned )
  1614. {
  1615. if( IterateForward<FOOTPRINT*>( m_footprints, inspector, testData, scanTypes )
  1616. == INSPECT_RESULT::QUIT )
  1617. {
  1618. return INSPECT_RESULT::QUIT;
  1619. }
  1620. footprintsScanned = true;
  1621. }
  1622. if( IterateForward<PCB_GENERATOR*>( m_generators, inspector, testData, { scanType } )
  1623. == INSPECT_RESULT::QUIT )
  1624. {
  1625. return INSPECT_RESULT::QUIT;
  1626. }
  1627. break;
  1628. case PCB_GROUP_T:
  1629. if( IterateForward<PCB_GROUP*>( m_groups, inspector, testData, { scanType } )
  1630. == INSPECT_RESULT::QUIT )
  1631. {
  1632. return INSPECT_RESULT::QUIT;
  1633. }
  1634. break;
  1635. default:
  1636. break;
  1637. }
  1638. }
  1639. return INSPECT_RESULT::CONTINUE;
  1640. }
  1641. NETINFO_ITEM* BOARD::FindNet( int aNetcode ) const
  1642. {
  1643. // the first valid netcode is 1 and the last is m_NetInfo.GetCount()-1.
  1644. // zero is reserved for "no connection" and is not actually a net.
  1645. // nullptr is returned for non valid netcodes
  1646. if( aNetcode == NETINFO_LIST::UNCONNECTED && m_NetInfo.GetNetCount() == 0 )
  1647. return NETINFO_LIST::OrphanedItem();
  1648. else
  1649. return m_NetInfo.GetNetItem( aNetcode );
  1650. }
  1651. NETINFO_ITEM* BOARD::FindNet( const wxString& aNetname ) const
  1652. {
  1653. return m_NetInfo.GetNetItem( aNetname );
  1654. }
  1655. int BOARD::MatchDpSuffix( const wxString& aNetName, wxString& aComplementNet )
  1656. {
  1657. int rv = 0;
  1658. int count = 0;
  1659. for( auto it = aNetName.rbegin(); it != aNetName.rend() && rv == 0; ++it, ++count )
  1660. {
  1661. int ch = *it;
  1662. if( ( ch >= '0' && ch <= '9' ) || ch == '_' )
  1663. {
  1664. continue;
  1665. }
  1666. else if( ch == '+' )
  1667. {
  1668. aComplementNet = wxT( "-" );
  1669. rv = 1;
  1670. }
  1671. else if( ch == '-' )
  1672. {
  1673. aComplementNet = wxT( "+" );
  1674. rv = -1;
  1675. }
  1676. else if( ch == 'N' )
  1677. {
  1678. aComplementNet = wxT( "P" );
  1679. rv = -1;
  1680. }
  1681. else if ( ch == 'P' )
  1682. {
  1683. aComplementNet = wxT( "N" );
  1684. rv = 1;
  1685. }
  1686. else
  1687. {
  1688. break;
  1689. }
  1690. }
  1691. if( rv != 0 && count >= 1 )
  1692. {
  1693. aComplementNet = aNetName.Left( aNetName.length() - count )
  1694. + aComplementNet
  1695. + aNetName.Right( count - 1 );
  1696. }
  1697. return rv;
  1698. }
  1699. NETINFO_ITEM* BOARD::DpCoupledNet( const NETINFO_ITEM* aNet )
  1700. {
  1701. if( aNet )
  1702. {
  1703. wxString refName = aNet->GetNetname();
  1704. wxString coupledNetName;
  1705. if( MatchDpSuffix( refName, coupledNetName ) )
  1706. return FindNet( coupledNetName );
  1707. }
  1708. return nullptr;
  1709. }
  1710. FOOTPRINT* BOARD::FindFootprintByReference( const wxString& aReference ) const
  1711. {
  1712. for( FOOTPRINT* footprint : m_footprints )
  1713. {
  1714. if( aReference == footprint->GetReference() )
  1715. return footprint;
  1716. }
  1717. return nullptr;
  1718. }
  1719. FOOTPRINT* BOARD::FindFootprintByPath( const KIID_PATH& aPath ) const
  1720. {
  1721. for( FOOTPRINT* footprint : m_footprints )
  1722. {
  1723. if( footprint->GetPath() == aPath )
  1724. return footprint;
  1725. }
  1726. return nullptr;
  1727. }
  1728. std::set<wxString> BOARD::GetNetClassAssignmentCandidates() const
  1729. {
  1730. std::set<wxString> names;
  1731. for( const NETINFO_ITEM* net : m_NetInfo )
  1732. {
  1733. if( !net->GetNetname().IsEmpty() )
  1734. names.insert( net->GetNetname() );
  1735. }
  1736. return names;
  1737. }
  1738. void BOARD::SynchronizeProperties()
  1739. {
  1740. if( m_project && !m_project->IsNullProject() )
  1741. SetProperties( m_project->GetTextVars() );
  1742. }
  1743. void BOARD::SynchronizeTimeDomainProperties()
  1744. {
  1745. m_lengthDelayCalc->SynchronizeTimeDomainProperties();
  1746. }
  1747. void BOARD::SynchronizeNetsAndNetClasses( bool aResetTrackAndViaSizes )
  1748. {
  1749. if( !m_project )
  1750. return;
  1751. BOARD_DESIGN_SETTINGS& bds = GetDesignSettings();
  1752. const std::shared_ptr<NETCLASS>& defaultNetClass = bds.m_NetSettings->GetDefaultNetclass();
  1753. bds.m_NetSettings->ClearAllCaches();
  1754. for( NETINFO_ITEM* net : m_NetInfo )
  1755. net->SetNetClass( bds.m_NetSettings->GetEffectiveNetClass( net->GetNetname() ) );
  1756. if( aResetTrackAndViaSizes )
  1757. {
  1758. // Set initial values for custom track width & via size to match the default
  1759. // netclass settings
  1760. bds.UseCustomTrackViaSize( false );
  1761. bds.SetCustomTrackWidth( defaultNetClass->GetTrackWidth() );
  1762. bds.SetCustomViaSize( defaultNetClass->GetViaDiameter() );
  1763. bds.SetCustomViaDrill( defaultNetClass->GetViaDrill() );
  1764. bds.SetCustomDiffPairWidth( defaultNetClass->GetDiffPairWidth() );
  1765. bds.SetCustomDiffPairGap( defaultNetClass->GetDiffPairGap() );
  1766. bds.SetCustomDiffPairViaGap( defaultNetClass->GetDiffPairViaGap() );
  1767. }
  1768. InvokeListeners( &BOARD_LISTENER::OnBoardNetSettingsChanged, *this );
  1769. }
  1770. bool BOARD::SynchronizeComponentClasses( const std::unordered_set<wxString>& aNewSheetPaths ) const
  1771. {
  1772. std::shared_ptr<COMPONENT_CLASS_SETTINGS> settings =
  1773. GetProject()->GetProjectFile().ComponentClassSettings();
  1774. return m_componentClassManager->SyncDynamicComponentClassAssignments(
  1775. settings->GetComponentClassAssignments(), settings->GetEnableSheetComponentClasses(),
  1776. aNewSheetPaths );
  1777. }
  1778. int BOARD::SetAreasNetCodesFromNetNames()
  1779. {
  1780. int error_count = 0;
  1781. for( ZONE* zone : Zones() )
  1782. {
  1783. if( !zone->IsOnCopperLayer() )
  1784. {
  1785. zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
  1786. continue;
  1787. }
  1788. if( zone->GetNetCode() != 0 ) // i.e. if this zone is connected to a net
  1789. {
  1790. const NETINFO_ITEM* net = zone->GetNet();
  1791. if( net )
  1792. {
  1793. zone->SetNetCode( net->GetNetCode() );
  1794. }
  1795. else
  1796. {
  1797. error_count++;
  1798. // keep Net Name and set m_NetCode to -1 : error flag.
  1799. zone->SetNetCode( -1 );
  1800. }
  1801. }
  1802. }
  1803. return error_count;
  1804. }
  1805. PAD* BOARD::GetPad( const VECTOR2I& aPosition, const LSET& aLayerSet ) const
  1806. {
  1807. for( FOOTPRINT* footprint : m_footprints )
  1808. {
  1809. PAD* pad = nullptr;
  1810. if( footprint->HitTest( aPosition ) )
  1811. pad = footprint->GetPad( aPosition, aLayerSet.any() ? aLayerSet : LSET::AllCuMask() );
  1812. if( pad )
  1813. return pad;
  1814. }
  1815. return nullptr;
  1816. }
  1817. PAD* BOARD::GetPad( const PCB_TRACK* aTrace, ENDPOINT_T aEndPoint ) const
  1818. {
  1819. const VECTOR2I& aPosition = aTrace->GetEndPoint( aEndPoint );
  1820. LSET lset( { aTrace->GetLayer() } );
  1821. return GetPad( aPosition, lset );
  1822. }
  1823. PAD* BOARD::GetPad( std::vector<PAD*>& aPadList, const VECTOR2I& aPosition, const LSET& aLayerSet ) const
  1824. {
  1825. // Search aPadList for aPosition
  1826. // aPadList is sorted by X then Y values, and a fast binary search is used
  1827. int idxmax = aPadList.size() - 1;
  1828. int delta = aPadList.size();
  1829. int idx = 0; // Starting index is the beginning of list
  1830. while( delta )
  1831. {
  1832. // Calculate half size of remaining interval to test.
  1833. // Ensure the computed value is not truncated (too small)
  1834. if( (delta & 1) && ( delta > 1 ) )
  1835. delta++;
  1836. delta /= 2;
  1837. PAD* pad = aPadList[idx];
  1838. if( pad->GetPosition() == aPosition ) // candidate found
  1839. {
  1840. // The pad must match the layer mask:
  1841. if( ( aLayerSet & pad->GetLayerSet() ).any() )
  1842. return pad;
  1843. // More than one pad can be at aPosition
  1844. // search for a pad at aPosition that matched this mask
  1845. // search next
  1846. for( int ii = idx+1; ii <= idxmax; ii++ )
  1847. {
  1848. pad = aPadList[ii];
  1849. if( pad->GetPosition() != aPosition )
  1850. break;
  1851. if( ( aLayerSet & pad->GetLayerSet() ).any() )
  1852. return pad;
  1853. }
  1854. // search previous
  1855. for( int ii = idx - 1 ;ii >=0; ii-- )
  1856. {
  1857. pad = aPadList[ii];
  1858. if( pad->GetPosition() != aPosition )
  1859. break;
  1860. if( ( aLayerSet & pad->GetLayerSet() ).any() )
  1861. return pad;
  1862. }
  1863. // Not found:
  1864. return nullptr;
  1865. }
  1866. if( pad->GetPosition().x == aPosition.x ) // Must search considering Y coordinate
  1867. {
  1868. if( pad->GetPosition().y < aPosition.y ) // Must search after this item
  1869. {
  1870. idx += delta;
  1871. if( idx > idxmax )
  1872. idx = idxmax;
  1873. }
  1874. else // Must search before this item
  1875. {
  1876. idx -= delta;
  1877. if( idx < 0 )
  1878. idx = 0;
  1879. }
  1880. }
  1881. else if( pad->GetPosition().x < aPosition.x ) // Must search after this item
  1882. {
  1883. idx += delta;
  1884. if( idx > idxmax )
  1885. idx = idxmax;
  1886. }
  1887. else // Must search before this item
  1888. {
  1889. idx -= delta;
  1890. if( idx < 0 )
  1891. idx = 0;
  1892. }
  1893. }
  1894. return nullptr;
  1895. }
  1896. /**
  1897. * Used by #GetSortedPadListByXCoord to sort a pad list by X coordinate value.
  1898. *
  1899. * This function is used to build ordered pads lists
  1900. */
  1901. bool sortPadsByXthenYCoord( PAD* const & aLH, PAD* const & aRH )
  1902. {
  1903. if( aLH->GetPosition().x == aRH->GetPosition().x )
  1904. return aLH->GetPosition().y < aRH->GetPosition().y;
  1905. return aLH->GetPosition().x < aRH->GetPosition().x;
  1906. }
  1907. void BOARD::GetSortedPadListByXthenYCoord( std::vector<PAD*>& aVector, int aNetCode ) const
  1908. {
  1909. for( FOOTPRINT* footprint : Footprints() )
  1910. {
  1911. for( PAD* pad : footprint->Pads( ) )
  1912. {
  1913. if( aNetCode < 0 || pad->GetNetCode() == aNetCode )
  1914. aVector.push_back( pad );
  1915. }
  1916. }
  1917. std::sort( aVector.begin(), aVector.end(), sortPadsByXthenYCoord );
  1918. }
  1919. BOARD_STACKUP BOARD::GetStackupOrDefault() const
  1920. {
  1921. if( GetDesignSettings().m_HasStackup )
  1922. return GetDesignSettings().GetStackupDescriptor();
  1923. BOARD_STACKUP stackup;
  1924. stackup.BuildDefaultStackupList( &GetDesignSettings(), GetCopperLayerCount() );
  1925. return stackup;
  1926. }
  1927. std::tuple<int, double, double, double, double> BOARD::GetTrackLength( const PCB_TRACK& aTrack ) const
  1928. {
  1929. std::shared_ptr<CONNECTIVITY_DATA> connectivity = GetBoard()->GetConnectivity();
  1930. std::vector<LENGTH_DELAY_CALCULATION_ITEM> items;
  1931. for( BOARD_CONNECTED_ITEM* boardItem : connectivity->GetConnectedItems( &aTrack, EXCLUDE_ZONES ) )
  1932. {
  1933. LENGTH_DELAY_CALCULATION_ITEM item = GetLengthCalculation()->GetLengthCalculationItem( boardItem );
  1934. if( item.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::UNKNOWN )
  1935. items.push_back( std::move( item ) );
  1936. }
  1937. constexpr PATH_OPTIMISATIONS opts = {
  1938. .OptimiseViaLayers = true, .MergeTracks = true, .OptimiseTracesInPads = true, .InferViaInPad = false
  1939. };
  1940. LENGTH_DELAY_STATS details = GetLengthCalculation()->CalculateLengthDetails(
  1941. items, opts, nullptr, nullptr, LENGTH_DELAY_LAYER_OPT::NO_LAYER_DETAIL,
  1942. LENGTH_DELAY_DOMAIN_OPT::WITH_DELAY_DETAIL );
  1943. return std::make_tuple( items.size(), details.TrackLength + details.ViaLength, details.PadToDieLength,
  1944. details.TrackDelay + details.ViaDelay, details.PadToDieDelay );
  1945. }
  1946. FOOTPRINT* BOARD::GetFootprint( const VECTOR2I& aPosition, PCB_LAYER_ID aActiveLayer,
  1947. bool aVisibleOnly, bool aIgnoreLocked ) const
  1948. {
  1949. FOOTPRINT* footprint = nullptr;
  1950. FOOTPRINT* alt_footprint = nullptr;
  1951. int min_dim = 0x7FFFFFFF;
  1952. int alt_min_dim = 0x7FFFFFFF;
  1953. bool current_layer_back = IsBackLayer( aActiveLayer );
  1954. for( FOOTPRINT* candidate : m_footprints )
  1955. {
  1956. // is the ref point within the footprint's bounds?
  1957. if( !candidate->HitTest( aPosition ) )
  1958. continue;
  1959. // if caller wants to ignore locked footprints, and this one is locked, skip it.
  1960. if( aIgnoreLocked && candidate->IsLocked() )
  1961. continue;
  1962. PCB_LAYER_ID layer = candidate->GetLayer();
  1963. // Filter non visible footprints if requested
  1964. if( !aVisibleOnly || IsFootprintLayerVisible( layer ) )
  1965. {
  1966. BOX2I bb = candidate->GetBoundingBox( false );
  1967. int offx = bb.GetX() + bb.GetWidth() / 2;
  1968. int offy = bb.GetY() + bb.GetHeight() / 2;
  1969. // off x & offy point to the middle of the box.
  1970. int dist = ( aPosition.x - offx ) * ( aPosition.x - offx ) +
  1971. ( aPosition.y - offy ) * ( aPosition.y - offy );
  1972. if( current_layer_back == IsBackLayer( layer ) )
  1973. {
  1974. if( dist <= min_dim )
  1975. {
  1976. // better footprint shown on the active side
  1977. footprint = candidate;
  1978. min_dim = dist;
  1979. }
  1980. }
  1981. else if( aVisibleOnly && IsFootprintLayerVisible( layer ) )
  1982. {
  1983. if( dist <= alt_min_dim )
  1984. {
  1985. // better footprint shown on the other side
  1986. alt_footprint = candidate;
  1987. alt_min_dim = dist;
  1988. }
  1989. }
  1990. }
  1991. }
  1992. if( footprint )
  1993. return footprint;
  1994. if( alt_footprint)
  1995. return alt_footprint;
  1996. return nullptr;
  1997. }
  1998. std::list<ZONE*> BOARD::GetZoneList( bool aIncludeZonesInFootprints ) const
  1999. {
  2000. std::list<ZONE*> zones;
  2001. for( ZONE* zone : Zones() )
  2002. zones.push_back( zone );
  2003. if( aIncludeZonesInFootprints )
  2004. {
  2005. for( FOOTPRINT* footprint : m_footprints )
  2006. {
  2007. for( ZONE* zone : footprint->Zones() )
  2008. zones.push_back( zone );
  2009. }
  2010. }
  2011. return zones;
  2012. }
  2013. ZONE* BOARD::AddArea( PICKED_ITEMS_LIST* aNewZonesList, int aNetcode, PCB_LAYER_ID aLayer,
  2014. VECTOR2I aStartPointPosition, ZONE_BORDER_DISPLAY_STYLE aHatch )
  2015. {
  2016. ZONE* new_area = new ZONE( this );
  2017. new_area->SetNetCode( aNetcode );
  2018. new_area->SetLayer( aLayer );
  2019. m_zones.push_back( new_area );
  2020. new_area->SetHatchStyle( (ZONE_BORDER_DISPLAY_STYLE) aHatch );
  2021. // Add the first corner to the new zone
  2022. new_area->AppendCorner( aStartPointPosition, -1 );
  2023. if( aNewZonesList )
  2024. {
  2025. ITEM_PICKER picker( nullptr, new_area, UNDO_REDO::NEWITEM );
  2026. aNewZonesList->PushItem( picker );
  2027. }
  2028. return new_area;
  2029. }
  2030. bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
  2031. OUTLINE_ERROR_HANDLER* aErrorHandler,
  2032. bool aAllowUseArcsInPolygons,
  2033. bool aIncludeNPTHAsOutlines )
  2034. {
  2035. // max dist from one endPt to next startPt: use the current value
  2036. int chainingEpsilon = GetOutlinesChainingEpsilon();
  2037. bool success = BuildBoardPolygonOutlines( this, aOutlines, GetDesignSettings().m_MaxError,
  2038. chainingEpsilon, aErrorHandler,
  2039. aAllowUseArcsInPolygons );
  2040. // Now add NPTH oval holes as holes in outlines if required
  2041. if( aIncludeNPTHAsOutlines )
  2042. {
  2043. for( FOOTPRINT* fp : Footprints() )
  2044. {
  2045. for( PAD* pad : fp->Pads() )
  2046. {
  2047. if( pad->GetAttribute () != PAD_ATTRIB::NPTH )
  2048. continue;
  2049. SHAPE_POLY_SET hole;
  2050. pad->TransformHoleToPolygon( hole, 0, pad->GetMaxError(), ERROR_INSIDE );
  2051. if( hole.OutlineCount() > 0 ) // can be not the case for malformed NPTH holes
  2052. {
  2053. // Add this pad hole to the main outline
  2054. // But we can have more than one main outline (i.e. more than one board), so
  2055. // search the right main outline i.e. the outline that contains the pad hole
  2056. SHAPE_LINE_CHAIN& pad_hole = hole.Outline( 0 );
  2057. const VECTOR2I holePt = pad_hole.CPoint( 0 );
  2058. for( int jj = 0; jj < aOutlines.OutlineCount(); ++jj )
  2059. {
  2060. if( aOutlines.Outline( jj ).PointInside( holePt ) )
  2061. {
  2062. aOutlines.AddHole( pad_hole, jj );
  2063. break;
  2064. }
  2065. }
  2066. }
  2067. }
  2068. }
  2069. }
  2070. // Make polygon strictly simple to avoid issues (especially in 3D viewer)
  2071. aOutlines.Simplify();
  2072. return success;
  2073. }
  2074. EMBEDDED_FILES* BOARD::GetEmbeddedFiles()
  2075. {
  2076. if( m_embeddedFilesDelegate )
  2077. return static_cast<EMBEDDED_FILES*>( m_embeddedFilesDelegate );
  2078. return static_cast<EMBEDDED_FILES*>( this );
  2079. }
  2080. const EMBEDDED_FILES* BOARD::GetEmbeddedFiles() const
  2081. {
  2082. if( m_embeddedFilesDelegate )
  2083. return static_cast<const EMBEDDED_FILES*>( m_embeddedFilesDelegate );
  2084. return static_cast<const EMBEDDED_FILES*>( this );
  2085. }
  2086. std::set<KIFONT::OUTLINE_FONT*> BOARD::GetFonts() const
  2087. {
  2088. using PERMISSION = KIFONT::OUTLINE_FONT::EMBEDDING_PERMISSION;
  2089. std::set<KIFONT::OUTLINE_FONT*> fonts;
  2090. for( BOARD_ITEM* item : Drawings() )
  2091. {
  2092. if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
  2093. {
  2094. KIFONT::FONT* font = text->GetFont();
  2095. if( font && font->IsOutline() )
  2096. {
  2097. KIFONT::OUTLINE_FONT* outlineFont = static_cast<KIFONT::OUTLINE_FONT*>( font );
  2098. PERMISSION permission = outlineFont->GetEmbeddingPermission();
  2099. if( permission == PERMISSION::EDITABLE || permission == PERMISSION::INSTALLABLE )
  2100. fonts.insert( outlineFont );
  2101. }
  2102. }
  2103. }
  2104. return fonts;
  2105. }
  2106. void BOARD::EmbedFonts()
  2107. {
  2108. for( KIFONT::OUTLINE_FONT* font : GetFonts() )
  2109. {
  2110. EMBEDDED_FILES::EMBEDDED_FILE* file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
  2111. file->type = EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::FONT;
  2112. }
  2113. }
  2114. const std::vector<PAD*> BOARD::GetPads() const
  2115. {
  2116. std::vector<PAD*> allPads;
  2117. for( FOOTPRINT* footprint : Footprints() )
  2118. {
  2119. for( PAD* pad : footprint->Pads() )
  2120. allPads.push_back( pad );
  2121. }
  2122. return allPads;
  2123. }
  2124. const std::vector<BOARD_CONNECTED_ITEM*> BOARD::AllConnectedItems()
  2125. {
  2126. std::vector<BOARD_CONNECTED_ITEM*> items;
  2127. for( PCB_TRACK* track : Tracks() )
  2128. items.push_back( track );
  2129. for( FOOTPRINT* footprint : Footprints() )
  2130. {
  2131. for( PAD* pad : footprint->Pads() )
  2132. items.push_back( pad );
  2133. }
  2134. for( ZONE* zone : Zones() )
  2135. items.push_back( zone );
  2136. for( BOARD_ITEM* item : Drawings() )
  2137. {
  2138. if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
  2139. items.push_back( bci );
  2140. }
  2141. return items;
  2142. }
  2143. void BOARD::MapNets( BOARD* aDestBoard )
  2144. {
  2145. for( BOARD_CONNECTED_ITEM* item : AllConnectedItems() )
  2146. {
  2147. NETINFO_ITEM* netInfo = aDestBoard->FindNet( item->GetNetname() );
  2148. if( netInfo )
  2149. item->SetNet( netInfo );
  2150. else
  2151. {
  2152. NETINFO_ITEM* newNet = new NETINFO_ITEM( aDestBoard, item->GetNetname() );
  2153. aDestBoard->Add( newNet );
  2154. item->SetNet( newNet );
  2155. }
  2156. }
  2157. }
  2158. void BOARD::SanitizeNetcodes()
  2159. {
  2160. for ( BOARD_CONNECTED_ITEM* item : AllConnectedItems() )
  2161. {
  2162. if( FindNet( item->GetNetCode() ) == nullptr )
  2163. item->SetNetCode( NETINFO_LIST::ORPHANED );
  2164. }
  2165. }
  2166. void BOARD::AddListener( BOARD_LISTENER* aListener )
  2167. {
  2168. if( !alg::contains( m_listeners, aListener ) )
  2169. m_listeners.push_back( aListener );
  2170. }
  2171. void BOARD::RemoveListener( BOARD_LISTENER* aListener )
  2172. {
  2173. auto i = std::find( m_listeners.begin(), m_listeners.end(), aListener );
  2174. if( i != m_listeners.end() )
  2175. {
  2176. std::iter_swap( i, m_listeners.end() - 1 );
  2177. m_listeners.pop_back();
  2178. }
  2179. }
  2180. void BOARD::RemoveAllListeners()
  2181. {
  2182. m_listeners.clear();
  2183. }
  2184. void BOARD::OnItemChanged( BOARD_ITEM* aItem )
  2185. {
  2186. InvokeListeners( &BOARD_LISTENER::OnBoardItemChanged, *this, aItem );
  2187. }
  2188. void BOARD::OnItemsChanged( std::vector<BOARD_ITEM*>& aItems )
  2189. {
  2190. InvokeListeners( &BOARD_LISTENER::OnBoardItemsChanged, *this, aItems );
  2191. }
  2192. void BOARD::OnItemsCompositeUpdate( std::vector<BOARD_ITEM*>& aAddedItems,
  2193. std::vector<BOARD_ITEM*>& aRemovedItems,
  2194. std::vector<BOARD_ITEM*>& aChangedItems )
  2195. {
  2196. InvokeListeners( &BOARD_LISTENER::OnBoardCompositeUpdate, *this, aAddedItems, aRemovedItems,
  2197. aChangedItems );
  2198. }
  2199. void BOARD::OnRatsnestChanged()
  2200. {
  2201. InvokeListeners( &BOARD_LISTENER::OnBoardRatsnestChanged, *this );
  2202. }
  2203. void BOARD::ResetNetHighLight()
  2204. {
  2205. m_highLight.Clear();
  2206. m_highLightPrevious.Clear();
  2207. InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
  2208. }
  2209. void BOARD::SetHighLightNet( int aNetCode, bool aMulti )
  2210. {
  2211. if( !m_highLight.m_netCodes.count( aNetCode ) )
  2212. {
  2213. if( !aMulti )
  2214. m_highLight.m_netCodes.clear();
  2215. m_highLight.m_netCodes.insert( aNetCode );
  2216. InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
  2217. }
  2218. }
  2219. void BOARD::HighLightON( bool aValue )
  2220. {
  2221. if( m_highLight.m_highLightOn != aValue )
  2222. {
  2223. m_highLight.m_highLightOn = aValue;
  2224. InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
  2225. }
  2226. }
  2227. wxString BOARD::GroupsSanityCheck( bool repair )
  2228. {
  2229. if( repair )
  2230. {
  2231. while( GroupsSanityCheckInternal( repair ) != wxEmptyString )
  2232. {};
  2233. return wxEmptyString;
  2234. }
  2235. return GroupsSanityCheckInternal( repair );
  2236. }
  2237. wxString BOARD::GroupsSanityCheckInternal( bool repair )
  2238. {
  2239. // Cycle detection
  2240. //
  2241. // Each group has at most one parent group.
  2242. // So we start at group 0 and traverse the parent chain, marking groups seen along the way.
  2243. // If we ever see a group that we've already marked, that's a cycle.
  2244. // If we reach the end of the chain, we know all groups in that chain are not part of any cycle.
  2245. //
  2246. // Algorithm below is linear in the # of groups because each group is visited only once.
  2247. // There may be extra time taken due to the container access calls and iterators.
  2248. //
  2249. // Groups we know are cycle free
  2250. std::unordered_set<EDA_GROUP*> knownCycleFreeGroups;
  2251. // Groups in the current chain we're exploring.
  2252. std::unordered_set<EDA_GROUP*> currentChainGroups;
  2253. // Groups we haven't checked yet.
  2254. std::unordered_set<EDA_GROUP*> toCheckGroups;
  2255. // Initialize set of groups and generators to check that could participate in a cycle.
  2256. for( PCB_GROUP* group : Groups() )
  2257. toCheckGroups.insert( group );
  2258. for( PCB_GENERATOR* gen : Generators() )
  2259. toCheckGroups.insert( gen );
  2260. while( !toCheckGroups.empty() )
  2261. {
  2262. currentChainGroups.clear();
  2263. EDA_GROUP* group = *toCheckGroups.begin();
  2264. while( true )
  2265. {
  2266. if( currentChainGroups.find( group ) != currentChainGroups.end() )
  2267. {
  2268. if( repair )
  2269. Remove( static_cast<BOARD_ITEM*>( group->AsEdaItem() ) );
  2270. return "Cycle detected in group membership";
  2271. }
  2272. else if( knownCycleFreeGroups.find( group ) != knownCycleFreeGroups.end() )
  2273. {
  2274. // Parent is a group we know does not lead to a cycle
  2275. break;
  2276. }
  2277. currentChainGroups.insert( group );
  2278. // We haven't visited currIdx yet, so it must be in toCheckGroups
  2279. toCheckGroups.erase( group );
  2280. group = group->AsEdaItem()->GetParentGroup();
  2281. if( !group )
  2282. {
  2283. // end of chain and no cycles found in this chain
  2284. break;
  2285. }
  2286. }
  2287. // No cycles found in chain, so add it to set of groups we know don't participate
  2288. // in a cycle.
  2289. knownCycleFreeGroups.insert( currentChainGroups.begin(), currentChainGroups.end() );
  2290. }
  2291. // Success
  2292. return "";
  2293. }
  2294. bool BOARD::cmp_items::operator() ( const BOARD_ITEM* a, const BOARD_ITEM* b ) const
  2295. {
  2296. if( a->Type() != b->Type() )
  2297. return a->Type() < b->Type();
  2298. if( a->GetLayer() != b->GetLayer() )
  2299. return a->GetLayer() < b->GetLayer();
  2300. if( a->GetPosition().x != b->GetPosition().x )
  2301. return a->GetPosition().x < b->GetPosition().x;
  2302. if( a->GetPosition().y != b->GetPosition().y )
  2303. return a->GetPosition().y < b->GetPosition().y;
  2304. if( a->m_Uuid != b->m_Uuid ) // shopuld be always the case foer valid boards
  2305. return a->m_Uuid < b->m_Uuid;
  2306. return a < b;
  2307. }
  2308. bool BOARD::cmp_drawings::operator()( const BOARD_ITEM* aFirst,
  2309. const BOARD_ITEM* aSecond ) const
  2310. {
  2311. if( aFirst->Type() != aSecond->Type() )
  2312. return aFirst->Type() < aSecond->Type();
  2313. if( aFirst->GetLayer() != aSecond->GetLayer() )
  2314. return aFirst->GetLayer() < aSecond->GetLayer();
  2315. if( aFirst->Type() == PCB_SHAPE_T )
  2316. {
  2317. const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aFirst );
  2318. const PCB_SHAPE* other = static_cast<const PCB_SHAPE*>( aSecond );
  2319. return shape->Compare( other );
  2320. }
  2321. else if( aFirst->Type() == PCB_TEXT_T || aFirst->Type() == PCB_FIELD_T )
  2322. {
  2323. const PCB_TEXT* text = static_cast<const PCB_TEXT*>( aFirst );
  2324. const PCB_TEXT* other = static_cast<const PCB_TEXT*>( aSecond );
  2325. return text->Compare( other );
  2326. }
  2327. else if( aFirst->Type() == PCB_TEXTBOX_T )
  2328. {
  2329. const PCB_TEXTBOX* textbox = static_cast<const PCB_TEXTBOX*>( aFirst );
  2330. const PCB_TEXTBOX* other = static_cast<const PCB_TEXTBOX*>( aSecond );
  2331. return textbox->PCB_SHAPE::Compare( other ) && textbox->EDA_TEXT::Compare( other );
  2332. }
  2333. else if( aFirst->Type() == PCB_TABLE_T )
  2334. {
  2335. const PCB_TABLE* table = static_cast<const PCB_TABLE*>( aFirst );
  2336. const PCB_TABLE* other = static_cast<const PCB_TABLE*>( aSecond );
  2337. return PCB_TABLE::Compare( table, other );
  2338. }
  2339. return aFirst->m_Uuid < aSecond->m_Uuid;
  2340. }
  2341. void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer,
  2342. SHAPE_POLY_SET& aOutlines ) const
  2343. {
  2344. int maxError = GetDesignSettings().m_MaxError;
  2345. // convert tracks and vias:
  2346. for( const PCB_TRACK* track : m_tracks )
  2347. {
  2348. if( !track->IsOnLayer( aLayer ) )
  2349. continue;
  2350. track->TransformShapeToPolygon( aOutlines, aLayer, 0, maxError, ERROR_INSIDE );
  2351. }
  2352. // convert pads and other copper items in footprints
  2353. for( const FOOTPRINT* footprint : m_footprints )
  2354. {
  2355. footprint->TransformPadsToPolySet( aOutlines, aLayer, 0, maxError, ERROR_INSIDE );
  2356. footprint->TransformFPShapesToPolySet( aOutlines, aLayer, 0, maxError, ERROR_INSIDE,
  2357. true, /* include text */
  2358. true, /* include shapes */
  2359. false /* include private items */ );
  2360. for( const ZONE* zone : footprint->Zones() )
  2361. {
  2362. if( zone->GetLayerSet().test( aLayer ) )
  2363. zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
  2364. }
  2365. }
  2366. // convert copper zones
  2367. for( const ZONE* zone : Zones() )
  2368. {
  2369. if( zone->GetLayerSet().test( aLayer ) )
  2370. zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
  2371. }
  2372. // convert graphic items on copper layers (texts)
  2373. for( const BOARD_ITEM* item : m_drawings )
  2374. {
  2375. if( !item->IsOnLayer( aLayer ) )
  2376. continue;
  2377. switch( item->Type() )
  2378. {
  2379. case PCB_SHAPE_T:
  2380. {
  2381. const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
  2382. shape->TransformShapeToPolygon( aOutlines, aLayer, 0, maxError, ERROR_INSIDE );
  2383. break;
  2384. }
  2385. case PCB_FIELD_T:
  2386. case PCB_TEXT_T:
  2387. {
  2388. const PCB_TEXT* text = static_cast<const PCB_TEXT*>( item );
  2389. text->TransformTextToPolySet( aOutlines, 0, maxError, ERROR_INSIDE );
  2390. break;
  2391. }
  2392. case PCB_TEXTBOX_T:
  2393. {
  2394. const PCB_TEXTBOX* textbox = static_cast<const PCB_TEXTBOX*>( item );
  2395. // border
  2396. textbox->PCB_SHAPE::TransformShapeToPolygon( aOutlines, aLayer, 0, maxError,
  2397. ERROR_INSIDE );
  2398. // text
  2399. textbox->TransformTextToPolySet( aOutlines, 0, maxError, ERROR_INSIDE );
  2400. break;
  2401. }
  2402. case PCB_TABLE_T:
  2403. {
  2404. const PCB_TABLE* table = static_cast<const PCB_TABLE*>( item );
  2405. table->TransformShapeToPolygon( aOutlines, aLayer, 0, maxError, ERROR_INSIDE );
  2406. break;
  2407. }
  2408. case PCB_DIM_ALIGNED_T:
  2409. case PCB_DIM_CENTER_T:
  2410. case PCB_DIM_RADIAL_T:
  2411. case PCB_DIM_ORTHOGONAL_T:
  2412. case PCB_DIM_LEADER_T:
  2413. {
  2414. const PCB_DIMENSION_BASE* dim = static_cast<const PCB_DIMENSION_BASE*>( item );
  2415. dim->TransformShapeToPolygon( aOutlines, aLayer, 0, maxError, ERROR_INSIDE );
  2416. dim->TransformTextToPolySet( aOutlines, 0, maxError, ERROR_INSIDE );
  2417. break;
  2418. }
  2419. default:
  2420. break;
  2421. }
  2422. }
  2423. }
  2424. const BOARD_ITEM_SET BOARD::GetItemSet()
  2425. {
  2426. BOARD_ITEM_SET items;
  2427. std::copy( m_tracks.begin(), m_tracks.end(), std::inserter( items, items.end() ) );
  2428. std::copy( m_zones.begin(), m_zones.end(), std::inserter( items, items.end() ) );
  2429. std::copy( m_footprints.begin(), m_footprints.end(), std::inserter( items, items.end() ) );
  2430. std::copy( m_drawings.begin(), m_drawings.end(), std::inserter( items, items.end() ) );
  2431. std::copy( m_markers.begin(), m_markers.end(), std::inserter( items, items.end() ) );
  2432. std::copy( m_groups.begin(), m_groups.end(), std::inserter( items, items.end() ) );
  2433. return items;
  2434. }
  2435. bool BOARD::operator==( const BOARD_ITEM& aItem ) const
  2436. {
  2437. if( aItem.Type() != Type() )
  2438. return false;
  2439. const BOARD& other = static_cast<const BOARD&>( aItem );
  2440. if( *m_designSettings != *other.m_designSettings )
  2441. return false;
  2442. if( m_NetInfo.GetNetCount() != other.m_NetInfo.GetNetCount() )
  2443. return false;
  2444. const NETNAMES_MAP& thisNetNames = m_NetInfo.NetsByName();
  2445. const NETNAMES_MAP& otherNetNames = other.m_NetInfo.NetsByName();
  2446. for( auto it1 = thisNetNames.begin(), it2 = otherNetNames.begin();
  2447. it1 != thisNetNames.end() && it2 != otherNetNames.end(); ++it1, ++it2 )
  2448. {
  2449. // We only compare the names in order here, not the index values
  2450. // as the index values are auto-generated and the names are not.
  2451. if( it1->first != it2->first )
  2452. return false;
  2453. }
  2454. if( m_properties.size() != other.m_properties.size() )
  2455. return false;
  2456. for( auto it1 = m_properties.begin(), it2 = other.m_properties.begin();
  2457. it1 != m_properties.end() && it2 != other.m_properties.end(); ++it1, ++it2 )
  2458. {
  2459. if( *it1 != *it2 )
  2460. return false;
  2461. }
  2462. if( m_paper.GetCustomHeightMils() != other.m_paper.GetCustomHeightMils() )
  2463. return false;
  2464. if( m_paper.GetCustomWidthMils() != other.m_paper.GetCustomWidthMils() )
  2465. return false;
  2466. if( m_paper.GetSizeMils() != other.m_paper.GetSizeMils() )
  2467. return false;
  2468. if( m_paper.GetPaperId() != other.m_paper.GetPaperId() )
  2469. return false;
  2470. if( m_paper.GetWxOrientation() != other.m_paper.GetWxOrientation() )
  2471. return false;
  2472. for( int ii = 0; !m_titles.GetComment( ii ).empty(); ++ii )
  2473. {
  2474. if( m_titles.GetComment( ii ) != other.m_titles.GetComment( ii ) )
  2475. return false;
  2476. }
  2477. wxArrayString ourVars;
  2478. m_titles.GetContextualTextVars( &ourVars );
  2479. wxArrayString otherVars;
  2480. other.m_titles.GetContextualTextVars( &otherVars );
  2481. if( ourVars != otherVars )
  2482. return false;
  2483. return true;
  2484. }
  2485. void BOARD::UpdateBoardOutline()
  2486. {
  2487. m_boardOutline->GetOutline().RemoveAllContours();
  2488. bool has_outline = GetBoardPolygonOutlines( m_boardOutline->GetOutline() );
  2489. if( has_outline )
  2490. m_boardOutline->GetOutline().Fracture();
  2491. }