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.

1970 lines
68 KiB

5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <bitmaps.h>
  24. #include <pcb_group.h>
  25. #include <tool/tool_manager.h>
  26. #include <tools/pcb_selection_tool.h>
  27. #include <tools/pcb_picker_tool.h>
  28. #include <tools/edit_tool.h>
  29. #include <pcb_painter.h>
  30. #include <connectivity/connectivity_data.h>
  31. #include <drc/drc_engine.h>
  32. #include <dialogs/dialog_board_statistics.h>
  33. #include <dialogs/dialog_constraints_reporter.h>
  34. #include <dialogs/panel_setup_rules_base.h>
  35. #include <string_utils.h>
  36. #include <tools/board_inspection_tool.h>
  37. #include <fp_lib_table.h>
  38. #include <pcbnew_settings.h>
  39. #include <widgets/appearance_controls.h>
  40. #include <widgets/wx_html_report_box.h>
  41. #include <drc/drc_item.h>
  42. #include <pad.h>
  43. BOARD_INSPECTION_TOOL::BOARD_INSPECTION_TOOL() :
  44. PCB_TOOL_BASE( "pcbnew.InspectionTool" ),
  45. m_frame( nullptr )
  46. {
  47. m_dynamicData = nullptr;
  48. }
  49. class NET_CONTEXT_MENU : public ACTION_MENU
  50. {
  51. public:
  52. NET_CONTEXT_MENU() : ACTION_MENU( true )
  53. {
  54. SetIcon( BITMAPS::show_ratsnest );
  55. SetTitle( _( "Net Inspection Tools" ) );
  56. Add( PCB_ACTIONS::showNetInRatsnest );
  57. Add( PCB_ACTIONS::hideNetInRatsnest );
  58. AppendSeparator();
  59. Add( PCB_ACTIONS::highlightNetSelection );
  60. Add( PCB_ACTIONS::clearHighlight );
  61. }
  62. private:
  63. ACTION_MENU* create() const override
  64. {
  65. return new NET_CONTEXT_MENU();
  66. }
  67. };
  68. bool BOARD_INSPECTION_TOOL::Init()
  69. {
  70. PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  71. std::shared_ptr<NET_CONTEXT_MENU> netSubMenu = std::make_shared<NET_CONTEXT_MENU>();
  72. netSubMenu->SetTool( this );
  73. static std::vector<KICAD_T> connectedTypes = { PCB_TRACE_T,
  74. PCB_VIA_T,
  75. PCB_ARC_T,
  76. PCB_PAD_T,
  77. PCB_ZONE_T };
  78. CONDITIONAL_MENU& menu = selectionTool->GetToolMenu().GetMenu();
  79. selectionTool->GetToolMenu().RegisterSubMenu( netSubMenu );
  80. menu.AddMenu( netSubMenu.get(), SELECTION_CONDITIONS::OnlyTypes( connectedTypes ), 100 );
  81. return true;
  82. }
  83. void BOARD_INSPECTION_TOOL::Reset( RESET_REASON aReason )
  84. {
  85. m_frame = getEditFrame<PCB_EDIT_FRAME>();
  86. }
  87. int BOARD_INSPECTION_TOOL::ShowBoardStatistics( const TOOL_EVENT& aEvent )
  88. {
  89. DIALOG_BOARD_STATISTICS dialog( m_frame );
  90. dialog.ShowModal();
  91. return 0;
  92. }
  93. DRC_ENGINE BOARD_INSPECTION_TOOL::makeDRCEngine( bool* aCompileError, bool* aCourtyardError )
  94. {
  95. DRC_ENGINE engine( m_frame->GetBoard(), &m_frame->GetBoard()->GetDesignSettings() );
  96. try
  97. {
  98. engine.InitEngine( m_frame->GetDesignRulesPath() );
  99. }
  100. catch( PARSE_ERROR& )
  101. {
  102. if( aCompileError )
  103. *aCompileError = true;
  104. }
  105. for( ZONE* zone : m_frame->GetBoard()->Zones() )
  106. zone->CacheBoundingBox();
  107. for( FOOTPRINT* footprint : m_frame->GetBoard()->Footprints() )
  108. {
  109. for( ZONE* zone : footprint->Zones() )
  110. zone->CacheBoundingBox();
  111. footprint->BuildCourtyardCaches();
  112. if( aCourtyardError && ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
  113. *aCourtyardError = true;
  114. }
  115. return engine;
  116. }
  117. bool isNPTHPad( BOARD_ITEM* aItem )
  118. {
  119. return aItem->Type() == PCB_PAD_T
  120. && static_cast<PAD*>( aItem )->GetAttribute() == PAD_ATTRIB::NPTH;
  121. }
  122. wxString BOARD_INSPECTION_TOOL::getItemDescription( BOARD_ITEM* aItem )
  123. {
  124. // Null items have no description
  125. if( !aItem )
  126. return wxString();
  127. wxString msg = aItem->GetItemDescription( m_frame );
  128. if( aItem->IsConnected() && !isNPTHPad( aItem ) )
  129. {
  130. BOARD_CONNECTED_ITEM* cItem = static_cast<BOARD_CONNECTED_ITEM*>( aItem );
  131. msg += wxS( " " ) + wxString::Format( _( "[netclass %s]" ),
  132. cItem->GetEffectiveNetClass()->GetName() );
  133. }
  134. return msg;
  135. };
  136. void BOARD_INSPECTION_TOOL::reportCompileError( REPORTER* r )
  137. {
  138. r->Report( "" );
  139. r->Report( _( "Report incomplete: could not compile custom design rules. " )
  140. + wxT( "<a href='boardsetup'>" ) + _( "Show design rules." ) + wxT( "</a>" ) );
  141. }
  142. void BOARD_INSPECTION_TOOL::reportHeader( const wxString& aTitle, BOARD_ITEM* a, REPORTER* r )
  143. {
  144. r->Report( wxT( "<h7>" ) + EscapeHTML( aTitle ) + wxT( "</h7>" ) );
  145. r->Report( wxT( "<ul><li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li></ul>" ) );
  146. }
  147. void BOARD_INSPECTION_TOOL::reportHeader( const wxString& aTitle, BOARD_ITEM* a, BOARD_ITEM* b,
  148. REPORTER* r )
  149. {
  150. r->Report( wxT( "<h7>" ) + EscapeHTML( aTitle ) + wxT( "</h7>" ) );
  151. r->Report( wxT( "<ul><li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li>" )
  152. + wxT( "<li>" ) + EscapeHTML( getItemDescription( b ) ) + wxT( "</li></ul>" ) );
  153. }
  154. void BOARD_INSPECTION_TOOL::reportHeader( const wxString& aTitle, BOARD_ITEM* a, BOARD_ITEM* b,
  155. PCB_LAYER_ID aLayer, REPORTER* r )
  156. {
  157. wxString layerStr = _( "Layer" ) + wxS( " " ) + m_frame->GetBoard()->GetLayerName( aLayer );
  158. r->Report( wxT( "<h7>" ) + EscapeHTML( aTitle ) + wxT( "</h7>" ) );
  159. r->Report( wxT( "<ul><li>" ) + EscapeHTML( layerStr ) + wxT( "</li>" )
  160. + wxT( "<li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li>" )
  161. + wxT( "<li>" ) + EscapeHTML( getItemDescription( b ) ) + wxT( "</li></ul>" ) );
  162. }
  163. wxString reportMin( PCB_BASE_FRAME* aFrame, DRC_CONSTRAINT& aConstraint )
  164. {
  165. if( aConstraint.m_Value.HasMin() )
  166. return aFrame->StringFromValue( aConstraint.m_Value.Min(), true );
  167. else
  168. return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
  169. }
  170. wxString reportOpt( PCB_BASE_FRAME* aFrame, DRC_CONSTRAINT& aConstraint )
  171. {
  172. if( aConstraint.m_Value.HasOpt() )
  173. return aFrame->StringFromValue( aConstraint.m_Value.Opt(), true );
  174. else
  175. return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
  176. }
  177. wxString reportMax( PCB_BASE_FRAME* aFrame, DRC_CONSTRAINT& aConstraint )
  178. {
  179. if( aConstraint.m_Value.HasMax() )
  180. return aFrame->StringFromValue( aConstraint.m_Value.Max(), true );
  181. else
  182. return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
  183. }
  184. void BOARD_INSPECTION_TOOL::InspectDRCError( const std::shared_ptr<RC_ITEM>& aDRCItem )
  185. {
  186. BOARD_ITEM* a = m_frame->GetBoard()->GetItem( aDRCItem->GetMainItemID() );
  187. BOARD_ITEM* b = m_frame->GetBoard()->GetItem( aDRCItem->GetAuxItemID() );
  188. BOARD_CONNECTED_ITEM* ac = dynamic_cast<BOARD_CONNECTED_ITEM*>( a );
  189. BOARD_CONNECTED_ITEM* bc = dynamic_cast<BOARD_CONNECTED_ITEM*>( b );
  190. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  191. if( m_inspectClearanceDialog == nullptr )
  192. {
  193. m_inspectClearanceDialog = std::make_unique<DIALOG_CONSTRAINTS_REPORTER>( m_frame );
  194. m_inspectClearanceDialog->SetTitle( _( "Violation Report" ) );
  195. m_inspectClearanceDialog->Connect( wxEVT_CLOSE_WINDOW,
  196. wxCommandEventHandler( BOARD_INSPECTION_TOOL::onInspectClearanceDialogClosed ),
  197. nullptr, this );
  198. }
  199. WX_HTML_REPORT_BOX* r = nullptr;
  200. bool compileError = false;
  201. DRC_ENGINE drcEngine = makeDRCEngine( &compileError );
  202. DRC_CONSTRAINT constraint;
  203. int clearance = 0;
  204. wxString clearanceStr;
  205. switch( aDRCItem->GetErrorCode() )
  206. {
  207. case DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG:
  208. {
  209. for( KIID id : aDRCItem->GetIDs() )
  210. {
  211. bc = dynamic_cast<BOARD_CONNECTED_ITEM*>( m_frame->GetBoard()->GetItem( id ) );
  212. if( ac && bc && ac->GetNetCode() != bc->GetNetCode() )
  213. break;
  214. }
  215. r = m_inspectClearanceDialog->AddPage( _( "Uncoupled Length" ) );
  216. reportHeader( _( "Diff pair uncoupled length resolution for:" ), ac, bc, r );
  217. if( compileError )
  218. reportCompileError( r );
  219. constraint = drcEngine.EvalRules( DIFF_PAIR_MAX_UNCOUPLED_CONSTRAINT, a, b, layer, r );
  220. r->Report( "" );
  221. r->Report( wxString::Format( _( "Resolved max uncoupled length: %s." ),
  222. reportMax( m_frame, constraint ) ) );
  223. break;
  224. }
  225. case DRCE_TEXT_HEIGHT:
  226. r = m_inspectClearanceDialog->AddPage( _( "Text Height" ) );
  227. reportHeader( _( "Text height resolution for:" ), a, r );
  228. if( compileError )
  229. reportCompileError( r );
  230. constraint = drcEngine.EvalRules( TEXT_HEIGHT_CONSTRAINT, a, b, layer, r );
  231. r->Report( "" );
  232. r->Report( wxString::Format( _( "Resolved height constraints: min %s; max %s." ),
  233. reportMin( m_frame, constraint ),
  234. reportMax( m_frame, constraint ) ) );
  235. break;
  236. case DRCE_TEXT_THICKNESS:
  237. r = m_inspectClearanceDialog->AddPage( _( "Text Thickness" ) );
  238. reportHeader( _( "Text thickness resolution for:" ), a, r );
  239. if( compileError )
  240. reportCompileError( r );
  241. constraint = drcEngine.EvalRules( TEXT_THICKNESS_CONSTRAINT, a, b, layer, r );
  242. r->Report( "" );
  243. r->Report( wxString::Format( _( "Resolved thickness constraints: min %s; max %s." ),
  244. reportMin( m_frame, constraint ),
  245. reportMax( m_frame, constraint ) ) );
  246. break;
  247. case DRCE_TRACK_WIDTH:
  248. r = m_inspectClearanceDialog->AddPage( _( "Track Width" ) );
  249. reportHeader( _( "Track width resolution for:" ), a, r );
  250. if( compileError )
  251. reportCompileError( r );
  252. constraint = drcEngine.EvalRules( TRACK_WIDTH_CONSTRAINT, a, b, layer, r );
  253. r->Report( "" );
  254. r->Report( wxString::Format( _( "Resolved width constraints: min %s; max %s." ),
  255. reportMin( m_frame, constraint ),
  256. reportMax( m_frame, constraint ) ) );
  257. break;
  258. case DRCE_CONNECTION_WIDTH:
  259. r = m_inspectClearanceDialog->AddPage( _( "Connection Width" ) );
  260. reportHeader( _( "Connection width resolution for:" ), a, b, r );
  261. if( compileError )
  262. reportCompileError( r );
  263. constraint = drcEngine.EvalRules( CONNECTION_WIDTH_CONSTRAINT, a, b, layer, r );
  264. r->Report( "" );
  265. r->Report( wxString::Format( _( "Resolved min connection width constraint: %s." ),
  266. reportMin( m_frame, constraint ) ) );
  267. break;
  268. case DRCE_VIA_DIAMETER:
  269. r = m_inspectClearanceDialog->AddPage( _( "Via Diameter" ) );
  270. reportHeader( _( "Via diameter resolution for:" ), a, r );
  271. if( compileError )
  272. reportCompileError( r );
  273. constraint = drcEngine.EvalRules( VIA_DIAMETER_CONSTRAINT, a, b, layer, r );
  274. r->Report( "" );
  275. r->Report( wxString::Format( _( "Resolved diameter constraints: min %s; max %s." ),
  276. reportMin( m_frame, constraint ),
  277. reportMax( m_frame, constraint ) ) );
  278. break;
  279. case DRCE_ANNULAR_WIDTH:
  280. r = m_inspectClearanceDialog->AddPage( _( "Via Annulus" ) );
  281. reportHeader( _( "Via annular width resolution for:" ), a, r );
  282. if( compileError )
  283. reportCompileError( r );
  284. constraint = drcEngine.EvalRules( ANNULAR_WIDTH_CONSTRAINT, a, b, layer, r );
  285. r->Report( "" );
  286. r->Report( wxString::Format( _( "Resolved annular width constraints: min %s; max %s." ),
  287. reportMin( m_frame, constraint ),
  288. reportMax( m_frame, constraint ) ) );
  289. break;
  290. case DRCE_DRILL_OUT_OF_RANGE:
  291. case DRCE_MICROVIA_DRILL_OUT_OF_RANGE:
  292. r = m_inspectClearanceDialog->AddPage( _( "Hole Size" ) );
  293. reportHeader( _( "Hole diameter resolution for:" ), a, r );
  294. if( compileError )
  295. reportCompileError( r );
  296. constraint = drcEngine.EvalRules( HOLE_SIZE_CONSTRAINT, a, b, layer, r );
  297. r->Report( "" );
  298. r->Report( wxString::Format( _( "Resolved diameter constraints: min %s; max %s." ),
  299. reportMin( m_frame, constraint ),
  300. reportMax( m_frame, constraint ) ) );
  301. break;
  302. case DRCE_HOLE_CLEARANCE:
  303. r = m_inspectClearanceDialog->AddPage( _( "Hole Clearance" ) );
  304. reportHeader( _( "Hole clearance resolution for:" ), a, b, r );
  305. if( compileError )
  306. reportCompileError( r );
  307. if( ac && bc && ac->GetNetCode() == bc->GetNetCode() )
  308. {
  309. r->Report( "" );
  310. r->Report( _( "Items belong to the same net. Clearance is 0." ) );
  311. }
  312. else
  313. {
  314. constraint = drcEngine.EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
  315. clearance = constraint.m_Value.Min();
  316. clearanceStr = m_frame->StringFromValue( clearance, true );
  317. r->Report( "" );
  318. r->Report( wxString::Format( _( "Resolved clearance: %s." ), clearanceStr ) );
  319. }
  320. r->Report( "" );
  321. r->Report( "" );
  322. r->Report( "" );
  323. reportHeader( _( "Physical hole clearance resolution for:" ), a, b, layer, r );
  324. constraint = drcEngine.EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
  325. clearance = constraint.m_Value.Min();
  326. clearanceStr = m_frame->StringFromValue( clearance, true );
  327. if( !drcEngine.HasRulesForConstraintType( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT ) )
  328. {
  329. r->Report( "" );
  330. r->Report( _( "No 'physical_hole_clearance' constraints defined." ) );
  331. }
  332. else
  333. {
  334. r->Report( "" );
  335. r->Report( wxString::Format( _( "Resolved clearance: %s." ), clearanceStr ) );
  336. }
  337. break;
  338. case DRCE_DRILLED_HOLES_TOO_CLOSE:
  339. r = m_inspectClearanceDialog->AddPage( _( "Hole to Hole" ) );
  340. reportHeader( _( "Hole to hole clearance resolution for:" ), a, b, r );
  341. if( compileError )
  342. reportCompileError( r );
  343. constraint = drcEngine.EvalRules( HOLE_TO_HOLE_CONSTRAINT, a, b, UNDEFINED_LAYER, r );
  344. clearance = constraint.m_Value.Min();
  345. clearanceStr = m_frame->StringFromValue( clearance, true );
  346. r->Report( "" );
  347. r->Report( wxString::Format( _( "Resolved clearance: %s." ), clearanceStr ) );
  348. break;
  349. case DRCE_EDGE_CLEARANCE:
  350. r = m_inspectClearanceDialog->AddPage( _( "Edge Clearance" ) );
  351. reportHeader( _( "Edge clearance resolution for:" ), a, b, r );
  352. if( compileError )
  353. reportCompileError( r );
  354. constraint = drcEngine.EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, layer, r );
  355. clearance = constraint.m_Value.Min();
  356. clearanceStr = m_frame->StringFromValue( clearance, true );
  357. r->Report( "" );
  358. r->Report( wxString::Format( _( "Resolved clearance: %s." ), clearanceStr ) );
  359. break;
  360. case DRCE_CLEARANCE:
  361. if( a->Type() == PCB_TRACE_T || a->Type() == PCB_ARC_T )
  362. {
  363. layer = a->GetLayer();
  364. }
  365. else if( b->Type() == PCB_TRACE_T || b->Type() == PCB_ARC_T )
  366. {
  367. layer = b->GetLayer();
  368. }
  369. else if( a->Type() == PCB_PAD_T && static_cast<PAD*>( a )->GetAttribute() == PAD_ATTRIB::SMD )
  370. {
  371. PAD* pad = static_cast<PAD*>( a );
  372. if( pad->IsOnLayer( F_Cu ) )
  373. layer = F_Cu;
  374. else
  375. layer = B_Cu;
  376. }
  377. else if( b->Type() == PCB_PAD_T && static_cast<PAD*>( a )->GetAttribute() == PAD_ATTRIB::SMD )
  378. {
  379. PAD* pad = static_cast<PAD*>( b );
  380. if( pad->IsOnLayer( F_Cu ) )
  381. layer = F_Cu;
  382. else
  383. layer = B_Cu;
  384. }
  385. r = m_inspectClearanceDialog->AddPage( _( "Clearance" ) );
  386. reportHeader( _( "Clearance resolution for:" ), a, b, layer, r );
  387. if( compileError )
  388. reportCompileError( r );
  389. if( ac && bc && ac->GetNetCode() == bc->GetNetCode() )
  390. {
  391. r->Report( "" );
  392. r->Report( _( "Items belong to the same net. Clearance is 0." ) );
  393. }
  394. else
  395. {
  396. constraint = drcEngine.EvalRules( CLEARANCE_CONSTRAINT, a, a, layer, r );
  397. clearance = constraint.m_Value.Min();
  398. clearanceStr = m_frame->StringFromValue( clearance, true );
  399. r->Report( "" );
  400. r->Report( wxString::Format( _( "Resolved clearance: %s." ), clearanceStr ) );
  401. }
  402. r->Report( "" );
  403. r->Report( "" );
  404. r->Report( "" );
  405. reportHeader( _( "Physical clearance resolution for:" ), a, b, layer, r );
  406. constraint = drcEngine.EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, layer, r );
  407. clearance = constraint.m_Value.Min();
  408. clearanceStr = m_frame->StringFromValue( clearance, true );
  409. if( !drcEngine.HasRulesForConstraintType( PHYSICAL_CLEARANCE_CONSTRAINT ) )
  410. {
  411. r->Report( "" );
  412. r->Report( _( "No 'physical_clearance' constraints defined." ) );
  413. }
  414. else
  415. {
  416. r->Report( "" );
  417. r->Report( wxString::Format( _( "Resolved clearance: %s." ), clearanceStr ) );
  418. }
  419. break;
  420. default:
  421. return;
  422. }
  423. r->Flush();
  424. m_inspectClearanceDialog->Raise();
  425. m_inspectClearanceDialog->Show( true );
  426. }
  427. int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
  428. {
  429. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  430. const PCB_SELECTION& selection = selTool->GetSelection();
  431. if( selection.Size() != 2 )
  432. {
  433. m_frame->ShowInfoBarError( _( "Select two items for a clearance resolution report." ) );
  434. return 0;
  435. }
  436. BOARD_ITEM* a = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) );
  437. BOARD_ITEM* b = static_cast<BOARD_ITEM*>( selection.GetItem( 1 ) );
  438. wxCHECK( a && b, 0 );
  439. if( a->Type() == PCB_GROUP_T )
  440. {
  441. PCB_GROUP* ag = static_cast<PCB_GROUP*>( a );
  442. if( ag->GetItems().empty() )
  443. {
  444. m_frame->ShowInfoBarError( _( "Cannot generate clearance report on empty group." ) );
  445. return 0;
  446. }
  447. a = *ag->GetItems().begin();
  448. }
  449. if( b->Type() == PCB_GROUP_T )
  450. {
  451. PCB_GROUP* bg = static_cast<PCB_GROUP*>( b );
  452. if( bg->GetItems().empty() )
  453. {
  454. m_frame->ShowInfoBarError( _( "Cannot generate clearance report on empty group." ) );
  455. return 0;
  456. }
  457. b = *bg->GetItems().begin();
  458. }
  459. // a and b could be null after group tests above.
  460. wxCHECK( a && b, 0 );
  461. if( m_inspectClearanceDialog == nullptr )
  462. {
  463. m_inspectClearanceDialog = std::make_unique<DIALOG_CONSTRAINTS_REPORTER>( m_frame );
  464. m_inspectClearanceDialog->SetTitle( _( "Clearance Report" ) );
  465. m_inspectClearanceDialog->Connect( wxEVT_CLOSE_WINDOW,
  466. wxCommandEventHandler( BOARD_INSPECTION_TOOL::onInspectClearanceDialogClosed ),
  467. nullptr, this );
  468. }
  469. m_inspectClearanceDialog->DeleteAllPages();
  470. if( a->Type() != PCB_ZONE_T && b->Type() == PCB_ZONE_T )
  471. std::swap( a, b );
  472. else if( !a->IsConnected() && b->IsConnected() )
  473. std::swap( a, b );
  474. WX_HTML_REPORT_BOX* r = nullptr;
  475. PCB_LAYER_ID active = m_frame->GetActiveLayer();
  476. LSET layerIntersection = a->GetLayerSet() & b->GetLayerSet();
  477. LSET copperIntersection = layerIntersection & LSET::AllCuMask();
  478. BOARD_CONNECTED_ITEM* ac = dynamic_cast<BOARD_CONNECTED_ITEM*>( a );
  479. BOARD_CONNECTED_ITEM* bc = dynamic_cast<BOARD_CONNECTED_ITEM*>( b );
  480. ZONE* zone = dynamic_cast<ZONE*>( a );
  481. PAD* pad = dynamic_cast<PAD*>( b );
  482. FOOTPRINT* aFP = dynamic_cast<FOOTPRINT*>( a );
  483. FOOTPRINT* bFP = dynamic_cast<FOOTPRINT*>( b );
  484. bool compileError = false;
  485. DRC_ENGINE drcEngine = makeDRCEngine( &compileError );
  486. DRC_CONSTRAINT constraint;
  487. int clearance = 0;
  488. if( copperIntersection.any() && zone && pad && zone->GetNetCode() == pad->GetNetCode() )
  489. {
  490. PCB_LAYER_ID layer = UNDEFINED_LAYER;
  491. if( zone->IsOnLayer( active ) )
  492. layer = active;
  493. else if( zone->GetLayerSet().count() > 0 )
  494. layer = zone->GetLayerSet().Seq().front();
  495. r = m_inspectClearanceDialog->AddPage( _( "Zone" ) );
  496. reportHeader( _( "Zone connection resolution for:" ), a, b, layer, r );
  497. constraint = drcEngine.EvalZoneConnection( pad, zone, layer, r );
  498. if( constraint.m_ZoneConnection == ZONE_CONNECTION::THERMAL )
  499. {
  500. r->Report( "" );
  501. r->Report( "" );
  502. reportHeader( _( "Thermal relief gap resolution for:" ), a, b, layer, r );
  503. constraint = drcEngine.EvalRules( THERMAL_RELIEF_GAP_CONSTRAINT, pad, zone, layer, r );
  504. int gap = constraint.m_Value.Min();
  505. if( compileError )
  506. reportCompileError( r );
  507. r->Report( "" );
  508. r->Report( wxString::Format( _( "Resolved thermal relief gap: %s." ),
  509. m_frame->StringFromValue( gap, true ) ) );
  510. r->Report( "" );
  511. r->Report( "" );
  512. reportHeader( _( "Spoke width resolution for:" ), a, b, layer, r );
  513. constraint = drcEngine.EvalRules( THERMAL_SPOKE_WIDTH_CONSTRAINT, pad, zone, layer, r );
  514. int width = constraint.m_Value.Opt();
  515. if( compileError )
  516. reportCompileError( r );
  517. r->Report( "" );
  518. r->Report( wxString::Format( _( "Resolved thermal relief spoke width: %s." ),
  519. m_frame->StringFromValue( width, true ) ) );
  520. r->Report( "" );
  521. r->Report( "" );
  522. reportHeader( _( "Spoke count resolution for:" ), a, b, layer, r );
  523. constraint = drcEngine.EvalRules( MIN_RESOLVED_SPOKES_CONSTRAINT, pad, zone, layer, r );
  524. int minSpokes = constraint.m_Value.Min();
  525. if( compileError )
  526. reportCompileError( r );
  527. r->Report( "" );
  528. r->Report( wxString::Format( _( "Resolved min thermal relief spoke count: %d." ),
  529. minSpokes ) );
  530. std::shared_ptr<CONNECTIVITY_DATA> connectivity = pad->GetBoard()->GetConnectivity();
  531. }
  532. else if( constraint.m_ZoneConnection == ZONE_CONNECTION::NONE )
  533. {
  534. r->Report( "" );
  535. r->Report( "" );
  536. reportHeader( _( "Zone clearance resolution for:" ), a, b, layer, r );
  537. clearance = zone->GetLocalClearance();
  538. r->Report( "" );
  539. r->Report( wxString::Format( _( "Zone clearance: %s." ),
  540. m_frame->StringFromValue( clearance, true ) ) );
  541. constraint = drcEngine.EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, pad, zone, layer, r );
  542. if( constraint.m_Value.Min() > clearance )
  543. {
  544. clearance = constraint.m_Value.Min();
  545. r->Report( "" );
  546. r->Report( wxString::Format( _( "Overridden by larger physical clearance from %s;"
  547. "clearance: %s." ),
  548. EscapeHTML( constraint.GetName() ),
  549. m_frame->StringFromValue( clearance, true ) ) );
  550. }
  551. if( !pad->FlashLayer( layer ) )
  552. {
  553. constraint = drcEngine.EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, pad, zone,
  554. layer, r );
  555. if( constraint.m_Value.Min() > clearance )
  556. {
  557. clearance = constraint.m_Value.Min();
  558. r->Report( "" );
  559. r->Report( wxString::Format( _( "Overridden by larger physical hole clearance from %s;"
  560. "clearance: %s." ),
  561. EscapeHTML( constraint.GetName() ),
  562. m_frame->StringFromValue( clearance, true ) ) );
  563. }
  564. }
  565. if( compileError )
  566. reportCompileError( r );
  567. r->Report( "" );
  568. r->Report( wxString::Format( _( "Resolved clearance: %s." ),
  569. m_frame->StringFromValue( clearance, true ) ) );
  570. }
  571. else
  572. {
  573. r->Report( "" );
  574. r->Report( "" );
  575. reportHeader( _( "Zone clearance resolution for:" ), a, b, layer, r );
  576. if( compileError )
  577. reportCompileError( r );
  578. // Report a 0 clearance for solid connections
  579. r->Report( "" );
  580. r->Report( wxString::Format( _( "Resolved clearance: %s." ),
  581. m_frame->StringFromValue( 0, true ) ) );
  582. }
  583. r->Flush();
  584. }
  585. else if( copperIntersection.any() && !aFP && !bFP )
  586. {
  587. PCB_LAYER_ID layer = active;
  588. if( !copperIntersection.test( layer ) )
  589. layer = copperIntersection.Seq().front();
  590. r = m_inspectClearanceDialog->AddPage( m_frame->GetBoard()->GetLayerName( layer ) );
  591. reportHeader( _( "Clearance resolution for:" ), a, b, layer, r );
  592. if( ac && bc && ac->GetNetCode() > 0 && ac->GetNetCode() == bc->GetNetCode() )
  593. {
  594. // Same nets....
  595. r->Report( _( "Items belong to the same net. Clearance is 0." ) );
  596. }
  597. else
  598. {
  599. // Different nets (or one or both unconnected)....
  600. constraint = drcEngine.EvalRules( CLEARANCE_CONSTRAINT, a, b, layer, r );
  601. clearance = constraint.m_Value.Min();
  602. if( compileError )
  603. reportCompileError( r );
  604. r->Report( "" );
  605. if( clearance < 0 )
  606. {
  607. r->Report( wxString::Format( _( "Resolved clearance: %s; clearance will not be tested." ),
  608. m_frame->StringFromValue( clearance, true ) ) );
  609. }
  610. else
  611. {
  612. r->Report( wxString::Format( _( "Resolved clearance: %s." ),
  613. m_frame->StringFromValue( clearance, true ) ) );
  614. }
  615. }
  616. r->Flush();
  617. }
  618. if( ac && bc )
  619. {
  620. NETINFO_ITEM* refNet = ac->GetNet();
  621. wxString coupledNet;
  622. wxString dummy;
  623. if( DRC_ENGINE::MatchDpSuffix( refNet->GetNetname(), coupledNet, dummy )
  624. && bc->GetNetname() == coupledNet )
  625. {
  626. r = m_inspectClearanceDialog->AddPage( _( "Diff Pair" ) );
  627. reportHeader( _( "Diff pair gap resolution for:" ), ac, bc, active, r );
  628. constraint = drcEngine.EvalRules( DIFF_PAIR_GAP_CONSTRAINT, ac, bc, active, r );
  629. r->Report( "" );
  630. r->Report( wxString::Format( _( "Resolved gap constraints: min %s; opt %s; max %s." ),
  631. reportMin( m_frame, constraint ),
  632. reportOpt( m_frame, constraint ),
  633. reportMax( m_frame, constraint ) ) );
  634. r->Report( "" );
  635. r->Report( "" );
  636. r->Report( "" );
  637. reportHeader( _( "Diff pair max uncoupled length resolution for:" ), ac, bc, active, r );
  638. if( !drcEngine.HasRulesForConstraintType( DIFF_PAIR_MAX_UNCOUPLED_CONSTRAINT ) )
  639. {
  640. r->Report( "" );
  641. r->Report( _( "No 'diff_pair_uncoupled' constraints defined." ) );
  642. }
  643. else
  644. {
  645. constraint = drcEngine.EvalRules( DIFF_PAIR_MAX_UNCOUPLED_CONSTRAINT, ac, bc,
  646. active, r );
  647. r->Report( "" );
  648. r->Report( wxString::Format( _( "Resolved max uncoupled length: %s." ),
  649. reportMax( m_frame, constraint ) ) );
  650. }
  651. r->Flush();
  652. }
  653. }
  654. auto isOnCorrespondingLayer=
  655. [&]( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, wxString* aWarning )
  656. {
  657. if( aItem->IsOnLayer( aLayer ) )
  658. return true;
  659. PCB_LAYER_ID correspondingMask = IsFrontLayer( aLayer ) ? F_Mask : B_Mask;
  660. PCB_LAYER_ID correspondingCopper = IsFrontLayer( aLayer ) ? F_Cu : B_Cu;
  661. if( aItem->IsOnLayer( aLayer ) )
  662. return true;
  663. if( aItem->IsOnLayer( correspondingMask ) )
  664. return true;
  665. if( aItem->IsTented() && aItem->IsOnLayer( correspondingCopper ) )
  666. {
  667. *aWarning = wxString::Format( _( "Note: %s is tented; clearance will only be "
  668. "applied to holes." ),
  669. getItemDescription( aItem ) );
  670. return true;
  671. }
  672. return false;
  673. };
  674. for( PCB_LAYER_ID layer : { F_SilkS, B_SilkS } )
  675. {
  676. wxString warning;
  677. if( ( a->IsOnLayer( layer ) && isOnCorrespondingLayer( b, layer, &warning ) )
  678. || ( b->IsOnLayer( layer ) && isOnCorrespondingLayer( a, layer, &warning ) ) )
  679. {
  680. r = m_inspectClearanceDialog->AddPage( m_frame->GetBoard()->GetLayerName( layer ) );
  681. reportHeader( _( "Silkscreen clearance resolution for:" ), a, b, layer, r );
  682. constraint = drcEngine.EvalRules( SILK_CLEARANCE_CONSTRAINT, a, b, layer, r );
  683. clearance = constraint.m_Value.Min();
  684. if( compileError )
  685. reportCompileError( r );
  686. r->Report( "" );
  687. if( !warning.IsEmpty() )
  688. r->Report( warning );
  689. r->Report( wxString::Format( _( "Resolved clearance: %s." ),
  690. m_frame->StringFromValue( clearance, true ) ) );
  691. r->Flush();
  692. }
  693. }
  694. for( PCB_LAYER_ID layer : { F_CrtYd, B_CrtYd } )
  695. {
  696. bool aCourtyard = aFP && !aFP->GetCourtyard( layer ).IsEmpty();
  697. bool bCourtyard = bFP && !bFP->GetCourtyard( layer ).IsEmpty();
  698. if( aCourtyard && bCourtyard )
  699. {
  700. r = m_inspectClearanceDialog->AddPage( m_frame->GetBoard()->GetLayerName( layer ) );
  701. reportHeader( _( "Courtyard clearance resolution for:" ), a, b, layer, r );
  702. constraint = drcEngine.EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, a, b, layer, r );
  703. clearance = constraint.m_Value.Min();
  704. if( compileError )
  705. reportCompileError( r );
  706. r->Report( "" );
  707. r->Report( wxString::Format( _( "Resolved clearance: %s." ),
  708. m_frame->StringFromValue( clearance, true ) ) );
  709. r->Flush();
  710. }
  711. }
  712. if( a->HasHole() || b->HasHole() )
  713. {
  714. PCB_LAYER_ID layer = UNDEFINED_LAYER;
  715. bool pageAdded = false;
  716. if( a->HasHole() && b->IsOnLayer( active ) && IsCopperLayer( active ) )
  717. layer = active;
  718. else if( b->HasHole() && a->IsOnLayer( active ) && IsCopperLayer( active ) )
  719. layer = active;
  720. else if( a->HasHole() && b->IsOnCopperLayer() )
  721. layer = b->GetLayer();
  722. else if( b->HasHole() && b->IsOnCopperLayer() )
  723. layer = a->GetLayer();
  724. if( layer >= 0 )
  725. {
  726. if( !pageAdded )
  727. {
  728. r = m_inspectClearanceDialog->AddPage( _( "Hole" ) );
  729. pageAdded = true;
  730. }
  731. else
  732. {
  733. r->Report( "" );
  734. r->Report( "" );
  735. r->Report( "" );
  736. }
  737. reportHeader( _( "Hole clearance resolution for:" ), a, b, layer, r );
  738. constraint = drcEngine.EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
  739. clearance = constraint.m_Value.Min();
  740. if( compileError )
  741. reportCompileError( r );
  742. r->Report( "" );
  743. r->Report( wxString::Format( _( "Resolved clearance: %s." ),
  744. m_frame->StringFromValue( clearance, true ) ) );
  745. r->Flush();
  746. }
  747. if( a->HasHole() && b->HasHole() )
  748. {
  749. if( !pageAdded )
  750. {
  751. r = m_inspectClearanceDialog->AddPage( _( "Hole" ) );
  752. pageAdded = true;
  753. }
  754. else
  755. {
  756. r->Report( "" );
  757. r->Report( "" );
  758. r->Report( "" );
  759. }
  760. reportHeader( _( "Hole to hole clearance resolution for:" ), a, b, r );
  761. constraint = drcEngine.EvalRules( HOLE_TO_HOLE_CONSTRAINT, a, b, UNDEFINED_LAYER, r );
  762. clearance = constraint.m_Value.Min();
  763. if( compileError )
  764. reportCompileError( r );
  765. r->Report( "" );
  766. r->Report( wxString::Format( _( "Resolved clearance: %s." ),
  767. m_frame->StringFromValue( clearance, true ) ) );
  768. r->Flush();
  769. }
  770. }
  771. for( PCB_LAYER_ID edgeLayer : { Edge_Cuts, Margin } )
  772. {
  773. PCB_LAYER_ID layer = UNDEFINED_LAYER;
  774. if( a->IsOnLayer( edgeLayer ) && b->Type() != PCB_FOOTPRINT_T )
  775. {
  776. if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
  777. layer = active;
  778. else if( IsCopperLayer( b->GetLayer() ) )
  779. layer = b->GetLayer();
  780. }
  781. else if( b->IsOnLayer( edgeLayer ) && a->Type() != PCB_FOOTPRINT_T )
  782. {
  783. if( a->IsOnLayer( active ) && IsCopperLayer( active ) )
  784. layer = active;
  785. else if( IsCopperLayer( a->GetLayer() ) )
  786. layer = a->GetLayer();
  787. }
  788. if( layer >= 0 )
  789. {
  790. wxString layerName = m_frame->GetBoard()->GetLayerName( edgeLayer );
  791. r = m_inspectClearanceDialog->AddPage( layerName + wxS( " " ) + _( "Clearance" ) );
  792. reportHeader( _( "Edge clearance resolution for:" ), a, b, layer, r );
  793. constraint = drcEngine.EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, layer, r );
  794. clearance = constraint.m_Value.Min();
  795. if( compileError )
  796. reportCompileError( r );
  797. r->Report( "" );
  798. r->Report( wxString::Format( _( "Resolved clearance: %s." ),
  799. m_frame->StringFromValue( clearance, true ) ) );
  800. r->Flush();
  801. }
  802. }
  803. r = m_inspectClearanceDialog->AddPage( _( "Physical Clearances" ) );
  804. auto reportPhysicalClearance =
  805. [&]( PCB_LAYER_ID aLayer )
  806. {
  807. reportHeader( _( "Physical clearance resolution for:" ), a, b, aLayer, r );
  808. constraint = drcEngine.EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, aLayer, r );
  809. clearance = constraint.m_Value.Min();
  810. if( compileError )
  811. {
  812. reportCompileError( r );
  813. }
  814. else if( !drcEngine.HasRulesForConstraintType( PHYSICAL_CLEARANCE_CONSTRAINT ) )
  815. {
  816. r->Report( "" );
  817. r->Report( _( "No 'physical_clearance' constraints defined." ) );
  818. }
  819. else
  820. {
  821. r->Report( "" );
  822. r->Report( wxString::Format( _( "Resolved clearance: %s." ),
  823. m_frame->StringFromValue( clearance, true ) ) );
  824. }
  825. r->Report( "" );
  826. r->Report( "" );
  827. r->Report( "" );
  828. };
  829. if( layerIntersection.any() )
  830. {
  831. PCB_LAYER_ID layer = active;
  832. if( !layerIntersection.test( layer ) )
  833. layer = layerIntersection.Seq().front();
  834. reportPhysicalClearance( layer );
  835. }
  836. if( aFP && b->IsOnLayer( Edge_Cuts ) )
  837. {
  838. if( !aFP->GetCourtyard( F_CrtYd ).IsEmpty() )
  839. reportPhysicalClearance( F_CrtYd );
  840. if( !aFP->GetCourtyard( B_CrtYd ).IsEmpty() )
  841. reportPhysicalClearance( B_CrtYd );
  842. }
  843. else if( bFP && a->IsOnLayer( Edge_Cuts ) )
  844. {
  845. if( !bFP->GetCourtyard( F_CrtYd ).IsEmpty() )
  846. reportPhysicalClearance( F_CrtYd );
  847. if( !bFP->GetCourtyard( B_CrtYd ).IsEmpty() )
  848. reportPhysicalClearance( B_CrtYd );
  849. }
  850. if( a->HasHole() || b->HasHole() )
  851. {
  852. PCB_LAYER_ID layer;
  853. if( a->HasHole() && b->IsOnLayer( active ) )
  854. layer = active;
  855. else if( b->HasHole() && a->IsOnLayer( active ) )
  856. layer = active;
  857. else if( a->HasHole() )
  858. layer = b->GetLayer();
  859. else
  860. layer = a->GetLayer();
  861. reportHeader( _( "Physical hole clearance resolution for:" ), a, b, layer, r );
  862. constraint = drcEngine.EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
  863. clearance = constraint.m_Value.Min();
  864. if( compileError )
  865. {
  866. reportCompileError( r );
  867. }
  868. else if( !drcEngine.HasRulesForConstraintType( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT ) )
  869. {
  870. r->Report( "" );
  871. r->Report( _( "No 'physical_hole_clearance' constraints defined." ) );
  872. }
  873. else
  874. {
  875. r->Report( "" );
  876. r->Report( wxString::Format( _( "Resolved clearance: %s." ),
  877. m_frame->StringFromValue( clearance, true ) ) );
  878. }
  879. }
  880. r->Flush();
  881. m_inspectClearanceDialog->Raise();
  882. m_inspectClearanceDialog->Show( true );
  883. return 0;
  884. }
  885. int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
  886. {
  887. #define EVAL_RULES( constraint, a, b, layer, r ) drcEngine.EvalRules( constraint, a, b, layer, r )
  888. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  889. const PCB_SELECTION& selection = selTool->GetSelection();
  890. if( selection.Size() != 1 )
  891. {
  892. m_frame->ShowInfoBarError( _( "Select an item for a constraints resolution report." ) );
  893. return 0;
  894. }
  895. if( m_inspectConstraintsDialog == nullptr )
  896. {
  897. m_inspectConstraintsDialog = std::make_unique<DIALOG_CONSTRAINTS_REPORTER>( m_frame );
  898. m_inspectConstraintsDialog->SetTitle( _( "Constraints Report" ) );
  899. m_inspectConstraintsDialog->Connect( wxEVT_CLOSE_WINDOW,
  900. wxCommandEventHandler( BOARD_INSPECTION_TOOL::onInspectConstraintsDialogClosed ),
  901. nullptr, this );
  902. }
  903. m_inspectConstraintsDialog->DeleteAllPages();
  904. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) );
  905. bool compileError = false;
  906. bool courtyardError = false;
  907. DRC_ENGINE drcEngine = makeDRCEngine( &compileError, &courtyardError );
  908. DRC_CONSTRAINT constraint;
  909. WX_HTML_REPORT_BOX* r = nullptr;
  910. if( item->Type() == PCB_TRACE_T )
  911. {
  912. r = m_inspectConstraintsDialog->AddPage( _( "Track Width" ) );
  913. reportHeader( _( "Track width resolution for:" ), item, r );
  914. constraint = EVAL_RULES( TRACK_WIDTH_CONSTRAINT, item, nullptr, item->GetLayer(), r );
  915. if( compileError )
  916. reportCompileError( r );
  917. r->Report( "" );
  918. r->Report( wxString::Format( _( "Width constraints: min %s; opt %s; max %s." ),
  919. reportMin( m_frame, constraint ),
  920. reportOpt( m_frame, constraint ),
  921. reportMax( m_frame, constraint ) ) );
  922. r->Flush();
  923. }
  924. if( item->Type() == PCB_VIA_T )
  925. {
  926. r = m_inspectConstraintsDialog->AddPage( _( "Via Diameter" ) );
  927. reportHeader( _( "Via diameter resolution for:" ), item, r );
  928. // PADSTACKS TODO: once we have padstacks we'll need to run this per-layer....
  929. constraint = EVAL_RULES( VIA_DIAMETER_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
  930. if( compileError )
  931. reportCompileError( r );
  932. r->Report( "" );
  933. r->Report( wxString::Format( _( "Diameter constraints: min %s; opt %s; max %s." ),
  934. reportMin( m_frame, constraint ),
  935. reportOpt( m_frame, constraint ),
  936. reportMax( m_frame, constraint ) ) );
  937. r->Flush();
  938. r = m_inspectConstraintsDialog->AddPage( _( "Via Annular Width" ) );
  939. reportHeader( _( "Via annular width resolution for:" ), item, r );
  940. // PADSTACKS TODO: once we have padstacks we'll need to run this per-layer....
  941. constraint = EVAL_RULES( ANNULAR_WIDTH_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
  942. if( compileError )
  943. reportCompileError( r );
  944. r->Report( "" );
  945. r->Report( wxString::Format( _( "Annular width constraints: min %s; opt %s; max %s." ),
  946. reportMin( m_frame, constraint ),
  947. reportOpt( m_frame, constraint ),
  948. reportMax( m_frame, constraint ) ) );
  949. r->Flush();
  950. }
  951. if( ( item->Type() == PCB_PAD_T && static_cast<PAD*>( item )->GetDrillSize().x > 0 )
  952. || item->Type() == PCB_VIA_T )
  953. {
  954. r = m_inspectConstraintsDialog->AddPage( _( "Hole Size" ) );
  955. reportHeader( _( "Hole diameter resolution for:" ), item, r );
  956. // PADSTACKS TODO: once we have padstacks we'll need to run this per-layer....
  957. constraint = EVAL_RULES( HOLE_SIZE_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
  958. if( compileError )
  959. reportCompileError( r );
  960. r->Report( "" );
  961. r->Report( wxString::Format( _( "Diameter constraints: min %s; opt %s; max %s." ),
  962. reportMin( m_frame, constraint ),
  963. reportOpt( m_frame, constraint ),
  964. reportMax( m_frame, constraint ) ) );
  965. r->Flush();
  966. }
  967. if( item->Type() == PCB_TEXT_T
  968. || item->Type() == PCB_TEXTBOX_T
  969. || item->Type() == PCB_FP_TEXT_T )
  970. {
  971. r = m_inspectConstraintsDialog->AddPage( _( "Text Size" ) );
  972. reportHeader( _( "Text height resolution for:" ), item, r );
  973. constraint = EVAL_RULES( TEXT_HEIGHT_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
  974. if( compileError )
  975. reportCompileError( r );
  976. r->Report( "" );
  977. r->Report( wxString::Format( _( "Text height constraints: min %s; opt %s; max %s." ),
  978. reportMin( m_frame, constraint ),
  979. reportOpt( m_frame, constraint ),
  980. reportMax( m_frame, constraint ) ) );
  981. r->Report( "" );
  982. r->Report( "" );
  983. r->Report( "" );
  984. reportHeader( _( "Text thickness resolution for:" ), item, r );
  985. constraint = EVAL_RULES( TEXT_THICKNESS_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
  986. if( compileError )
  987. reportCompileError( r );
  988. r->Report( "" );
  989. r->Report( wxString::Format( _( "Text thickness constraints: min %s; opt %s; max %s." ),
  990. reportMin( m_frame, constraint ),
  991. reportOpt( m_frame, constraint ),
  992. reportMax( m_frame, constraint ) ) );
  993. r->Flush();
  994. }
  995. r = m_inspectConstraintsDialog->AddPage( _( "Keepouts" ) );
  996. reportHeader( _( "Keepout resolution for:" ), item, r );
  997. constraint = EVAL_RULES( DISALLOW_CONSTRAINT, item, nullptr, item->GetLayer(), r );
  998. if( compileError )
  999. reportCompileError( r );
  1000. if( courtyardError )
  1001. {
  1002. r->Report( "" );
  1003. r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
  1004. + wxT( " <a href='drc'>" ) + _( "Run DRC for a full analysis." ) + wxT( "</a>" ) );
  1005. }
  1006. r->Report( "" );
  1007. if( constraint.m_DisallowFlags )
  1008. r->Report( _( "Item <b>disallowed</b> at current location." ) );
  1009. else
  1010. r->Report( _( "Item allowed at current location." ) );
  1011. r->Flush();
  1012. r = m_inspectConstraintsDialog->AddPage( _( "Assertions" ) );
  1013. reportHeader( _( "Assertions for:" ), item, r );
  1014. if( compileError )
  1015. reportCompileError( r );
  1016. if( courtyardError )
  1017. {
  1018. r->Report( "" );
  1019. r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
  1020. + wxT( " <a href='drc'>" ) + _( "Run DRC for a full analysis." ) + wxT( "</a>" ) );
  1021. }
  1022. drcEngine.ProcessAssertions( item, []( const DRC_CONSTRAINT* c ){}, r );
  1023. r->Flush();
  1024. m_inspectConstraintsDialog->FinishInitialization();
  1025. m_inspectConstraintsDialog->Raise();
  1026. m_inspectConstraintsDialog->Show( true );
  1027. return 0;
  1028. }
  1029. int BOARD_INSPECTION_TOOL::InspectLibraryDiff( const TOOL_EVENT& aEvent )
  1030. {
  1031. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  1032. const PCB_SELECTION& selection = selTool->RequestSelection(
  1033. []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
  1034. {
  1035. // Iterate from the back so we don't have to worry about removals.
  1036. for( int i = aCollector.GetCount() - 1; i >= 0; --i )
  1037. {
  1038. BOARD_ITEM* item = aCollector[ i ];
  1039. if( !dynamic_cast<FOOTPRINT*>( item ) )
  1040. aCollector.Remove( item );
  1041. }
  1042. },
  1043. false /* ignore locked flag */ );
  1044. if( selection.Size() != 1 )
  1045. {
  1046. m_frame->ShowInfoBarError( _( "Select a footprint to check against its library equivalent." ) );
  1047. return 0;
  1048. }
  1049. if( m_inspectConstraintsDialog == nullptr )
  1050. {
  1051. m_inspectConstraintsDialog = std::make_unique<DIALOG_CONSTRAINTS_REPORTER>( m_frame );
  1052. m_inspectConstraintsDialog->SetTitle( _( "Check Footprint against Library" ) );
  1053. m_inspectConstraintsDialog->Connect( wxEVT_CLOSE_WINDOW,
  1054. wxCommandEventHandler( BOARD_INSPECTION_TOOL::onInspectConstraintsDialogClosed ),
  1055. nullptr, this );
  1056. }
  1057. m_inspectConstraintsDialog->DeleteAllPages();
  1058. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( selection.GetItem( 0 ) );
  1059. LIB_ID fpID = footprint->GetFPID();
  1060. wxString libName = fpID.GetLibNickname();
  1061. wxString fpName = fpID.GetLibItemName();
  1062. WX_HTML_REPORT_BOX* r = nullptr;
  1063. r = m_inspectConstraintsDialog->AddPage( _( "Diff" ) );
  1064. r->Report( wxT( "<h7>" ) + _( "Board/library check for:" ) + wxT( "</h7>" ) );
  1065. r->Report( wxT( "<ul><li>" ) + EscapeHTML( getItemDescription( footprint ) ) + wxT( "</li>" )
  1066. + wxT( "<li>" ) + _( "Library: " ) + EscapeHTML( libName ) + wxT( "</li>" )
  1067. + wxT( "<li>" ) + _( "Library item: " ) + EscapeHTML( fpName ) + wxT( "</li></ul>" ) );
  1068. r->Report( "" );
  1069. PROJECT* project = footprint->GetBoard()->GetProject();
  1070. FP_LIB_TABLE* libTable = project->PcbFootprintLibs();
  1071. const LIB_TABLE_ROW* libTableRow = nullptr;
  1072. try
  1073. {
  1074. libTableRow = libTable->FindRow( libName );
  1075. }
  1076. catch( const IO_ERROR& )
  1077. {
  1078. }
  1079. if( !libTableRow )
  1080. {
  1081. r->Report( _( "The current configuration does not include the library." ) );
  1082. }
  1083. else if( !libTable->HasLibrary( libName, true ) )
  1084. {
  1085. r->Report( _( "The library is not enabled in the current configuration." ) );
  1086. }
  1087. else
  1088. {
  1089. std::shared_ptr<FOOTPRINT> libFootprint;
  1090. try
  1091. {
  1092. libFootprint.reset( libTable->FootprintLoad( libName, fpName, true ) );
  1093. }
  1094. catch( const IO_ERROR& )
  1095. {
  1096. }
  1097. if( !libFootprint )
  1098. {
  1099. r->Report( wxString::Format( _( "The library no longer contains the item %s." ),
  1100. fpName) );
  1101. }
  1102. else if( !footprint->FootprintNeedsUpdate( libFootprint.get(), r ) )
  1103. {
  1104. r->Report( _( "Footprint matches library equivalent." ) );
  1105. }
  1106. }
  1107. r->Flush();
  1108. m_inspectConstraintsDialog->FinishInitialization();
  1109. m_inspectConstraintsDialog->Raise();
  1110. m_inspectConstraintsDialog->Show( true );
  1111. return 0;
  1112. }
  1113. int BOARD_INSPECTION_TOOL::HighlightItem( const TOOL_EVENT& aEvent )
  1114. {
  1115. BOARD_ITEM* item = aEvent.Parameter<BOARD_ITEM*>();
  1116. m_frame->m_probingSchToPcb = true; // recursion guard
  1117. {
  1118. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1119. if( item )
  1120. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, (void*) item );
  1121. }
  1122. m_frame->m_probingSchToPcb = false;
  1123. bool request3DviewRedraw = frame()->GetPcbNewSettings()->m_Display.m_Live3DRefresh;
  1124. if( item && item->Type() != PCB_FOOTPRINT_T )
  1125. request3DviewRedraw = false;
  1126. // Update 3D viewer highlighting
  1127. if( request3DviewRedraw )
  1128. m_frame->Update3DView( false, true );
  1129. return 0;
  1130. }
  1131. bool BOARD_INSPECTION_TOOL::highlightNet( const VECTOR2D& aPosition, bool aUseSelection )
  1132. {
  1133. BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
  1134. KIGFX::RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
  1135. PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  1136. int net = -1;
  1137. bool enableHighlight = false;
  1138. if( aUseSelection )
  1139. {
  1140. const PCB_SELECTION& selection = selectionTool->GetSelection();
  1141. std::set<int> netcodes;
  1142. for( EDA_ITEM* item : selection )
  1143. {
  1144. if( BOARD_CONNECTED_ITEM* ci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
  1145. netcodes.insert( ci->GetNetCode() );
  1146. }
  1147. enableHighlight = !netcodes.empty();
  1148. if( enableHighlight && netcodes.size() > 1 )
  1149. {
  1150. // If we are doing a multi-highlight, cross-probing back and other stuff is not
  1151. // yet supported
  1152. settings->SetHighlight( netcodes );
  1153. board->ResetNetHighLight();
  1154. for( int multiNet : netcodes )
  1155. board->SetHighLightNet( multiNet, true );
  1156. board->HighLightON();
  1157. m_toolMgr->GetView()->UpdateAllLayersColor();
  1158. m_currentlyHighlighted = netcodes;
  1159. return true;
  1160. }
  1161. else if( enableHighlight )
  1162. {
  1163. net = *netcodes.begin();
  1164. }
  1165. }
  1166. // If we didn't get a net to highlight from the selection, use the cursor
  1167. if( net < 0 )
  1168. {
  1169. GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
  1170. guide.SetIgnoreZoneFills( false );
  1171. PCB_LAYER_ID activeLayer = static_cast<PCB_LAYER_ID>( view()->GetTopLayer() );
  1172. guide.SetPreferredLayer( activeLayer );
  1173. GENERAL_COLLECTOR collector;
  1174. collector.Collect( board, { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T }, aPosition,
  1175. guide );
  1176. if( collector.GetCount() == 0 )
  1177. collector.Collect( board, { PCB_ZONE_T, PCB_FP_ZONE_T }, aPosition, guide );
  1178. // Apply the active selection filter, except we want to allow picking locked items for
  1179. // highlighting even if the user has disabled them for selection
  1180. SELECTION_FILTER_OPTIONS& filter = selectionTool->GetFilter();
  1181. bool saved = filter.lockedItems;
  1182. filter.lockedItems = true;
  1183. selectionTool->FilterCollectedItems( collector, true );
  1184. filter.lockedItems = saved;
  1185. // Clear the previous highlight
  1186. //m_frame->SendMessageToEESCHEMA( nullptr );
  1187. bool highContrast = settings->GetHighContrast();
  1188. PCB_LAYER_ID contrastLayer = settings->GetPrimaryHighContrastLayer();
  1189. for( int i = collector.GetCount() - 1; i >= 0; i-- )
  1190. {
  1191. LSET itemLayers = collector[i]->GetLayerSet();
  1192. if( ( itemLayers & LSET::AllCuMask() ).none() ||
  1193. ( highContrast && !itemLayers.Contains( contrastLayer ) ) )
  1194. {
  1195. collector.Remove( i );
  1196. continue;
  1197. }
  1198. }
  1199. enableHighlight = ( collector.GetCount() > 0 );
  1200. // Obtain net code for the clicked item
  1201. if( enableHighlight )
  1202. {
  1203. BOARD_CONNECTED_ITEM* targetItem = static_cast<BOARD_CONNECTED_ITEM*>( collector[0] );
  1204. if( targetItem->Type() == PCB_PAD_T )
  1205. m_frame->SendCrossProbeItem( targetItem );
  1206. net = targetItem->GetNetCode();
  1207. }
  1208. }
  1209. const std::set<int>& netcodes = settings->GetHighlightNetCodes();
  1210. // Toggle highlight when the same net was picked
  1211. if( netcodes.count( net ) )
  1212. enableHighlight = !settings->IsHighlightEnabled();
  1213. if( enableHighlight != settings->IsHighlightEnabled() || !netcodes.count( net ) )
  1214. {
  1215. if( !netcodes.empty() )
  1216. m_lastHighlighted = netcodes;
  1217. settings->SetHighlight( enableHighlight, net );
  1218. m_toolMgr->GetView()->UpdateAllLayersColor();
  1219. }
  1220. // Store the highlighted netcode in the current board (for dialogs for instance)
  1221. if( enableHighlight && net >= 0 )
  1222. {
  1223. m_currentlyHighlighted = netcodes;
  1224. board->SetHighLightNet( net );
  1225. board->HighLightON();
  1226. NETINFO_ITEM* netinfo = board->FindNet( net );
  1227. if( netinfo )
  1228. {
  1229. std::vector<MSG_PANEL_ITEM> items;
  1230. netinfo->GetMsgPanelInfo( m_frame, items );
  1231. m_frame->SetMsgPanel( items );
  1232. m_frame->SendCrossProbeNetName( netinfo->GetNetname() );
  1233. }
  1234. }
  1235. else
  1236. {
  1237. m_currentlyHighlighted.clear();
  1238. board->ResetNetHighLight();
  1239. m_frame->SetMsgPanel( board );
  1240. m_frame->SendCrossProbeNetName( "" );
  1241. }
  1242. return true;
  1243. }
  1244. int BOARD_INSPECTION_TOOL::HighlightNet( const TOOL_EVENT& aEvent )
  1245. {
  1246. int netcode = aEvent.Parameter<intptr_t>();
  1247. KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings();
  1248. const std::set<int>& highlighted = settings->GetHighlightNetCodes();
  1249. if( netcode > 0 )
  1250. {
  1251. m_lastHighlighted = highlighted;
  1252. settings->SetHighlight( true, netcode );
  1253. m_toolMgr->GetView()->UpdateAllLayersColor();
  1254. m_currentlyHighlighted.clear();
  1255. m_currentlyHighlighted.insert( netcode );
  1256. }
  1257. else if( aEvent.IsAction( &PCB_ACTIONS::highlightNetSelection ) )
  1258. {
  1259. // Highlight selection (cursor position will be ignored)
  1260. highlightNet( getViewControls()->GetMousePosition(), true );
  1261. }
  1262. else if( aEvent.IsAction( &PCB_ACTIONS::toggleLastNetHighlight ) )
  1263. {
  1264. std::set<int> temp = highlighted;
  1265. settings->SetHighlight( m_lastHighlighted );
  1266. m_toolMgr->GetView()->UpdateAllLayersColor();
  1267. m_currentlyHighlighted = m_lastHighlighted;
  1268. m_lastHighlighted = temp;
  1269. }
  1270. else if( aEvent.IsAction( &PCB_ACTIONS::toggleNetHighlight ) )
  1271. {
  1272. bool turnOn = highlighted.empty() && !m_currentlyHighlighted.empty();
  1273. settings->SetHighlight( m_currentlyHighlighted, turnOn );
  1274. m_toolMgr->GetView()->UpdateAllLayersColor();
  1275. }
  1276. else // Highlight the net belonging to the item under the cursor
  1277. {
  1278. highlightNet( getViewControls()->GetMousePosition(), false );
  1279. }
  1280. return 0;
  1281. }
  1282. int BOARD_INSPECTION_TOOL::ClearHighlight( const TOOL_EVENT& aEvent )
  1283. {
  1284. BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
  1285. KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings();
  1286. m_currentlyHighlighted.clear();
  1287. m_lastHighlighted.clear();
  1288. board->ResetNetHighLight();
  1289. settings->SetHighlight( false );
  1290. m_toolMgr->GetView()->UpdateAllLayersColor();
  1291. m_frame->SetMsgPanel( board );
  1292. m_frame->SendCrossProbeNetName( "" );
  1293. return 0;
  1294. }
  1295. int BOARD_INSPECTION_TOOL::LocalRatsnestTool( const TOOL_EVENT& aEvent )
  1296. {
  1297. PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
  1298. BOARD* board = getModel<BOARD>();
  1299. // Deactivate other tools; particularly important if another PICKER is currently running
  1300. Activate();
  1301. picker->SetCursor( KICURSOR::BULLSEYE );
  1302. picker->SetClickHandler(
  1303. [this, board]( const VECTOR2D& pt ) -> bool
  1304. {
  1305. PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  1306. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1307. m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true, EDIT_TOOL::PadFilter );
  1308. PCB_SELECTION& selection = selectionTool->GetSelection();
  1309. if( selection.Empty() )
  1310. {
  1311. m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true,
  1312. EDIT_TOOL::FootprintFilter );
  1313. selection = selectionTool->GetSelection();
  1314. }
  1315. if( selection.Empty() )
  1316. {
  1317. // Clear the previous local ratsnest if we click off all items
  1318. for( FOOTPRINT* fp : board->Footprints() )
  1319. {
  1320. for( PAD* pad : fp->Pads() )
  1321. pad->SetLocalRatsnestVisible( displayOptions().m_ShowGlobalRatsnest );
  1322. }
  1323. }
  1324. else
  1325. {
  1326. for( EDA_ITEM* item : selection )
  1327. {
  1328. if( PAD* pad = dyn_cast<PAD*>( item) )
  1329. {
  1330. pad->SetLocalRatsnestVisible( !pad->GetLocalRatsnestVisible() );
  1331. }
  1332. else if( FOOTPRINT* fp = dyn_cast<FOOTPRINT*>( item) )
  1333. {
  1334. if( !fp->Pads().empty() )
  1335. {
  1336. bool enable = !fp->Pads()[0]->GetLocalRatsnestVisible();
  1337. for( PAD* childPad : fp->Pads() )
  1338. childPad->SetLocalRatsnestVisible( enable );
  1339. }
  1340. }
  1341. }
  1342. }
  1343. m_toolMgr->GetView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
  1344. return true;
  1345. } );
  1346. picker->SetFinalizeHandler(
  1347. [this, board]( int aCondition )
  1348. {
  1349. if( aCondition != PCB_PICKER_TOOL::END_ACTIVATE )
  1350. {
  1351. for( FOOTPRINT* fp : board->Footprints() )
  1352. {
  1353. for( PAD* pad : fp->Pads() )
  1354. pad->SetLocalRatsnestVisible( displayOptions().m_ShowGlobalRatsnest );
  1355. }
  1356. }
  1357. } );
  1358. m_toolMgr->RunAction( ACTIONS::pickerTool, true );
  1359. return 0;
  1360. }
  1361. int BOARD_INSPECTION_TOOL::UpdateLocalRatsnest( const TOOL_EVENT& aEvent )
  1362. {
  1363. VECTOR2I delta;
  1364. // If we have passed the simple move vector, we can update without recalculation
  1365. if( aEvent.Parameter<VECTOR2I*>() )
  1366. {
  1367. delta = *aEvent.Parameter<VECTOR2I*>();
  1368. delete aEvent.Parameter<VECTOR2I*>();
  1369. }
  1370. else
  1371. {
  1372. // We can delete the existing map to force a recalculation
  1373. delete m_dynamicData;
  1374. m_dynamicData = nullptr;
  1375. }
  1376. auto selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  1377. auto& selection = selectionTool->GetSelection();
  1378. auto connectivity = getModel<BOARD>()->GetConnectivity();
  1379. if( selection.Empty() )
  1380. {
  1381. connectivity->ClearLocalRatsnest();
  1382. delete m_dynamicData;
  1383. m_dynamicData = nullptr;
  1384. }
  1385. else
  1386. {
  1387. calculateSelectionRatsnest( delta );
  1388. }
  1389. return 0;
  1390. }
  1391. int BOARD_INSPECTION_TOOL::HideLocalRatsnest( const TOOL_EVENT& aEvent )
  1392. {
  1393. getModel<BOARD>()->GetConnectivity()->ClearLocalRatsnest();
  1394. delete m_dynamicData;
  1395. m_dynamicData = nullptr;
  1396. return 0;
  1397. }
  1398. void BOARD_INSPECTION_TOOL::calculateSelectionRatsnest( const VECTOR2I& aDelta )
  1399. {
  1400. PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  1401. SELECTION& selection = selectionTool->GetSelection();
  1402. std::shared_ptr<CONNECTIVITY_DATA> connectivity = board()->GetConnectivity();
  1403. std::vector<BOARD_ITEM*> items;
  1404. std::deque<EDA_ITEM*> queued_items( selection.begin(), selection.end() );
  1405. for( std::size_t i = 0; i < queued_items.size(); ++i )
  1406. {
  1407. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( queued_items[i] );
  1408. if( item->Type() == PCB_FOOTPRINT_T )
  1409. {
  1410. for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
  1411. {
  1412. if( pad->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
  1413. items.push_back( pad );
  1414. }
  1415. }
  1416. else if( item->Type() == PCB_GROUP_T )
  1417. {
  1418. PCB_GROUP *group = static_cast<PCB_GROUP*>( item );
  1419. group->RunOnDescendants( [ &queued_items ]( BOARD_ITEM *aItem )
  1420. {
  1421. queued_items.push_back( aItem );
  1422. } );
  1423. }
  1424. else if( BOARD_CONNECTED_ITEM* boardItem = dyn_cast<BOARD_CONNECTED_ITEM*>( item ) )
  1425. {
  1426. if( boardItem->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
  1427. items.push_back( boardItem );
  1428. }
  1429. }
  1430. if( items.empty() || std::none_of( items.begin(), items.end(),
  1431. []( const BOARD_ITEM* aItem )
  1432. {
  1433. return( aItem->Type() == PCB_TRACE_T
  1434. || aItem->Type() == PCB_PAD_T
  1435. || aItem->Type() == PCB_ARC_T
  1436. || aItem->Type() == PCB_ZONE_T
  1437. || aItem->Type() == PCB_FOOTPRINT_T
  1438. || aItem->Type() == PCB_VIA_T );
  1439. } ) )
  1440. {
  1441. return;
  1442. }
  1443. if( !m_dynamicData )
  1444. {
  1445. m_dynamicData = new CONNECTIVITY_DATA( items, true );
  1446. connectivity->BlockRatsnestItems( items );
  1447. }
  1448. else
  1449. {
  1450. m_dynamicData->Move( aDelta );
  1451. }
  1452. connectivity->ComputeLocalRatsnest( items, m_dynamicData );
  1453. }
  1454. int BOARD_INSPECTION_TOOL::ListNets( const TOOL_EVENT& aEvent )
  1455. {
  1456. if( m_listNetsDialog == nullptr )
  1457. {
  1458. m_listNetsDialog =
  1459. std::make_unique<DIALOG_NET_INSPECTOR>( m_frame, m_listNetsDialogSettings );
  1460. m_listNetsDialog->Connect( wxEVT_CLOSE_WINDOW,
  1461. wxCommandEventHandler( BOARD_INSPECTION_TOOL::onListNetsDialogClosed ), nullptr,
  1462. this );
  1463. m_listNetsDialog->Connect( wxEVT_BUTTON,
  1464. wxCommandEventHandler( BOARD_INSPECTION_TOOL::onListNetsDialogClosed ), nullptr,
  1465. this );
  1466. }
  1467. m_listNetsDialog->Raise();
  1468. m_listNetsDialog->Show( true );
  1469. return 0;
  1470. }
  1471. void BOARD_INSPECTION_TOOL::onListNetsDialogClosed( wxCommandEvent& event )
  1472. {
  1473. m_listNetsDialogSettings = m_listNetsDialog->Settings();
  1474. m_listNetsDialog->Disconnect( wxEVT_CLOSE_WINDOW,
  1475. wxCommandEventHandler( BOARD_INSPECTION_TOOL::onListNetsDialogClosed ), nullptr, this );
  1476. m_listNetsDialog->Disconnect( wxEVT_BUTTON,
  1477. wxCommandEventHandler( BOARD_INSPECTION_TOOL::onListNetsDialogClosed ), nullptr, this );
  1478. m_listNetsDialog->Destroy();
  1479. m_listNetsDialog.release();
  1480. }
  1481. void BOARD_INSPECTION_TOOL::onInspectClearanceDialogClosed( wxCommandEvent& event )
  1482. {
  1483. m_inspectClearanceDialog->Disconnect( wxEVT_CLOSE_WINDOW,
  1484. wxCommandEventHandler( BOARD_INSPECTION_TOOL::onInspectClearanceDialogClosed ),
  1485. nullptr, this );
  1486. m_inspectClearanceDialog->Destroy();
  1487. m_inspectClearanceDialog.release();
  1488. }
  1489. void BOARD_INSPECTION_TOOL::onInspectConstraintsDialogClosed( wxCommandEvent& event )
  1490. {
  1491. m_inspectConstraintsDialog->Disconnect( wxEVT_CLOSE_WINDOW,
  1492. wxCommandEventHandler( BOARD_INSPECTION_TOOL::onInspectConstraintsDialogClosed ),
  1493. nullptr, this );
  1494. m_inspectConstraintsDialog->Destroy();
  1495. m_inspectConstraintsDialog.release();
  1496. }
  1497. int BOARD_INSPECTION_TOOL::HideNetInRatsnest( const TOOL_EVENT& aEvent )
  1498. {
  1499. doHideRatsnestNet( aEvent.Parameter<intptr_t>(), true );
  1500. return 0;
  1501. }
  1502. int BOARD_INSPECTION_TOOL::ShowNetInRatsnest( const TOOL_EVENT& aEvent )
  1503. {
  1504. doHideRatsnestNet( aEvent.Parameter<intptr_t>(), false );
  1505. return 0;
  1506. }
  1507. void BOARD_INSPECTION_TOOL::doHideRatsnestNet( int aNetCode, bool aHide )
  1508. {
  1509. KIGFX::PCB_RENDER_SETTINGS* rs = static_cast<KIGFX::PCB_RENDER_SETTINGS*>(
  1510. m_toolMgr->GetView()->GetPainter()->GetSettings() );
  1511. PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  1512. SELECTION& selection = selectionTool->GetSelection();
  1513. if( aNetCode <= 0 && !selection.Empty() )
  1514. {
  1515. for( EDA_ITEM* item : selection )
  1516. {
  1517. if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
  1518. {
  1519. if( bci->GetNetCode() > 0 )
  1520. doHideRatsnestNet( bci->GetNetCode(), aHide );
  1521. }
  1522. }
  1523. return;
  1524. }
  1525. if( aHide )
  1526. rs->GetHiddenNets().insert( aNetCode );
  1527. else
  1528. rs->GetHiddenNets().erase( aNetCode );
  1529. m_frame->GetCanvas()->RedrawRatsnest();
  1530. m_frame->GetCanvas()->Refresh();
  1531. m_frame->GetAppearancePanel()->OnNetVisibilityChanged( aNetCode, !aHide );
  1532. }
  1533. void BOARD_INSPECTION_TOOL::setTransitions()
  1534. {
  1535. Go( &BOARD_INSPECTION_TOOL::LocalRatsnestTool, PCB_ACTIONS::localRatsnestTool.MakeEvent() );
  1536. Go( &BOARD_INSPECTION_TOOL::HideLocalRatsnest, PCB_ACTIONS::hideLocalRatsnest.MakeEvent() );
  1537. Go( &BOARD_INSPECTION_TOOL::UpdateLocalRatsnest, PCB_ACTIONS::updateLocalRatsnest.MakeEvent() );
  1538. Go( &BOARD_INSPECTION_TOOL::ListNets, PCB_ACTIONS::listNets.MakeEvent() );
  1539. Go( &BOARD_INSPECTION_TOOL::ShowBoardStatistics, PCB_ACTIONS::boardStatistics.MakeEvent() );
  1540. Go( &BOARD_INSPECTION_TOOL::InspectClearance, PCB_ACTIONS::inspectClearance.MakeEvent() );
  1541. Go( &BOARD_INSPECTION_TOOL::InspectConstraints, PCB_ACTIONS::inspectConstraints.MakeEvent() );
  1542. Go( &BOARD_INSPECTION_TOOL::InspectLibraryDiff, PCB_ACTIONS::inspectLibraryDiff.MakeEvent() );
  1543. Go( &BOARD_INSPECTION_TOOL::HighlightNet, PCB_ACTIONS::highlightNet.MakeEvent() );
  1544. Go( &BOARD_INSPECTION_TOOL::HighlightNet, PCB_ACTIONS::highlightNetSelection.MakeEvent() );
  1545. Go( &BOARD_INSPECTION_TOOL::HighlightNet, PCB_ACTIONS::toggleLastNetHighlight.MakeEvent() );
  1546. Go( &BOARD_INSPECTION_TOOL::ClearHighlight, PCB_ACTIONS::clearHighlight.MakeEvent() );
  1547. Go( &BOARD_INSPECTION_TOOL::HighlightNet, PCB_ACTIONS::toggleNetHighlight.MakeEvent() );
  1548. Go( &BOARD_INSPECTION_TOOL::HighlightItem, PCB_ACTIONS::highlightItem.MakeEvent() );
  1549. Go( &BOARD_INSPECTION_TOOL::HideNetInRatsnest, PCB_ACTIONS::hideNetInRatsnest.MakeEvent() );
  1550. Go( &BOARD_INSPECTION_TOOL::ShowNetInRatsnest, PCB_ACTIONS::showNetInRatsnest.MakeEvent() );
  1551. }