KiCad PCB EDA Suite
Loading...
Searching...
No Matches
board_inspection_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The 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, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <bitmaps.h>
23#include <board_loader.h>
24#include <collectors.h>
29#include <footprint.h>
30#include <pcb_io/pcb_io_mgr.h>
32#include <kiway.h>
33#include <local_history.h>
35#include <wx/filedlg.h>
36#include <wx/filename.h>
37#include <pcb_group.h>
38#include <tool/tool_manager.h>
41#include <tools/edit_tool.h>
42#include <tools/drc_tool.h>
43#include <pcb_painter.h>
45#include <drc/drc_engine.h>
50#include <dialogs/dialog_drc.h>
51#include <kiplatform/ui.h>
52#include <status_popup.h>
53#include <string_utils.h>
55#include <pcb_painter.h>
56#include <pcb_shape.h>
60#include <wx/choice.h>
61#include <wx/sizer.h>
62#include <wx/stattext.h>
63#include <drc/drc_item.h>
64#include <pad.h>
65#include <pcb_track.h>
66#include <project_pcb.h>
67#include <view/view_controls.h>
68
69
71 PCB_TOOL_BASE( "pcbnew.InspectionTool" ),
72 m_frame( nullptr )
73{
74 m_dynamicData = nullptr;
75}
76
77
79{
80public:
93
94private:
95 ACTION_MENU* create() const override
96 {
97 return new NET_CONTEXT_MENU();
98 }
99};
100
101
103{
104 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
105
106 std::shared_ptr<NET_CONTEXT_MENU> netSubMenu = std::make_shared<NET_CONTEXT_MENU>();
107 netSubMenu->SetTool( this );
108
109 // Only show the net menu if all items in the selection are connectable
110 auto showNetMenuFunc =
111 []( const SELECTION& aSelection )
112 {
113 if( aSelection.Empty() )
114 return false;
115
116 for( const EDA_ITEM* item : aSelection )
117 {
118 switch( item->Type() )
119 {
120 case PCB_TRACE_T:
121 case PCB_ARC_T:
122 case PCB_VIA_T:
123 case PCB_PAD_T:
124 case PCB_ZONE_T:
125 continue;
126
127 case PCB_SHAPE_T:
128 {
129 if( !static_cast<const PCB_SHAPE*>( item )->IsOnCopperLayer() )
130 return false;
131 else
132 continue;
133 }
134
135 default:
136 return false;
137 }
138 }
139
140 return true;
141 };
142
143 CONDITIONAL_MENU& menu = selectionTool->GetToolMenu().GetMenu();
144
145 selectionTool->GetToolMenu().RegisterSubMenu( netSubMenu );
146
147 menu.AddMenu( netSubMenu.get(), showNetMenuFunc, 100 );
148
149 return true;
150}
151
152
157
158
160{
162 dialog.ShowModal();
163 return 0;
164}
165
166
167std::unique_ptr<DRC_ENGINE> BOARD_INSPECTION_TOOL::makeDRCEngine( bool* aCompileError,
168 bool* aCourtyardError )
169{
170 auto engine = std::make_unique<DRC_ENGINE>( m_frame->GetBoard(),
171 &m_frame->GetBoard()->GetDesignSettings() );
172
173 try
174 {
175 engine->InitEngine( m_frame->GetBoard()->GetDesignRulesPath() );
176 }
177 catch( PARSE_ERROR& )
178 {
179 if( aCompileError )
180 *aCompileError = true;
181 }
182
183 for( ZONE* zone : m_frame->GetBoard()->Zones() )
184 zone->CacheBoundingBox();
185
186 for( FOOTPRINT* footprint : m_frame->GetBoard()->Footprints() )
187 {
188 for( ZONE* zone : footprint->Zones() )
189 zone->CacheBoundingBox();
190
191 footprint->BuildCourtyardCaches();
192
193 if( aCourtyardError && ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
194 *aCourtyardError = true;
195 }
196
197 return engine;
198}
199
200
201bool isNPTHPad( BOARD_ITEM* aItem )
202{
203 return aItem->Type() == PCB_PAD_T
204 && static_cast<PAD*>( aItem )->GetAttribute() == PAD_ATTRIB::NPTH;
205}
206
207
209{
210 // Null items have no description
211 if( !aItem )
212 return wxString();
213
214 wxString msg = aItem->GetItemDescription( m_frame, true );
215
216 if( aItem->IsConnected() && !isNPTHPad( aItem ) )
217 {
218 BOARD_CONNECTED_ITEM* cItem = static_cast<BOARD_CONNECTED_ITEM*>( aItem );
219
220 msg += wxS( " " )
221 + wxString::Format( _( "[netclass %s]" ),
223 }
224
225 return msg;
226};
227
228
230 const VECTOR2I& aPos )
231{
232 std::vector<BOARD_ITEM*> toAdd;
233
234 for( int i = 0; i < aCollector.GetCount(); ++i )
235 {
236 if( aCollector[i]->Type() == PCB_GROUP_T )
237 {
238 PCB_GROUP* group = static_cast<PCB_GROUP*>( aCollector[i] );
239
240 group->RunOnChildren(
241 [&]( BOARD_ITEM* child )
242 {
243 if( child->Type() == PCB_GROUP_T )
244 return;
245
246 if( !child->HitTest( aPos ) )
247 return;
248
249 toAdd.push_back( child );
250
251 if( child->Type() == PCB_FOOTPRINT_T )
252 {
253 for( PAD* pad : static_cast<FOOTPRINT*>( child )->Pads() )
254 {
255 if( pad->HitTest( aPos ) )
256 toAdd.push_back( pad );
257 }
258 }
259 },
261 }
262 }
263
264 for( BOARD_ITEM* item : toAdd )
265 aCollector.Append( item );
266
267 bool hasPadOrTrack = false;
268
269 for( int i = 0; i < aCollector.GetCount(); ++i )
270 {
271 KICAD_T type = aCollector[i]->Type();
272
273 if( type == PCB_PAD_T || type == PCB_VIA_T || type == PCB_TRACE_T
274 || type == PCB_ARC_T || type == PCB_ZONE_T )
275 {
276 hasPadOrTrack = true;
277 break;
278 }
279 }
280
281 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
282 {
283 BOARD_ITEM* item = aCollector[i];
284
285 if( hasPadOrTrack && item->Type() == PCB_FOOTPRINT_T )
286 {
287 aCollector.Remove( i );
288 continue;
289 }
290
291 if( item->Type() == PCB_GROUP_T )
292 aCollector.Remove( i );
293 }
294}
295
296
298 const wxString& aPrompt,
299 const std::vector<KICAD_T>& aTypes,
300 BOARD_ITEM* aLockedHighlight )
301{
302 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
303 PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
304 STATUS_TEXT_POPUP statusPopup( m_frame );
305 BOARD_ITEM* pickedItem = nullptr;
306 BOARD_ITEM* highlightedItem = nullptr;
307 bool done = false;
308
309 statusPopup.SetText( aPrompt );
310
311 picker->SetCursor( KICURSOR::BULLSEYE );
312 picker->SetSnapping( false );
313 picker->ClearHandlers();
314
315 picker->SetClickHandler(
316 [&]( const VECTOR2D& aPoint ) -> bool
317 {
319
320 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
321 GENERAL_COLLECTOR collector;
322
323 collector.Collect( m_frame->GetBoard(), aTypes, aPoint, guide );
324
325 for( int i = collector.GetCount() - 1; i >= 0; --i )
326 {
327 if( !selTool->Selectable( collector[i] ) )
328 collector.Remove( i );
329 }
330
331 filterCollectorForInspection( collector, aPoint );
332
333 if( collector.GetCount() > 1 )
334 selTool->GuessSelectionCandidates( collector, aPoint );
335
336 if( collector.GetCount() == 0 )
337 return true;
338
339 pickedItem = collector[0];
340 statusPopup.Hide();
341
342 return false;
343 } );
344
345 picker->SetMotionHandler(
346 [&]( const VECTOR2D& aPos )
347 {
348 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
349
350 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
351 GENERAL_COLLECTOR collector;
352
353 collector.Collect( m_frame->GetBoard(), aTypes, aPos, guide );
354
355 for( int i = collector.GetCount() - 1; i >= 0; --i )
356 {
357 if( !selTool->Selectable( collector[i] ) )
358 collector.Remove( i );
359 }
360
361 filterCollectorForInspection( collector, aPos );
362
363 if( collector.GetCount() > 1 )
364 selTool->GuessSelectionCandidates( collector, aPos );
365
366 BOARD_ITEM* item = collector.GetCount() >= 1 ? collector[0] : nullptr;
367
368 if( highlightedItem != item )
369 {
370 if( highlightedItem && highlightedItem != aLockedHighlight )
371 selTool->UnbrightenItem( highlightedItem );
372
373 highlightedItem = item;
374
375 if( highlightedItem && highlightedItem != aLockedHighlight )
376 selTool->BrightenItem( highlightedItem );
377 }
378 } );
379
380 picker->SetCancelHandler(
381 [&]()
382 {
383 if( highlightedItem && highlightedItem != aLockedHighlight )
384 selTool->UnbrightenItem( highlightedItem );
385
386 highlightedItem = nullptr;
387 statusPopup.Hide();
388 done = true;
389 } );
390
391 picker->SetFinalizeHandler(
392 [&]( const int& aFinalState )
393 {
394 if( highlightedItem && highlightedItem != aLockedHighlight )
395 selTool->UnbrightenItem( highlightedItem );
396
397 highlightedItem = nullptr;
398
399 if( !pickedItem )
400 done = true;
401 } );
402
403 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
404 statusPopup.Popup();
405 m_frame->GetCanvas()->SetStatusPopup( statusPopup.GetPanel() );
406
407 m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
408
409 while( !done && !pickedItem )
410 {
411 if( TOOL_EVENT* evt = Wait() )
412 evt->SetPassEvent();
413 else
414 break;
415 }
416
417 picker->ClearHandlers();
418 m_frame->GetCanvas()->SetStatusPopup( nullptr );
419
420 return pickedItem;
421}
422
423
425{
426 r->Report( "" );
427 r->Report( _( "Report incomplete: could not compile custom design rules." )
428 + wxS( "&nbsp;&nbsp;" )
429 + wxS( "<a href='$CUSTOM_RULES'>" ) + _( "Show design rules." ) + wxS( "</a>" ) );
430}
431
432
433void BOARD_INSPECTION_TOOL::reportHeader( const wxString& aTitle, BOARD_ITEM* a, REPORTER* r )
434{
435 r->Report( wxT( "<h7>" ) + EscapeHTML( aTitle ) + wxT( "</h7>" ) );
436 r->Report( wxT( "<ul><li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li></ul>" ) );
437}
438
439
440void BOARD_INSPECTION_TOOL::reportHeader( const wxString& aTitle, BOARD_ITEM* a, BOARD_ITEM* b,
441 REPORTER* r )
442{
443 r->Report( wxT( "<h7>" ) + EscapeHTML( aTitle ) + wxT( "</h7>" ) );
444 r->Report( wxT( "<ul><li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li>" )
445 + wxT( "<li>" ) + EscapeHTML( getItemDescription( b ) ) + wxT( "</li></ul>" ) );
446}
447
448
449void BOARD_INSPECTION_TOOL::reportHeader( const wxString& aTitle, BOARD_ITEM* a, BOARD_ITEM* b,
450 PCB_LAYER_ID aLayer, REPORTER* r )
451{
452 wxString layerStr = _( "Layer" ) + wxS( " " ) + m_frame->GetBoard()->GetLayerName( aLayer );
453
454 r->Report( wxT( "<h7>" ) + EscapeHTML( aTitle ) + wxT( "</h7>" ) );
455 r->Report( wxT( "<ul><li>" ) + EscapeHTML( layerStr ) + wxT( "</li>" )
456 + wxT( "<li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li>" )
457 + wxT( "<li>" ) + EscapeHTML( getItemDescription( b ) ) + wxT( "</li></ul>" ) );
458}
459
460
461namespace
462{
463class VECTOR_REPORTER : public REPORTER
464{
465public:
466 REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED ) override
467 {
468 m_messages.push_back( aText );
469 return *this;
470 }
471
472 bool HasMessage() const override { return !m_messages.empty(); }
473 EDA_UNITS GetUnits() const override { return EDA_UNITS::UNSCALED; }
474
475 std::vector<wxString> m_messages;
476};
477} // namespace
478
479
480wxString reportMin( PCB_BASE_FRAME* aFrame, DRC_CONSTRAINT& aConstraint )
481{
482 if( aConstraint.m_Value.HasMin() )
483 return aFrame->StringFromValue( aConstraint.m_Value.Min(), true );
484 else
485 return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
486}
487
488
489wxString reportOpt( PCB_BASE_FRAME* aFrame, DRC_CONSTRAINT& aConstraint )
490{
491 if( aConstraint.m_Value.HasOpt() )
492 return aFrame->StringFromValue( aConstraint.m_Value.Opt(), true );
493 else
494 return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
495}
496
497
498wxString reportMax( PCB_BASE_FRAME* aFrame, DRC_CONSTRAINT& aConstraint )
499{
500 if( aConstraint.m_Value.HasMax() )
501 return aFrame->StringFromValue( aConstraint.m_Value.Max(), true );
502 else
503 return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
504}
505
506
507wxString BOARD_INSPECTION_TOOL::InspectDRCErrorMenuText( const std::shared_ptr<RC_ITEM>& aDRCItem )
508{
509 if( aDRCItem->GetErrorCode() == DRCE_CLEARANCE
510 || aDRCItem->GetErrorCode() == DRCE_EDGE_CLEARANCE
511 || aDRCItem->GetErrorCode() == DRCE_HOLE_CLEARANCE
512 || aDRCItem->GetErrorCode() == DRCE_DRILLED_HOLES_TOO_CLOSE
513 || aDRCItem->GetErrorCode() == DRCE_STARVED_THERMAL )
514 {
515 return m_frame->GetRunMenuCommandDescription( PCB_ACTIONS::inspectClearance );
516 }
517 else if( aDRCItem->GetErrorCode() == DRCE_TEXT_HEIGHT
518 || aDRCItem->GetErrorCode() == DRCE_TEXT_THICKNESS
519 || aDRCItem->GetErrorCode() == DRCE_DP_UNCOUPLED_LENGTH_TOO_LONG
520 || aDRCItem->GetErrorCode() == DRCE_TRACK_WIDTH
521 || aDRCItem->GetErrorCode() == DRCE_TRACK_ANGLE
522 || aDRCItem->GetErrorCode() == DRCE_TRACK_SEGMENT_LENGTH
523 || aDRCItem->GetErrorCode() == DRCE_VIA_DIAMETER
524 || aDRCItem->GetErrorCode() == DRCE_ANNULAR_WIDTH
525 || aDRCItem->GetErrorCode() == DRCE_DRILL_OUT_OF_RANGE
526 || aDRCItem->GetErrorCode() == DRCE_MICROVIA_DRILL_OUT_OF_RANGE
527 || aDRCItem->GetErrorCode() == DRCE_CONNECTION_WIDTH
528 || aDRCItem->GetErrorCode() == DRCE_ASSERTION_FAILURE )
529 {
530 return m_frame->GetRunMenuCommandDescription( PCB_ACTIONS::inspectConstraints );
531 }
532 else if( aDRCItem->GetErrorCode() == DRCE_LIB_FOOTPRINT_MISMATCH )
533 {
534 return m_frame->GetRunMenuCommandDescription( PCB_ACTIONS::diffFootprint );
535 }
536
537 return wxEmptyString;
538}
539
540
541void BOARD_INSPECTION_TOOL::InspectDRCError( const std::shared_ptr<RC_ITEM>& aDRCItem )
542{
543 DRC_TOOL* drcTool = m_toolMgr->GetTool<DRC_TOOL>();
544
545 wxCHECK( drcTool && m_frame, /* void */ );
546
547 BOARD_ITEM* a = m_frame->GetBoard()->ResolveItem( aDRCItem->GetMainItemID() );
548 BOARD_ITEM* b = m_frame->GetBoard()->ResolveItem( aDRCItem->GetAuxItemID() );
549 BOARD_CONNECTED_ITEM* ac = dynamic_cast<BOARD_CONNECTED_ITEM*>( a );
550 BOARD_CONNECTED_ITEM* bc = dynamic_cast<BOARD_CONNECTED_ITEM*>( b );
551 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
552
553 if( aDRCItem->GetErrorCode() == DRCE_LIB_FOOTPRINT_MISMATCH )
554 {
555 if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( a ) )
556 DiffFootprint( footprint, drcTool->GetDRCDialog() );
557
558 return;
559 }
560
561 DIALOG_BOOK_REPORTER* dialog = m_frame->GetInspectDrcErrorDialog();
562 wxCHECK( dialog, /* void */ );
563
564 dialog->DeleteAllPages();
565
566 bool compileError = false;
567 bool courtyardError = false;
568 std::unique_ptr<DRC_ENGINE> drcEngine = makeDRCEngine( &compileError, &courtyardError );
569
570 WX_HTML_REPORT_BOX* r = nullptr;
571 DRC_CONSTRAINT constraint;
572 int clearance = 0;
573 wxString clearanceStr;
574
575 switch( aDRCItem->GetErrorCode() )
576 {
578 {
579 for( KIID id : aDRCItem->GetIDs() )
580 {
581 bc = dynamic_cast<BOARD_CONNECTED_ITEM*>( m_frame->GetBoard()->ResolveItem( id, true ) );
582
583 if( ac && bc && ac->GetNetCode() != bc->GetNetCode() )
584 break;
585 }
586
587 r = dialog->AddHTMLPage( _( "Uncoupled Length" ) );
588 reportHeader( _( "Diff pair uncoupled length resolution for:" ), ac, bc, r );
589
590 if( compileError )
592
593 constraint = drcEngine->EvalRules( MAX_UNCOUPLED_CONSTRAINT, a, b, layer, r );
594
595 r->Report( "" );
596 r->Report( wxString::Format( _( "Resolved max uncoupled length: %s." ),
597 reportMax( m_frame, constraint ) ) );
598 break;
599 }
600
601 case DRCE_TEXT_HEIGHT:
602 r = dialog->AddHTMLPage( _( "Text Height" ) );
603 reportHeader( _( "Text height resolution for:" ), a, r );
604
605 if( compileError )
607
608 constraint = drcEngine->EvalRules( TEXT_HEIGHT_CONSTRAINT, a, b, layer, r );
609
610 r->Report( "" );
611 r->Report( wxString::Format( _( "Resolved height constraints: min %s; max %s." ),
612 reportMin( m_frame, constraint ),
613 reportMax( m_frame, constraint ) ) );
614 break;
615
617 r = dialog->AddHTMLPage( _( "Text Thickness" ) );
618 reportHeader( _( "Text thickness resolution for:" ), a, r );
619
620 if( compileError )
622
623 constraint = drcEngine->EvalRules( TEXT_THICKNESS_CONSTRAINT, a, b, layer, r );
624
625 r->Report( "" );
626 r->Report( wxString::Format( _( "Resolved thickness constraints: min %s; max %s." ),
627 reportMin( m_frame, constraint ),
628 reportMax( m_frame, constraint ) ) );
629 break;
630
631 case DRCE_TRACK_WIDTH:
632 r = dialog->AddHTMLPage( _( "Track Width" ) );
633 reportHeader( _( "Track width resolution for:" ), a, r );
634
635 if( compileError )
637
638 constraint = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, a, b, layer, r );
639
640 r->Report( "" );
641 r->Report( wxString::Format( _( "Resolved width constraints: min %s; max %s." ),
642 reportMin( m_frame, constraint ),
643 reportMax( m_frame, constraint ) ) );
644 break;
645
646 case DRCE_TRACK_ANGLE:
647 r = dialog->AddHTMLPage( _( "Track Angle" ) );
648 reportHeader( _( "Track Angle resolution for:" ), a, r );
649
650 if( compileError )
652
653 constraint = drcEngine->EvalRules( TRACK_ANGLE_CONSTRAINT, a, b, layer, r );
654
655 r->Report( "" );
656 r->Report( wxString::Format( _( "Resolved angle constraints: min %s; max %s." ),
657 reportMin( m_frame, constraint ),
658 reportMax( m_frame, constraint ) ) );
659 break;
660
662 r = dialog->AddHTMLPage( _( "Track Segment Length" ) );
663 reportHeader( _( "Track segment length resolution for:" ), a, r );
664
665 if( compileError )
667
668 constraint = drcEngine->EvalRules( TRACK_SEGMENT_LENGTH_CONSTRAINT, a, b, layer, r );
669
670 r->Report( "" );
671 r->Report( wxString::Format( _( "Resolved segment length constraints: min %s; max %s." ),
672 reportMin( m_frame, constraint ),
673 reportMax( m_frame, constraint ) ) );
674 break;
675
677 r = dialog->AddHTMLPage( _( "Connection Width" ) );
678 reportHeader( _( "Connection width resolution for:" ), a, b, r );
679
680 if( compileError )
682
683 constraint = drcEngine->EvalRules( CONNECTION_WIDTH_CONSTRAINT, a, b, layer, r );
684
685 r->Report( "" );
686 r->Report( wxString::Format( _( "Resolved min connection width: %s." ),
687 reportMin( m_frame, constraint ) ) );
688 break;
689
691 r = dialog->AddHTMLPage( _( "Via Diameter" ) );
692 reportHeader( _( "Via diameter resolution for:" ), a, r );
693
694 if( compileError )
696
697 constraint = drcEngine->EvalRules( VIA_DIAMETER_CONSTRAINT, a, b, layer, r );
698
699 r->Report( "" );
700 r->Report( wxString::Format( _( "Resolved diameter constraints: min %s; max %s." ),
701 reportMin( m_frame, constraint ),
702 reportMax( m_frame, constraint ) ) );
703 break;
704
706 r = dialog->AddHTMLPage( _( "Via Annulus" ) );
707 reportHeader( _( "Via annular width resolution for:" ), a, r );
708
709 if( compileError )
711
712 constraint = drcEngine->EvalRules( ANNULAR_WIDTH_CONSTRAINT, a, b, layer, r );
713
714 r->Report( "" );
715 r->Report( wxString::Format( _( "Resolved annular width constraints: min %s; max %s." ),
716 reportMin( m_frame, constraint ),
717 reportMax( m_frame, constraint ) ) );
718 break;
719
722 r = dialog->AddHTMLPage( _( "Hole Size" ) );
723 reportHeader( _( "Hole size resolution for:" ), a, r );
724
725 if( compileError )
727
728 constraint = drcEngine->EvalRules( HOLE_SIZE_CONSTRAINT, a, b, layer, r );
729
730 r->Report( "" );
731 r->Report( wxString::Format( _( "Resolved hole size constraints: min %s; max %s." ),
732 reportMin( m_frame, constraint ),
733 reportMax( m_frame, constraint ) ) );
734 break;
735
737 r = dialog->AddHTMLPage( _( "Hole Clearance" ) );
738 reportHeader( _( "Hole clearance resolution for:" ), a, b, r );
739
740 if( compileError )
742
743 if( ac && bc && ac->GetNetCode() == bc->GetNetCode() )
744 {
745 r->Report( "" );
746 r->Report( _( "Items belong to the same net. Clearance is 0." ) );
747 }
748 else
749 {
750 constraint = drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
751 clearance = constraint.m_Value.Min();
752 clearanceStr = m_frame->StringFromValue( clearance, true );
753
754 r->Report( "" );
755 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
756 }
757
758 r->Report( "" );
759 r->Report( "" );
760 r->Report( "" );
761 reportHeader( _( "Physical hole clearance resolution for:" ), a, b, layer, r );
762
763 constraint = drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
764 clearance = constraint.m_Value.Min();
765 clearanceStr = m_frame->StringFromValue( clearance, true );
766
767 if( !drcEngine->HasRulesForConstraintType( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT ) )
768 {
769 r->Report( "" );
770 r->Report( _( "No 'physical_hole_clearance' constraints defined." ) );
771 }
772 else
773 {
774 r->Report( "" );
775 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
776 }
777
778 break;
779
781 r = dialog->AddHTMLPage( _( "Hole to Hole" ) );
782 reportHeader( _( "Hole-to-hole clearance resolution for:" ), a, b, r );
783
784 if( compileError )
786
787 constraint = drcEngine->EvalRules( HOLE_TO_HOLE_CONSTRAINT, a, b, UNDEFINED_LAYER, r );
788 clearance = constraint.m_Value.Min();
789 clearanceStr = m_frame->StringFromValue( clearance, true );
790
791 r->Report( "" );
792 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
793 break;
794
796 r = dialog->AddHTMLPage( _( "Edge Clearance" ) );
797 reportHeader( _( "Edge clearance resolution for:" ), a, b, r );
798
799 if( compileError )
801
802 constraint = drcEngine->EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, layer, r );
803 clearance = constraint.m_Value.Min();
804 clearanceStr = m_frame->StringFromValue( clearance, true );
805
806 r->Report( "" );
807 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
808 break;
809
810 case DRCE_CLEARANCE:
811 if( a->Type() == PCB_TRACE_T || a->Type() == PCB_ARC_T )
812 {
813 layer = a->GetLayer();
814 }
815 else if( b->Type() == PCB_TRACE_T || b->Type() == PCB_ARC_T )
816 {
817 layer = b->GetLayer();
818 }
819 else if( a->Type() == PCB_PAD_T
820 && static_cast<PAD*>( a )->GetAttribute() == PAD_ATTRIB::SMD )
821 {
822 PAD* pad = static_cast<PAD*>( a );
823
824 if( pad->IsOnLayer( F_Cu ) )
825 layer = F_Cu;
826 else
827 layer = B_Cu;
828 }
829 else if( b->Type() == PCB_PAD_T
830 && static_cast<PAD*>( a )->GetAttribute() == PAD_ATTRIB::SMD )
831 {
832 PAD* pad = static_cast<PAD*>( b );
833
834 if( pad->IsOnLayer( F_Cu ) )
835 layer = F_Cu;
836 else
837 layer = B_Cu;
838 }
839
840 r = dialog->AddHTMLPage( _( "Clearance" ) );
841 reportHeader( _( "Clearance resolution for:" ), a, b, layer, r );
842
843 if( compileError )
845
846 if( ac && bc && ac->GetNetCode() == bc->GetNetCode() )
847 {
848 r->Report( "" );
849 r->Report( _( "Items belong to the same net. Clearance is 0." ) );
850 }
851 else
852 {
853 constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, a, b, layer, r );
854 clearance = constraint.m_Value.Min();
855 clearanceStr = m_frame->StringFromValue( clearance, true );
856
857 r->Report( "" );
858 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
859 }
860
861 r->Report( "" );
862 r->Report( "" );
863 r->Report( "" );
864 reportHeader( _( "Physical clearance resolution for:" ), a, b, layer, r );
865
866 constraint = drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, layer, r );
867 clearance = constraint.m_Value.Min();
868 clearanceStr = m_frame->StringFromValue( clearance, true );
869
870 if( !drcEngine->HasRulesForConstraintType( PHYSICAL_CLEARANCE_CONSTRAINT ) )
871 {
872 r->Report( "" );
873 r->Report( _( "No 'physical_clearance' constraints defined." ) );
874 }
875 else
876 {
877 r->Report( "" );
878 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
879 }
880
881 break;
882
884 r = dialog->AddHTMLPage( _( "Assertions" ) );
885 reportHeader( _( "Assertions for:" ), a, r );
886
887 if( compileError )
889
890 drcEngine->ProcessAssertions( a, []( const DRC_CONSTRAINT* c ){}, r );
891 break;
892
893 default:
894 return;
895 }
896
897 r->Flush();
898
899 KIPLATFORM::UI::ReparentWindow( dialog, drcTool->GetDRCDialog() );
900 dialog->Show( true );
901}
902
903
905{
906 wxCHECK( m_frame, 0 );
907
908 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
909
910 wxCHECK( selTool, 0 );
911
912 const PCB_SELECTION& selection = selTool->GetSelection();
913 BOARD_ITEM* firstItem = nullptr;
914 BOARD_ITEM* secondItem = nullptr;
915
916 if( selection.Size() == 2 )
917 {
918 if( !selection.GetItem( 0 )->IsBOARD_ITEM() || !selection.GetItem( 1 )->IsBOARD_ITEM() )
919 return 0;
920
921 firstItem = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) );
922 secondItem = static_cast<BOARD_ITEM*>( selection.GetItem( 1 ) );
923
924 reportClearance( firstItem, secondItem );
925 return 0;
926 }
927
928 // Selection size is not 2, so we need to use picker mode.
929 // If there is one item selected, use it as the first item.
930 if( selection.Size() == 1 && selection.GetItem( 0 )->IsBOARD_ITEM() )
931 firstItem = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) );
932
933 static const std::vector<KICAD_T> clearanceTypes = {
934 PCB_PAD_T,
935 PCB_VIA_T,
937 PCB_ARC_T,
942 };
943
944 Activate();
945
946 if( !firstItem )
947 {
948 firstItem = pickItemForInspection( aEvent,
949 _( "Select first item for clearance resolution..." ),
950 clearanceTypes, nullptr );
951
952 if( !firstItem )
953 return 0;
954 }
955
956 // Keep the first item highlighted while selecting the second
957 selTool->BrightenItem( firstItem );
958
959 secondItem = pickItemForInspection( aEvent,
960 _( "Select second item for clearance resolution..." ),
961 clearanceTypes, firstItem );
962
963 selTool->UnbrightenItem( firstItem );
964
965 if( !secondItem )
966 return 0;
967
968 if( firstItem == secondItem )
969 {
970 m_frame->ShowInfoBarError( _( "Select two different items for clearance resolution." ) );
971 return 0;
972 }
973
974 reportClearance( firstItem, secondItem );
975
976 return 0;
977}
978
979
981{
982 wxCHECK( m_frame && aItemA && aItemB, /* void */ );
983
984 BOARD_ITEM* a = aItemA;
985 BOARD_ITEM* b = aItemB;
986
987 if( a->Type() == PCB_GROUP_T )
988 {
989 PCB_GROUP* ag = static_cast<PCB_GROUP*>( a );
990
991 if( ag->GetItems().empty() )
992 {
993 m_frame->ShowInfoBarError( _( "Cannot generate clearance report on empty group." ) );
994 return;
995 }
996
997 a = static_cast<BOARD_ITEM*>( *ag->GetItems().begin() );
998 }
999
1000 if( b->Type() == PCB_GROUP_T )
1001 {
1002 PCB_GROUP* bg = static_cast<PCB_GROUP*>( b );
1003
1004 if( bg->GetItems().empty() )
1005 {
1006 m_frame->ShowInfoBarError( _( "Cannot generate clearance report on empty group." ) );
1007 return;
1008 }
1009
1010 b = static_cast<BOARD_ITEM*>( *bg->GetItems().begin() );
1011 }
1012
1013 if( !a || !b )
1014 return;
1015
1016 auto checkFootprint =
1017 [&]( FOOTPRINT* footprint ) -> BOARD_ITEM*
1018 {
1019 PAD* foundPad = nullptr;
1020
1021 for( PAD* pad : footprint->Pads() )
1022 {
1023 if( !foundPad || pad->SameLogicalPadAs( foundPad ) )
1024 foundPad = pad;
1025 else
1026 return footprint;
1027 }
1028
1029 if( !foundPad )
1030 return footprint;
1031
1032 return foundPad;
1033 };
1034
1035 if( a->Type() == PCB_FOOTPRINT_T )
1036 a = checkFootprint( static_cast<FOOTPRINT*>( a ) );
1037
1038 if( b->Type() == PCB_FOOTPRINT_T )
1039 b = checkFootprint( static_cast<FOOTPRINT*>( b ) );
1040
1041 if( !a || !b )
1042 return;
1043
1044 DIALOG_BOOK_REPORTER* dialog = m_frame->GetInspectClearanceDialog();
1045
1046 wxCHECK( dialog, /* void */ );
1047
1048 dialog->DeleteAllPages();
1049
1050 if( a->Type() != PCB_ZONE_T && b->Type() == PCB_ZONE_T )
1051 std::swap( a, b );
1052 else if( !a->IsConnected() && b->IsConnected() )
1053 std::swap( a, b );
1054
1055 WX_HTML_REPORT_BOX* r = nullptr;
1056 PCB_LAYER_ID active = m_frame->GetActiveLayer();
1057 LSET layerIntersection = a->GetLayerSet() & b->GetLayerSet();
1058 LSET copperIntersection = layerIntersection & LSET::AllCuMask();
1059 BOARD_CONNECTED_ITEM* ac = dynamic_cast<BOARD_CONNECTED_ITEM*>( a );
1060 BOARD_CONNECTED_ITEM* bc = dynamic_cast<BOARD_CONNECTED_ITEM*>( b );
1061 ZONE* zone = dynamic_cast<ZONE*>( a );
1062 PAD* pad = dynamic_cast<PAD*>( b );
1063 FOOTPRINT* aFP = dynamic_cast<FOOTPRINT*>( a );
1064 FOOTPRINT* bFP = dynamic_cast<FOOTPRINT*>( b );
1065 DRC_CONSTRAINT constraint;
1066 int clearance = 0;
1067
1068 bool compileError = false;
1069 bool courtyardError = false;
1070 std::unique_ptr<DRC_ENGINE> drcEngine = makeDRCEngine( &compileError, &courtyardError );
1071
1072 if( copperIntersection.any() && zone && pad && zone->GetNetCode() == pad->GetNetCode() )
1073 {
1075
1076 if( zone->IsOnLayer( active ) )
1077 layer = active;
1078 else if( zone->GetLayerSet().count() > 0 )
1079 layer = zone->GetLayerSet().Seq().front();
1080
1081 r = dialog->AddHTMLPage( _( "Zone" ) );
1082 reportHeader( _( "Zone connection resolution for:" ), a, b, layer, r );
1083
1084 constraint = drcEngine->EvalZoneConnection( pad, zone, layer, r );
1085
1086 if( constraint.m_ZoneConnection == ZONE_CONNECTION::THERMAL )
1087 {
1088 r->Report( "" );
1089 r->Report( "" );
1090 reportHeader( _( "Thermal-relief gap resolution for:" ), a, b, layer, r );
1091
1092 constraint = drcEngine->EvalRules( THERMAL_RELIEF_GAP_CONSTRAINT, pad, zone, layer, r );
1093 int gap = constraint.m_Value.Min();
1094
1095 if( compileError )
1096 reportCompileError( r );
1097
1098 r->Report( "" );
1099 r->Report( wxString::Format( _( "Resolved thermal relief gap: %s." ),
1100 m_frame->StringFromValue( gap, true ) ) );
1101
1102 r->Report( "" );
1103 r->Report( "" );
1104 reportHeader( _( "Thermal-relief spoke width resolution for:" ), a, b, layer, r );
1105
1106 constraint = drcEngine->EvalRules( THERMAL_SPOKE_WIDTH_CONSTRAINT, pad, zone, layer, r );
1107 int width = constraint.m_Value.Opt();
1108
1109 if( compileError )
1110 reportCompileError( r );
1111
1112 r->Report( "" );
1113 r->Report( wxString::Format( _( "Resolved spoke width: %s." ),
1114 m_frame->StringFromValue( width, true ) ) );
1115
1116 r->Report( "" );
1117 r->Report( "" );
1118 reportHeader( _( "Thermal-relief min spoke count resolution for:" ), a, b, layer, r );
1119
1120 constraint = drcEngine->EvalRules( MIN_RESOLVED_SPOKES_CONSTRAINT, pad, zone, layer, r );
1121 int minSpokes = constraint.m_Value.Min();
1122
1123 if( compileError )
1124 reportCompileError( r );
1125
1126 r->Report( "" );
1127 r->Report( wxString::Format( _( "Resolved min spoke count: %d." ),
1128 minSpokes ) );
1129
1130 std::shared_ptr<CONNECTIVITY_DATA> connectivity = pad->GetBoard()->GetConnectivity();
1131 }
1132 else if( constraint.m_ZoneConnection == ZONE_CONNECTION::NONE )
1133 {
1134 r->Report( "" );
1135 r->Report( "" );
1136 reportHeader( _( "Zone clearance resolution for:" ), a, b, layer, r );
1137
1138 clearance = zone->GetLocalClearance().value();
1139 r->Report( "" );
1140 r->Report( wxString::Format( _( "Zone clearance: %s." ),
1141 m_frame->StringFromValue( clearance, true ) ) );
1142
1143 constraint = drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, pad, zone, layer, r );
1144
1145 if( constraint.m_Value.Min() > clearance )
1146 {
1147 clearance = constraint.m_Value.Min();
1148
1149 r->Report( "" );
1150 r->Report( wxString::Format( _( "Overridden by larger physical clearance from %s;"
1151 "clearance: %s." ),
1152 EscapeHTML( constraint.GetName() ),
1153 m_frame->StringFromValue( clearance, true ) ) );
1154 }
1155
1156 if( !pad->FlashLayer( layer ) )
1157 {
1158 constraint = drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, pad, zone,
1159 layer, r );
1160
1161 if( constraint.m_Value.Min() > clearance )
1162 {
1163 clearance = constraint.m_Value.Min();
1164
1165 r->Report( "" );
1166 r->Report( wxString::Format( _( "Overridden by larger physical hole clearance "
1167 "from %s; clearance: %s." ),
1168 EscapeHTML( constraint.GetName() ),
1169 m_frame->StringFromValue( clearance, true ) ) );
1170 }
1171 }
1172
1173 if( compileError )
1174 reportCompileError( r );
1175
1176 r->Report( "" );
1177 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1178 m_frame->StringFromValue( clearance, true ) ) );
1179 }
1180 else
1181 {
1182 r->Report( "" );
1183 r->Report( "" );
1184 reportHeader( _( "Zone clearance resolution for:" ), a, b, layer, r );
1185
1186 if( compileError )
1187 reportCompileError( r );
1188
1189 r->Report( "" );
1190 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1191 m_frame->StringFromValue( 0, true ) ) );
1192 }
1193
1194 r->Flush();
1195 }
1196 else if( copperIntersection.any() && !aFP && !bFP )
1197 {
1198 bool sameNet = ac && bc && ac->GetNetCode() > 0 && ac->GetNetCode() == bc->GetNetCode();
1199
1200 std::vector<PCB_LAYER_ID> layers;
1201
1202 if( copperIntersection.test( active ) )
1203 layers.push_back( active );
1204
1205 for( PCB_LAYER_ID layer : copperIntersection.Seq() )
1206 {
1207 if( layer != active )
1208 layers.push_back( layer );
1209 }
1210
1211 auto fillReport =
1212 [&]( PCB_LAYER_ID layer, REPORTER* rep )
1213 {
1214 reportHeader( _( "Clearance resolution for:" ), a, b, layer, rep );
1215
1216 if( sameNet )
1217 {
1218 rep->Report( _( "Items belong to the same net. Min clearance is 0." ) );
1219 return;
1220 }
1221
1222 constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, a, b, layer, rep );
1223 clearance = constraint.m_Value.Min();
1224
1225 if( compileError )
1226 reportCompileError( rep );
1227
1228 rep->Report( "" );
1229
1230 if( constraint.IsNull() )
1231 {
1232 rep->Report( _( "Min clearance is 0." ) );
1233 }
1234 else if( clearance < 0 )
1235 {
1236 rep->Report( wxString::Format( _( "Resolved clearance: %s; clearance will "
1237 "not be tested." ),
1238 m_frame->StringFromValue( clearance, true ) ) );
1239 }
1240 else
1241 {
1242 rep->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1243 m_frame->StringFromValue( clearance, true ) ) );
1244 }
1245 };
1246
1247 if( layers.size() == 1 )
1248 {
1249 PCB_LAYER_ID layer = layers.front();
1250
1251 r = dialog->AddHTMLPage( m_frame->GetBoard()->GetLayerName( layer ) );
1252 fillReport( layer, r );
1253 r->Flush();
1254 }
1255 else
1256 {
1257 auto perLayerMessages = std::make_shared<std::vector<std::vector<wxString>>>();
1258 perLayerMessages->reserve( layers.size() );
1259
1260 for( PCB_LAYER_ID layer : layers )
1261 {
1262 VECTOR_REPORTER tmp;
1263 fillReport( layer, &tmp );
1264 perLayerMessages->push_back( std::move( tmp.m_messages ) );
1265 }
1266
1267 wxPanel* panel = dialog->AddBlankPage( _( "Clearance" ) );
1268 wxBoxSizer* vbox = new wxBoxSizer( wxVERTICAL );
1269
1270 wxChoice* choice = new wxChoice( panel, wxID_ANY );
1271
1272 for( PCB_LAYER_ID layer : layers )
1273 choice->Append( m_frame->GetBoard()->GetLayerName( layer ) );
1274
1275 choice->SetSelection( 0 );
1276
1277 WX_HTML_REPORT_BOX* reportBox = new WX_HTML_REPORT_BOX( panel, wxID_ANY, wxDefaultPosition,
1278 wxDefaultSize,
1279 wxHW_SCROLLBAR_AUTO | wxBORDER_SIMPLE );
1280 reportBox->SetUnits( m_frame->GetUserUnits() );
1281
1282 wxStaticText* layerLabel = new wxStaticText( panel, wxID_ANY, _( "Layer:" ) );
1283
1284 vbox->Add( layerLabel, 0, wxLEFT | wxRIGHT | wxTOP, 5 );
1285 vbox->Add( choice, 0, wxEXPAND | wxALL, 5 );
1286 vbox->Add( reportBox, 1, wxEXPAND | wxALL, 5 );
1287 panel->SetSizer( vbox );
1288 panel->Layout();
1289
1290 auto refresh =
1291 [reportBox, perLayerMessages]( int sel )
1292 {
1293 reportBox->Clear();
1294
1295 if( sel >= 0 && sel < (int) perLayerMessages->size() )
1296 {
1297 for( const wxString& line : ( *perLayerMessages )[sel] )
1298 reportBox->Report( line );
1299 }
1300
1301 reportBox->Flush();
1302 };
1303
1304 choice->Bind( wxEVT_CHOICE,
1305 [refresh]( wxCommandEvent& evt )
1306 {
1307 refresh( evt.GetSelection() );
1308 } );
1309
1310 refresh( 0 );
1311 }
1312 }
1313
1314 if( ac && bc )
1315 {
1316 NETINFO_ITEM* refNet = ac->GetNet();
1317 wxString coupledNet;
1318 wxString dummy;
1319
1320 if( DRC_ENGINE::MatchDpSuffix( refNet->GetNetname(), coupledNet, dummy )
1321 && bc->GetNetname() == coupledNet )
1322 {
1323 LSET dpIntersection = ac->GetLayerSet() & bc->GetLayerSet() & LSET::AllCuMask();
1324 PCB_LAYER_ID dpLayer = active;
1325
1326 if( !dpIntersection.test( dpLayer ) && dpIntersection.any() )
1327 dpLayer = dpIntersection.Seq().front();
1328
1329 r = dialog->AddHTMLPage( _( "Diff Pair" ) );
1330 reportHeader( _( "Diff-pair gap resolution for:" ), ac, bc, dpLayer, r );
1331
1332 constraint = drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, ac, bc, dpLayer, r );
1333
1334 r->Report( "" );
1335 r->Report( wxString::Format( _( "Resolved gap constraints: min %s; opt %s; max %s." ),
1336 reportMin( m_frame, constraint ),
1337 reportOpt( m_frame, constraint ),
1338 reportMax( m_frame, constraint ) ) );
1339
1340 r->Report( "" );
1341 r->Report( "" );
1342 r->Report( "" );
1343 reportHeader( _( "Diff-pair max uncoupled length resolution for:" ), ac, bc, dpLayer, r );
1344
1345 if( !drcEngine->HasRulesForConstraintType( MAX_UNCOUPLED_CONSTRAINT ) )
1346 {
1347 r->Report( "" );
1348 r->Report( _( "No 'diff_pair_uncoupled' constraints defined." ) );
1349 }
1350 else
1351 {
1352 constraint = drcEngine->EvalRules( MAX_UNCOUPLED_CONSTRAINT, ac, bc, dpLayer, r );
1353
1354 r->Report( "" );
1355 r->Report( wxString::Format( _( "Resolved max uncoupled length: %s." ),
1356 reportMax( m_frame, constraint ) ) );
1357 }
1358
1359 r->Flush();
1360 }
1361 }
1362
1363 auto isOnCorrespondingLayer=
1364 [&]( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, wxString* aWarning )
1365 {
1366 if( aItem->IsOnLayer( aLayer ) )
1367 return true;
1368
1369 PCB_LAYER_ID correspondingMask = IsFrontLayer( aLayer ) ? F_Mask : B_Mask;
1370 PCB_LAYER_ID correspondingCopper = IsFrontLayer( aLayer ) ? F_Cu : B_Cu;
1371
1372 if( aItem->IsOnLayer( aLayer ) )
1373 return true;
1374
1375 if( aItem->IsOnLayer( correspondingMask ) )
1376 return true;
1377
1378 if( aItem->IsTented( correspondingMask ) && aItem->IsOnLayer( correspondingCopper ) )
1379 {
1380 *aWarning = wxString::Format( _( "Note: %s is tented; clearance will only be "
1381 "applied to holes." ),
1382 getItemDescription( aItem ) );
1383 return true;
1384 }
1385
1386 return false;
1387 };
1388
1389 for( PCB_LAYER_ID layer : { F_SilkS, B_SilkS } )
1390 {
1391 wxString warning;
1392
1393 if( ( a->IsOnLayer( layer ) && isOnCorrespondingLayer( b, layer, &warning ) )
1394 || ( b->IsOnLayer( layer ) && isOnCorrespondingLayer( a, layer, &warning ) ) )
1395 {
1396 r = dialog->AddHTMLPage( m_frame->GetBoard()->GetLayerName( layer ) );
1397 reportHeader( _( "Silkscreen clearance resolution for:" ), a, b, layer, r );
1398
1399 constraint = drcEngine->EvalRules( SILK_CLEARANCE_CONSTRAINT, a, b, layer, r );
1400 clearance = constraint.m_Value.Min();
1401
1402 if( compileError )
1403 reportCompileError( r );
1404
1405 r->Report( "" );
1406
1407 if( !warning.IsEmpty() )
1408 r->Report( warning );
1409
1410 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1411 m_frame->StringFromValue( clearance, true ) ) );
1412
1413 r->Flush();
1414 }
1415 }
1416
1417 for( PCB_LAYER_ID layer : { F_CrtYd, B_CrtYd } )
1418 {
1419 bool aCourtyard = aFP && !aFP->GetCourtyard( layer ).IsEmpty();
1420 bool bCourtyard = bFP && !bFP->GetCourtyard( layer ).IsEmpty();
1421
1422 if( aCourtyard && bCourtyard )
1423 {
1424 r = dialog->AddHTMLPage( m_frame->GetBoard()->GetLayerName( layer ) );
1425 reportHeader( _( "Courtyard clearance resolution for:" ), a, b, layer, r );
1426
1427 constraint = drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, a, b, layer, r );
1428 clearance = constraint.m_Value.Min();
1429
1430 if( compileError )
1431 reportCompileError( r );
1432
1433 r->Report( "" );
1434 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1435 m_frame->StringFromValue( clearance, true ) ) );
1436
1437 r->Flush();
1438 }
1439 }
1440
1441 if( a->HasHole() || b->HasHole() )
1442 {
1444 bool pageAdded = false;
1445
1446 if( a->HasHole() && b->IsOnLayer( active ) && IsCopperLayer( active ) )
1447 layer = active;
1448 else if( b->HasHole() && a->IsOnLayer( active ) && IsCopperLayer( active ) )
1449 layer = active;
1450 else if( a->HasHole() && b->IsOnCopperLayer() )
1451 layer = b->GetLayer();
1452 else if( b->HasHole() && a->IsOnCopperLayer() )
1453 layer = a->GetLayer();
1454
1455 if( layer >= 0 )
1456 {
1457 r = dialog->AddHTMLPage( _( "Hole" ) );
1458 pageAdded = true;
1459
1460 reportHeader( _( "Hole clearance resolution for:" ), a, b, layer, r );
1461
1462 constraint = drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
1463 clearance = constraint.m_Value.Min();
1464
1465 if( compileError )
1466 reportCompileError( r );
1467
1468 r->Report( "" );
1469 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1470 m_frame->StringFromValue( clearance, true ) ) );
1471
1472 r->Flush();
1473 }
1474
1475 if( a->HasDrilledHole() || b->HasDrilledHole() )
1476 {
1477 if( !pageAdded )
1478 {
1479 r = dialog->AddHTMLPage( _( "Hole" ) );
1480 pageAdded = true;
1481 }
1482 else
1483 {
1484 r->Report( "" );
1485 r->Report( "" );
1486 r->Report( "" );
1487 }
1488
1489 reportHeader( _( "Hole-to-hole clearance resolution for:" ), a, b, r );
1490
1491 constraint = drcEngine->EvalRules( HOLE_TO_HOLE_CONSTRAINT, a, b, UNDEFINED_LAYER, r );
1492 clearance = constraint.m_Value.Min();
1493
1494 if( compileError )
1495 reportCompileError( r );
1496
1497 r->Report( "" );
1498 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1499 m_frame->StringFromValue( clearance, true ) ) );
1500
1501 r->Flush();
1502 }
1503 }
1504
1505 for( PCB_LAYER_ID edgeLayer : { Edge_Cuts, Margin } )
1506 {
1508
1509 if( a->IsOnLayer( edgeLayer ) && b->Type() != PCB_FOOTPRINT_T )
1510 {
1511 if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
1512 layer = active;
1513 else if( IsCopperLayer( b->GetLayer() ) )
1514 layer = b->GetLayer();
1515 }
1516 else if( b->IsOnLayer( edgeLayer ) && a->Type() != PCB_FOOTPRINT_T )
1517 {
1518 if( a->IsOnLayer( active ) && IsCopperLayer( active ) )
1519 layer = active;
1520 else if( IsCopperLayer( a->GetLayer() ) )
1521 layer = a->GetLayer();
1522 }
1523
1524 if( layer >= 0 )
1525 {
1526 wxString layerName = m_frame->GetBoard()->GetLayerName( edgeLayer );
1527 r = dialog->AddHTMLPage( layerName + wxS( " " ) + _( "Clearance" ) );
1528 reportHeader( _( "Edge clearance resolution for:" ), a, b, layer, r );
1529
1530 constraint = drcEngine->EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, layer, r );
1531 clearance = constraint.m_Value.Min();
1532
1533 if( compileError )
1534 reportCompileError( r );
1535
1536 r->Report( "" );
1537 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1538 m_frame->StringFromValue( clearance, true ) ) );
1539
1540 r->Flush();
1541 }
1542 }
1543
1544 r = dialog->AddHTMLPage( _( "Physical Clearances" ) );
1545
1546 if( compileError )
1547 {
1548 reportCompileError( r );
1549 }
1550 else if( !drcEngine->HasRulesForConstraintType( PHYSICAL_CLEARANCE_CONSTRAINT ) )
1551 {
1552 r->Report( "" );
1553 r->Report( _( "No 'physical_clearance' constraints defined." ) );
1554 }
1555 else
1556 {
1557 LSET reportLayers = layerIntersection;
1558 bool reported = false;
1559
1560 if( a->IsOnLayer( Edge_Cuts ) )
1561 {
1562 LSET edgeInteractingLayers = bFP ? LSET( { F_CrtYd, B_CrtYd } )
1564 reportLayers |= edgeInteractingLayers;
1565 }
1566
1567 if( b->IsOnLayer( Edge_Cuts ) )
1568 {
1569 LSET edgeInteractingLayers = aFP ? LSET( { F_CrtYd, B_CrtYd } )
1571 reportLayers |= edgeInteractingLayers;
1572 }
1573
1574 for( PCB_LAYER_ID layer : reportLayers )
1575 {
1576 reported = true;
1577 reportHeader( _( "Physical clearance resolution for:" ), a, b, layer, r );
1578
1579 constraint = drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, layer, r );
1580 clearance = constraint.m_Value.Min();
1581
1582 if( constraint.IsNull() )
1583 {
1584 r->Report( "" );
1585 r->Report( wxString::Format( _( "No 'physical_clearance' constraints in effect on %s." ),
1586 m_frame->GetBoard()->GetLayerName( layer ) ) );
1587 }
1588 else
1589 {
1590 r->Report( "" );
1591 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1592 m_frame->StringFromValue( clearance, true ) ) );
1593 }
1594
1595 r->Report( "" );
1596 r->Report( "" );
1597 r->Report( "" );
1598 }
1599
1600 if( !reported )
1601 {
1602 reportHeader( _( "Physical clearance resolution for:" ), a, b, r );
1603 r->Report( "" );
1604 r->Report( _( "Items share no relevant layers. No 'physical_clearance' constraints will "
1605 "be applied." ) );
1606 }
1607 }
1608
1609 if( a->HasHole() || b->HasHole() )
1610 {
1611 PCB_LAYER_ID layer;
1612
1613 if( a->HasHole() && b->IsOnLayer( active ) )
1614 layer = active;
1615 else if( b->HasHole() && a->IsOnLayer( active ) )
1616 layer = active;
1617 else if( a->HasHole() )
1618 layer = b->GetLayer();
1619 else
1620 layer = a->GetLayer();
1621
1622 reportHeader( _( "Physical hole clearance resolution for:" ), a, b, layer, r );
1623
1624 constraint = drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
1625 clearance = constraint.m_Value.Min();
1626
1627 if( compileError )
1628 {
1629 reportCompileError( r );
1630 }
1631 else if( !drcEngine->HasRulesForConstraintType( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT ) )
1632 {
1633 r->Report( "" );
1634 r->Report( _( "No 'physical_hole_clearance' constraints defined." ) );
1635 }
1636 else
1637 {
1638 r->Report( "" );
1639 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1640 m_frame->StringFromValue( clearance, true ) ) );
1641 }
1642 }
1643
1644 r->Flush();
1645
1646 dialog->Raise();
1647 dialog->Show( true );
1648}
1649
1650
1652{
1653#define EVAL_RULES( constraint, a, b, layer, r ) drcEngine->EvalRules( constraint, a, b, layer, r )
1654
1655 wxCHECK( m_frame, 0 );
1656
1657 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1658
1659 wxCHECK( selTool, 0 );
1660
1661 const PCB_SELECTION& selection = selTool->GetSelection();
1662 BOARD_ITEM* item = nullptr;
1663
1664 if( selection.Size() == 1 && selection.GetItem( 0 )->IsBOARD_ITEM() )
1665 {
1666 item = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) );
1667 }
1668 else if( selection.Size() == 0 )
1669 {
1670 static const std::vector<KICAD_T> constraintTypes = {
1671 PCB_PAD_T,
1672 PCB_VIA_T,
1674 PCB_ARC_T,
1675 PCB_ZONE_T,
1679 PCB_TEXT_T,
1682 };
1683
1684 Activate();
1685
1686 item = pickItemForInspection( aEvent, _( "Select item for constraints resolution..." ),
1687 constraintTypes, nullptr );
1688
1689 if( !item )
1690 return 0;
1691 }
1692 else
1693 {
1694 m_frame->ShowInfoBarError( _( "Select a single item for a constraints resolution report." ) );
1695 return 0;
1696 }
1697
1698 DIALOG_BOOK_REPORTER* dialog = m_frame->GetInspectConstraintsDialog();
1699
1700 wxCHECK( dialog, 0 );
1701
1702 dialog->DeleteAllPages();
1703 DRC_CONSTRAINT constraint;
1704
1705 bool compileError = false;
1706 bool courtyardError = false;
1707 std::unique_ptr<DRC_ENGINE> drcEngine = makeDRCEngine( &compileError, &courtyardError );
1708
1709 WX_HTML_REPORT_BOX* r = nullptr;
1710
1711 if( item->Type() == PCB_TRACE_T )
1712 {
1713 r = dialog->AddHTMLPage( _( "Track Width" ) );
1714 reportHeader( _( "Track width resolution for:" ), item, r );
1715
1716 constraint = EVAL_RULES( TRACK_WIDTH_CONSTRAINT, item, nullptr, item->GetLayer(), r );
1717
1718 if( compileError )
1719 reportCompileError( r );
1720
1721 r->Report( "" );
1722 r->Report( wxString::Format( _( "Resolved width constraints: min %s; opt %s; max %s." ),
1723 reportMin( m_frame, constraint ),
1724 reportOpt( m_frame, constraint ),
1725 reportMax( m_frame, constraint ) ) );
1726
1727 r->Flush();
1728 }
1729
1730 if( item->Type() == PCB_VIA_T )
1731 {
1732 r = dialog->AddHTMLPage( _( "Via Diameter" ) );
1733 reportHeader( _( "Via diameter resolution for:" ), item, r );
1734
1735 // PADSTACKS TODO: once we have padstacks we'll need to run this per-layer....
1736 constraint = EVAL_RULES( VIA_DIAMETER_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1737
1738 if( compileError )
1739 reportCompileError( r );
1740
1741 r->Report( "" );
1742 r->Report( wxString::Format( _( "Resolved diameter constraints: min %s; opt %s; max %s." ),
1743 reportMin( m_frame, constraint ),
1744 reportOpt( m_frame, constraint ),
1745 reportMax( m_frame, constraint ) ) );
1746
1747 r->Flush();
1748
1749 r = dialog->AddHTMLPage( _( "Via Annular Width" ) );
1750 reportHeader( _( "Via annular width resolution for:" ), item, r );
1751
1752 // PADSTACKS TODO: once we have padstacks we'll need to run this per-layer....
1753 constraint = EVAL_RULES( ANNULAR_WIDTH_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1754
1755 if( compileError )
1756 reportCompileError( r );
1757
1758 r->Report( "" );
1759 r->Report( wxString::Format( _( "Resolved annular width constraints: min %s; opt %s; max %s." ),
1760 reportMin( m_frame, constraint ),
1761 reportOpt( m_frame, constraint ),
1762 reportMax( m_frame, constraint ) ) );
1763
1764 r->Flush();
1765 }
1766
1767 if( ( item->Type() == PCB_PAD_T && static_cast<PAD*>( item )->GetDrillSize().x > 0 )
1768 || item->Type() == PCB_VIA_T )
1769 {
1770 r = dialog->AddHTMLPage( _( "Hole Size" ) );
1771 reportHeader( _( "Hole size resolution for:" ), item, r );
1772
1773 constraint = EVAL_RULES( HOLE_SIZE_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1774
1775 if( compileError )
1776 reportCompileError( r );
1777
1778 r->Report( "" );
1779 r->Report( wxString::Format( _( "Resolved hole size constraints: min %s; opt %s; max %s." ),
1780 reportMin( m_frame, constraint ),
1781 reportOpt( m_frame, constraint ),
1782 reportMax( m_frame, constraint ) ) );
1783
1784 r->Flush();
1785 }
1786
1787 if( item->Type() == PCB_PAD_T || item->Type() == PCB_SHAPE_T || dynamic_cast<PCB_TRACK*>( item ) )
1788 {
1789 r = dialog->AddHTMLPage( _( "Solder Mask" ) );
1790 reportHeader( _( "Solder mask expansion resolution for:" ), item, r );
1791
1792 constraint = EVAL_RULES( SOLDER_MASK_EXPANSION_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1793
1794 if( compileError )
1795 reportCompileError( r );
1796
1797 r->Report( "" );
1798 r->Report( wxString::Format( _( "Resolved solder mask expansion: %s." ),
1799 reportOpt( m_frame, constraint ) ) );
1800
1801 r->Flush();
1802 }
1803
1804 if( item->Type() == PCB_PAD_T )
1805 {
1806 r = dialog->AddHTMLPage( _( "Solder Paste" ) );
1807 reportHeader( _( "Solder paste absolute clearance resolution for:" ), item, r );
1808
1809 constraint = EVAL_RULES( SOLDER_PASTE_ABS_MARGIN_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1810
1811 if( compileError )
1812 reportCompileError( r );
1813
1814 r->Report( "" );
1815 r->Report( wxString::Format( _( "Resolved solder paste absolute clearance: %s." ),
1816 reportOpt( m_frame, constraint ) ) );
1817
1818 reportHeader( _( "Solder paste relative clearance resolution for:" ), item, r );
1819
1820 constraint = EVAL_RULES( SOLDER_PASTE_REL_MARGIN_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1821
1822 if( compileError )
1823 reportCompileError( r );
1824
1825 r->Report( "" );
1826 r->Report( "" );
1827 r->Report( "" );
1828 r->Report( wxString::Format( _( "Resolved solder paste relative clearance: %s." ),
1829 reportOpt( m_frame, constraint ) ) );
1830
1831 r->Flush();
1832 }
1833
1834 if( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T || item->Type() == PCB_TEXTBOX_T )
1835 {
1836 r = dialog->AddHTMLPage( _( "Text Size" ) );
1837 reportHeader( _( "Text height resolution for:" ), item, r );
1838
1839 constraint = EVAL_RULES( TEXT_HEIGHT_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1840
1841 if( compileError )
1842 reportCompileError( r );
1843
1844 r->Report( "" );
1845 r->Report( wxString::Format( _( "Resolved height constraints: min %s; opt %s; max %s." ),
1846 reportMin( m_frame, constraint ),
1847 reportOpt( m_frame, constraint ),
1848 reportMax( m_frame, constraint ) ) );
1849
1850 r->Report( "" );
1851 r->Report( "" );
1852 r->Report( "" );
1853 reportHeader( _( "Text thickness resolution for:" ), item, r );
1854
1855 constraint = EVAL_RULES( TEXT_THICKNESS_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1856
1857 if( compileError )
1858 reportCompileError( r );
1859
1860 r->Report( "" );
1861 r->Report( wxString::Format( _( "Resolved thickness constraints: min %s; opt %s; max %s." ),
1862 reportMin( m_frame, constraint ),
1863 reportOpt( m_frame, constraint ),
1864 reportMax( m_frame, constraint ) ) );
1865
1866 r->Flush();
1867 }
1868
1869 r = dialog->AddHTMLPage( _( "Keepouts" ) );
1870 reportHeader( _( "Keepout resolution for:" ), item, r );
1871
1872 constraint = EVAL_RULES( DISALLOW_CONSTRAINT, item, nullptr, item->GetLayer(), r );
1873
1874 if( compileError )
1875 reportCompileError( r );
1876
1877 if( courtyardError )
1878 {
1879 r->Report( "" );
1880 r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
1881 + wxS( "&nbsp;&nbsp;" )
1882 + wxS( "<a href='$DRC'>" ) + _( "Run DRC for a full analysis." )
1883 + wxS( "</a>" ) );
1884 }
1885
1886 r->Report( "" );
1887
1888 if( constraint.m_DisallowFlags )
1889 r->Report( _( "Item <b>disallowed</b> at current location." ) );
1890 else
1891 r->Report( _( "Item allowed at current location." ) );
1892
1893 r->Flush();
1894
1895 r = dialog->AddHTMLPage( _( "Assertions" ) );
1896 reportHeader( _( "Assertions for:" ), item, r );
1897
1898 if( compileError )
1899 reportCompileError( r );
1900
1901 if( courtyardError )
1902 {
1903 r->Report( "" );
1904 r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
1905 + wxS( "&nbsp;&nbsp;" )
1906 + wxS( "<a href='$DRC'>" ) + _( "Run DRC for a full analysis." )
1907 + wxS( "</a>" ) );
1908 }
1909
1910 drcEngine->ProcessAssertions( item, []( const DRC_CONSTRAINT* c ){}, r );
1911 r->Flush();
1912
1913 dialog->Raise();
1914 dialog->Show( true );
1915 return 0;
1916}
1917
1918
1920{
1921 wxCHECK( m_frame, 0 );
1922
1923 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1924
1925 wxCHECK( selTool, 0 );
1926
1927 const PCB_SELECTION& selection = selTool->RequestSelection(
1928 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1929 {
1930 // Iterate from the back so we don't have to worry about removals.
1931 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1932 {
1933 BOARD_ITEM* item = aCollector[ i ];
1934
1935 if( !dynamic_cast<FOOTPRINT*>( item ) )
1936 aCollector.Remove( item );
1937 }
1938 } );
1939
1940 if( selection.Size() == 1 )
1941 DiffFootprint( static_cast<FOOTPRINT*>( selection.GetItem( 0 ) ) );
1942 else
1943 m_frame->ShowInfoBarError( _( "Select a footprint to diff with its library equivalent." ) );
1944
1945 return 0;
1946}
1947
1948
1950{
1951 wxCHECK( m_frame, 0 );
1952
1953 wxFileDialog dlg( m_frame, _( "Choose Board to Compare With" ), wxEmptyString,
1954 wxEmptyString, FILEEXT::PcbFileWildcard(),
1955 wxFD_OPEN | wxFD_FILE_MUST_EXIST );
1956
1957 if( dlg.ShowModal() != wxID_OK )
1958 return 0;
1959
1960 wxFileName otherFn( dlg.GetPath() );
1961 otherFn.MakeAbsolute();
1962
1963 const wxString otherPath = otherFn.GetFullPath();
1964
1965 wxFileName projectFn = otherFn;
1966 projectFn.SetExt( FILEEXT::ProjectFileExtension );
1967 const wxString projectPath = projectFn.GetFullPath();
1968
1969 wxFileName activeProjectFn( m_frame->Prj().GetProjectFullName() );
1970 activeProjectFn.MakeAbsolute();
1971
1972 // Refuse the self-compare before touching SETTINGS_MANAGER — attaching the
1973 // active PROJECT to a second BOARD would clobber its m_BoardSettings. Use
1974 // wxFileName::SameAs (path normalization + case folding) rather than a raw
1975 // string compare, matching SCH_INSPECTION_TOOL::CompareSchematic.
1976 if( projectFn.SameAs( activeProjectFn ) )
1977 {
1978 m_frame->ShowInfoBarError(
1979 _( "Select a board file from another project to compare." ) );
1980 return 0;
1981 }
1982
1983 return showBoardComparison( otherPath, projectPath, otherPath );
1984}
1985
1986
1987namespace
1988{
1989// Everything needed to show one board-vs-board comparison: the diff, the clones
1990// of removed live-board items (kept alive because the canvas references them),
1991// and the canvas-context switcher. Moveable so it can be swapped on reload.
1992struct PCB_DIFF_VIEW
1993{
1995 std::vector<std::unique_ptr<BOARD_ITEM>> clones;
1997};
1998
1999
2000PCB_DIFF_VIEW buildPcbDiffView( BOARD* aLive, BOARD* aOther, const wxString& aOtherPath )
2001{
2002 PCB_DIFF_VIEW view;
2003
2004 KICAD_DIFF::PCB_DIFFER differ( aLive, aOther, aOtherPath );
2005 view.result = differ.Diff();
2006
2007 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2008
2009 std::map<KIID, KIGFX::COLOR4D> refOverrides;
2010 std::map<KIID, KIGFX::COLOR4D> compOverrides;
2011 std::map<KIID, KICAD_DIFF::CATEGORY> kiidCategories;
2012
2013 std::function<void( const KICAD_DIFF::ITEM_CHANGE& )> collect =
2014 [&]( const KICAD_DIFF::ITEM_CHANGE& aChange )
2015 {
2016 if( !aChange.id.empty() )
2017 {
2018 const KIID& kiid = aChange.id.back();
2019 kiidCategories[kiid] = KICAD_DIFF::CategoryFor( aChange.kind );
2020
2021 switch( aChange.kind )
2022 {
2023 case KICAD_DIFF::CHANGE_KIND::ADDED: compOverrides[kiid] = theme.added; break;
2025 refOverrides[kiid] = theme.removed;
2026 compOverrides[kiid] = theme.removed;
2027 break;
2029 refOverrides[kiid] = theme.modified;
2030 compOverrides[kiid] = theme.modified;
2031 break;
2032 default:
2033 refOverrides[kiid] = theme.conflict;
2034 compOverrides[kiid] = theme.conflict;
2035 break;
2036 }
2037 }
2038
2039 for( const KICAD_DIFF::ITEM_CHANGE& child : aChange.children )
2040 collect( child );
2041 };
2042
2043 for( const KICAD_DIFF::ITEM_CHANGE& change : view.result.changes )
2044 collect( change );
2045
2046 // Clone removed items from the live board because their originals are
2047 // pinned to the editor's VIEW.
2048 for( const auto& [kiid, color] : refOverrides )
2049 {
2050 if( color != theme.removed )
2051 continue;
2052
2053 if( BOARD_ITEM* found = aLive->ResolveItem( kiid, /*aAllowNullptrReturn=*/true ) )
2054 {
2055 if( BOARD_ITEM* clone = dynamic_cast<BOARD_ITEM*>( found->Clone() ) )
2056 view.clones.emplace_back( clone );
2057 }
2058 }
2059
2060 std::vector<KIGFX::VIEW_ITEM*> extraItems;
2061
2062 for( const std::unique_ptr<BOARD_ITEM>& clone : view.clones )
2063 {
2064 // A footprint draws nothing itself. Its pads, graphics and fields are
2065 // independent view items, so add them too or the clone is invisible.
2066 if( FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( clone.get() ) )
2067 {
2068 std::vector<KIGFX::VIEW_ITEM*> fpItems = KICAD_DIFF::CollectFootprintDiffContextItems( *fp );
2069 extraItems.insert( extraItems.end(), fpItems.begin(), fpItems.end() );
2070 }
2071 else
2072 {
2073 extraItems.push_back( clone.get() );
2074 }
2075 }
2076
2077 view.switcher =
2078 [other = aOther, color = theme.modified, overrides = compOverrides, extras = std::move( extraItems ),
2079 categories = kiidCategories] ( WIDGET_DIFF_CANVAS& aCanvas, const KIID_PATH& )
2080 {
2081 KICAD_DIFF::ConfigurePcbDiffCanvasContext( aCanvas, nullptr, other, color, overrides, extras,
2082 categories );
2083 };
2084
2085 return view;
2086}
2087} // namespace
2088
2089
2090int BOARD_INSPECTION_TOOL::showBoardComparison( const wxString& aOtherPath, const wxString& aProjectPath,
2091 const wxString& aComparisonLabel )
2092{
2093 // Load the comparison board into its own non-active PROJECT so the live
2094 // project settings are never overwritten. A missing .kicad_pro is fine
2095 // (LoadProject returns false but still inserts a defaults-only slot); a
2096 // present-but-malformed .kicad_pro is treated as failure.
2097 SETTINGS_MANAGER* mgr = m_frame->GetSettingsManager();
2098 bool projectLoadOk = mgr && mgr->LoadProject( aProjectPath, false );
2099 PROJECT* otherPrj = mgr ? mgr->GetProject( aProjectPath ) : nullptr;
2100
2101 if( !otherPrj )
2102 {
2103 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
2104 return 0;
2105 }
2106
2107 if( !projectLoadOk && wxFileName( aProjectPath ).FileExists() )
2108 {
2109 mgr->UnloadProject( otherPrj, false );
2110 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
2111 return 0;
2112 }
2113
2115
2116 // Skip editor-frame initialization (drawing sheet singleton, BASE_SCREEN
2117 // globals, DRC engine, connectivity) so the read-only compare cannot leak
2118 // state into the live frame.
2119 BOARD_LOADER::OPTIONS loadOptions;
2120 loadOptions.initialize_after_load = false;
2121
2122 std::unique_ptr<BOARD> otherBoard;
2123
2124 try
2125 {
2126 otherBoard = BOARD_LOADER::Load( aOtherPath, pluginType, otherPrj, loadOptions );
2127 }
2128 catch( const IO_ERROR& ioe )
2129 {
2130 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load %s: %s" ), aOtherPath, ioe.What() ) );
2131 mgr->UnloadProject( otherPrj, false );
2132 return 0;
2133 }
2134 catch( ... )
2135 {
2136 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
2137 mgr->UnloadProject( otherPrj, false );
2138 return 0;
2139 }
2140
2141 if( !otherBoard )
2142 {
2143 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
2144 mgr->UnloadProject( otherPrj, false );
2145 return 0;
2146 }
2147
2148 BOARD* board = m_frame->GetBoard();
2149 PCB_DIFF_VIEW view = buildPcbDiffView( board, otherBoard.get(), aOtherPath );
2150
2151 auto dlgDiff = std::make_unique<DIALOG_KICAD_DIFF>( m_frame, board->GetFileName(), aComparisonLabel, view.result,
2153 KICAD_DIFF::DOCUMENT_GEOMETRY{}, view.switcher );
2154 dlgDiff->ShowModal();
2155
2156 // Destroy the dialog (its canvas drops its references to the board's items)
2157 // before the board is freed, or teardown dereferences freed items.
2158 dlgDiff.reset();
2159
2160 otherBoard->ClearProject();
2161 otherBoard.reset();
2162
2163 mgr->UnloadProject( otherPrj, false );
2164
2165 return 0;
2166}
2167
2168
2170{
2171 wxCHECK( m_frame, 0 );
2172
2173 BOARD* liveBoard = m_frame->GetBoard();
2174
2175 if( !liveBoard || liveBoard->GetFileName().IsEmpty() )
2176 {
2177 m_frame->ShowInfoBarError( _( "Save the board before comparing against local history." ) );
2178 return 0;
2179 }
2180
2181 const wxString projectPath = m_frame->Prj().GetProjectPath();
2182 LOCAL_HISTORY& history = m_frame->Kiway().LocalHistory();
2183
2184 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> snapshots = history.GetSnapshots( projectPath );
2185
2186 if( snapshots.empty() )
2187 {
2188 m_frame->ShowInfoBarError( _( "No local history snapshots for this project." ) );
2189 return 0;
2190 }
2191
2192 // The history repo stores files by project-relative path with forward slashes.
2193 wxFileName boardFn( liveBoard->GetFileName() );
2194 boardFn.MakeRelativeTo( projectPath );
2195 const wxString relPath = boardFn.GetFullPath( wxPATH_UNIX );
2196
2197 wxFileName projFn( m_frame->Prj().GetProjectFullName() );
2198 projFn.MakeRelativeTo( projectPath );
2199 const wxString projRel = projFn.GetFullPath( wxPATH_UNIX );
2200
2201 // One entry per distinct board version: newest-first, skipping commits with
2202 // no board or whose board content matches the previously kept one. This
2203 // drops schematic-only saves and collapses runs that left the board untouched.
2204 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> boardSnapshots;
2205 wxString prevFingerprint;
2206
2207 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
2208 {
2209 wxString fingerprint = history.TreeFingerprint( projectPath, s.hash, wxS( ".kicad_pcb" ) );
2210
2211 if( fingerprint.IsEmpty() || fingerprint == prevFingerprint )
2212 continue;
2213
2214 prevFingerprint = fingerprint;
2215 boardSnapshots.push_back( s );
2216 }
2217
2218 if( boardSnapshots.empty() )
2219 {
2220 m_frame->ShowInfoBarError( _( "No local history snapshots change this board." ) );
2221 return 0;
2222 }
2223
2224 snapshots = std::move( boardSnapshots );
2225
2226 SETTINGS_MANAGER* mgr = m_frame->GetSettingsManager();
2227
2228 std::vector<wxString> labels;
2229
2230 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
2231 {
2232 wxString summary = s.summary.IsEmpty() ? s.message.BeforeFirst( '\n' ) : s.summary;
2233 labels.push_back( wxString::Format( wxS( "%s (%s)" ), summary, s.hash.Left( 8 ) ) );
2234 }
2235
2236 // State for the revision currently shown. Swapped on each dropdown change.
2237 std::unique_ptr<BOARD> curBoard;
2238 PROJECT* curPrj = nullptr;
2239 wxString curTempDir;
2240
2241 auto cleanupCurrent =
2242 [&]()
2243 {
2244 if( curBoard )
2245 {
2246 curBoard->ClearProject();
2247 curBoard.reset();
2248 }
2249
2250 // Skip if the project was already evicted from the manager.
2251 if( curPrj && mgr->IsProjectLoaded( curPrj ) )
2252 mgr->UnloadProject( curPrj, false );
2253
2254 curPrj = nullptr;
2255
2256 if( !curTempDir.IsEmpty() )
2257 {
2258 wxFileName::Rmdir( curTempDir, wxPATH_RMDIR_RECURSIVE );
2259 curTempDir.Clear();
2260 }
2261 };
2262
2263 // Extract + load snapshot aIndex into the out-params. Cleans up its own temp
2264 // on any failure.
2265 auto loadRevision =
2266 [&]( int aIndex, std::unique_ptr<BOARD>& aBoard, PROJECT*& aPrj, wxString& aTempDir ) -> bool
2267 {
2268 const wxString hash = snapshots[aIndex].hash;
2269 wxFileName dirFn;
2270 dirFn.AssignDir( wxFileName::GetTempDir() );
2271 dirFn.AppendDir( wxS( "kicad-history-" ) + hash.Left( 8 ) );
2272 const wxString tempDir = dirFn.GetPath();
2273
2274 // Extract just the board and project file (its real net classes and
2275 // design settings), skipping the schematic, 3D models, gerbers, etc.
2276 if( !history.ExtractAllFilesAtCommit( projectPath, hash, tempDir,
2277 { wxS( ".kicad_pcb" ), wxS( ".kicad_pro" ) } ) )
2278 {
2279 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2280 return false;
2281 }
2282
2283 const wxString boardPath = tempDir + wxS( "/" ) + relPath;
2284 const wxString proPath = tempDir + wxS( "/" ) + projRel;
2285
2286 mgr->LoadProject( proPath, false );
2287 PROJECT* prj = mgr->GetProject( proPath );
2288
2289 if( !prj )
2290 {
2291 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2292 return false;
2293 }
2294
2295 BOARD_LOADER::OPTIONS loadOptions;
2296 loadOptions.initialize_after_load = false;
2297
2298 std::unique_ptr<BOARD> loaded;
2299
2300 try
2301 {
2302 loaded = BOARD_LOADER::Load( boardPath,
2304 prj, loadOptions );
2305 }
2306 catch( ... )
2307 {
2308 // A historical board may be malformed or in a format this build cannot parse. Skip it
2309 // rather than letting the throw escape.
2310 mgr->UnloadProject( prj, false );
2311 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2312 return false;
2313 }
2314
2315 if( !loaded )
2316 {
2317 mgr->UnloadProject( prj, false );
2318 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2319 return false;
2320 }
2321
2322 aBoard = std::move( loaded );
2323 aPrj = prj;
2324 aTempDir = tempDir;
2325 return true;
2326 };
2327
2328 // Load a revision and build its diff view, guarding both the parse and the
2329 // diff so a bad snapshot is skipped instead of crashing.
2330 auto loadView =
2331 [&]( int aIndex, std::unique_ptr<BOARD>& aBoard, PROJECT*& aPrj, wxString& aTempDir,
2332 PCB_DIFF_VIEW& aView ) -> bool
2333 {
2334 if( !loadRevision( aIndex, aBoard, aPrj, aTempDir ) )
2335 return false;
2336
2337 try
2338 {
2339 aView = buildPcbDiffView( liveBoard, aBoard.get(), aTempDir + wxS( "/" ) + relPath );
2340 }
2341 catch( ... )
2342 {
2343 aBoard->ClearProject();
2344 aBoard.reset();
2345 mgr->UnloadProject( aPrj, false );
2346 aPrj = nullptr;
2347 wxFileName::Rmdir( aTempDir, wxPATH_RMDIR_RECURSIVE );
2348 aTempDir.Clear();
2349 return false;
2350 }
2351
2352 return true;
2353 };
2354
2355 PCB_DIFF_VIEW view;
2356 int startIndex = 0;
2357
2358 if( !loadView( 0, curBoard, curPrj, curTempDir, view ) )
2359 {
2360 m_frame->ShowInfoBarError( _( "Could not compare against the selected snapshot." ) );
2361 return 0;
2362 }
2363
2364 // Default to the first commit back from HEAD that actually differs from the
2365 // current board, so opening lands on real changes rather than an in-sync HEAD.
2366 while( view.result.Empty() && startIndex + 1 < static_cast<int>( snapshots.size() ) )
2367 {
2368 std::unique_ptr<BOARD> nextBoard;
2369 PROJECT* nextPrj = nullptr;
2370 wxString nextTempDir;
2371 PCB_DIFF_VIEW nextView;
2372
2373 if( !loadView( startIndex + 1, nextBoard, nextPrj, nextTempDir, nextView ) )
2374 break;
2375
2376 cleanupCurrent();
2377 view = std::move( nextView );
2378 curBoard = std::move( nextBoard );
2379 curPrj = nextPrj;
2380 curTempDir = nextTempDir;
2381 startIndex++;
2382 }
2383
2384 auto dlgDiff = std::make_unique<DIALOG_KICAD_DIFF>( m_frame, liveBoard->GetFileName(), labels[startIndex],
2386 KICAD_DIFF::DOCUMENT_GEOMETRY{}, view.switcher );
2387
2388 dlgDiff->SetRevisionChooser(
2389 labels, startIndex,
2390 [&]( int aIndex )
2391 {
2392 std::unique_ptr<BOARD> newBoard;
2393 PROJECT* newPrj = nullptr;
2394 wxString newTempDir;
2395 PCB_DIFF_VIEW newView;
2396
2397 if( !loadView( aIndex, newBoard, newPrj, newTempDir, newView ) )
2398 {
2399 m_frame->ShowInfoBarError( _( "Could not compare against the selected snapshot." ) );
2400 return;
2401 }
2402
2403 dlgDiff->Reload( liveBoard->GetFileName(), labels[aIndex], newView.result,
2404 /*aReferenceGeometry=*/{}, /*aComparisonGeometry=*/{}, newView.switcher, {} );
2405
2406 // The canvas now references newBoard, so the previous one can go.
2407 cleanupCurrent();
2408 view = std::move( newView );
2409 curBoard = std::move( newBoard );
2410 curPrj = newPrj;
2411 curTempDir = newTempDir;
2412 } );
2413
2414 dlgDiff->ShowModal();
2415
2416 // Destroy the dialog before the board it references is freed.
2417 dlgDiff.reset();
2418
2419 cleanupCurrent();
2420 return 0;
2421}
2422
2423
2425{
2426 wxCHECK( m_frame, 0 );
2427
2428 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2429
2430 wxCHECK( selTool, 0 );
2431
2432 const PCB_SELECTION& selection = selTool->GetSelection();
2433
2434 if( selection.Size() != 1 || selection.Front()->Type() != PCB_FOOTPRINT_T )
2435 {
2436 m_frame->ShowInfoBarError( _( "Select a footprint for a footprint associations report." ) );
2437 return 0;
2438 }
2439
2440 DIALOG_FOOTPRINT_ASSOCIATIONS dlg( m_frame, static_cast<FOOTPRINT*>( selection.Front() ) );
2441
2442 dlg.ShowModal();
2443
2444 return 0;
2445}
2446
2447
2448void BOARD_INSPECTION_TOOL::DiffFootprint( FOOTPRINT* aFootprint, wxTopLevelWindow* aReparentTo )
2449{
2450 DIALOG_BOOK_REPORTER* dialog = m_frame->GetFootprintDiffDialog();
2451
2452 wxCHECK( dialog, /* void */ );
2453
2454 dialog->DeleteAllPages();
2455 dialog->SetUserItemID( aFootprint->m_Uuid );
2456
2457 LIB_ID fpID = aFootprint->GetFPID();
2458 wxString libName = fpID.GetLibNickname();
2459 wxString fpName = fpID.GetLibItemName();
2460 WX_HTML_REPORT_BOX* r = nullptr;
2461
2462 r = dialog->AddHTMLPage( _( "Summary" ) );
2463
2464 r->Report( wxS( "<h7>" ) + _( "Board vs library diff for:" ) + wxS( "</h7>" ) );
2465 r->Report( wxS( "<ul><li>" ) + EscapeHTML( getItemDescription( aFootprint ) ) + wxS( "</li>" )
2466 + wxS( "<li>" ) + _( "Library: " ) + EscapeHTML( libName ) + wxS( "</li>" )
2467 + wxS( "<li>" ) + _( "Library item: " ) + EscapeHTML( fpName ) + wxS( "</li></ul>" ) );
2468
2469 r->Report( "" );
2470
2471 PROJECT* project = aFootprint->GetBoard()->GetProject();
2473
2474 if( !adapter->HasLibrary( libName, false ) )
2475 {
2476 r->Report( _( "The library is not included in the current configuration." )
2477 + wxS( "&nbsp;&nbsp;&nbsp" )
2478 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Footprint Libraries" )
2479 + wxS( "</a>" ) );
2480
2481 }
2482 else if( !adapter->HasLibrary( libName, true ) )
2483 {
2484 r->Report( _( "The library is not enabled in the current configuration." )
2485 + wxS( "&nbsp;&nbsp;&nbsp" )
2486 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Footprint Libraries" )
2487 + wxS( "</a>" ) );
2488
2489 }
2490 else
2491 {
2492 std::shared_ptr<FOOTPRINT> libFootprint;
2493
2494 try
2495 {
2496 libFootprint.reset( adapter->LoadFootprint( libName, fpName, true ) );
2497 }
2498 catch( const IO_ERROR& )
2499 {
2500 }
2501
2502 if( !libFootprint )
2503 {
2504 r->Report( wxString::Format( _( "The library no longer contains the item %s." ),
2505 fpName) );
2506 }
2507 else
2508 {
2509 if( !aFootprint->FootprintNeedsUpdate( libFootprint.get(), 0, r ) )
2510 r->Report( _( "No relevant differences detected." ) );
2511
2512 wxPanel* panel = dialog->AddBlankPage( _( "Visual" ) );
2514
2515 diff->DisplayDiff( aFootprint, libFootprint );
2516 }
2517 }
2518
2519 r->Flush();
2520
2521 if( aReparentTo )
2522 KIPLATFORM::UI::ReparentWindow( dialog, aReparentTo );
2523 else
2524 dialog->Raise();
2525
2526 dialog->Show( true );
2527}
2528
2529
2531{
2532 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
2533
2534 FOOTPRINT_DIFF_WIDGET* diffWidget = new FOOTPRINT_DIFF_WIDGET( aParentPanel, m_frame->Kiway() );
2535
2536 sizer->Add( diffWidget, 1, wxEXPAND | wxALL, 5 );
2537 aParentPanel->SetSizer( sizer );
2538 aParentPanel->Layout();
2539
2540 return diffWidget;
2541}
2542
2543
2545{
2546 BOARD_ITEM* item = aEvent.Parameter<BOARD_ITEM*>();
2547
2548 m_frame->m_ProbingSchToPcb = true; // recursion guard
2549 {
2550 m_toolMgr->RunAction( ACTIONS::selectionClear );
2551
2552 if( item )
2553 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, item );
2554 }
2555 m_frame->m_ProbingSchToPcb = false;
2556
2557 bool request3DviewRedraw = frame()->GetPcbNewSettings()->m_Display.m_Live3DRefresh;
2558
2559 if( item && item->Type() != PCB_FOOTPRINT_T )
2560 request3DviewRedraw = false;
2561
2562 // Update 3D viewer highlighting
2563 if( request3DviewRedraw )
2564 m_frame->Update3DView( false, true );
2565
2566 return 0;
2567}
2568
2569
2570 bool BOARD_INSPECTION_TOOL::highlightNet( const VECTOR2D& aPosition, bool aUseSelection )
2571{
2572 BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
2574 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2575
2576 int net = -1;
2577 bool enableHighlight = false;
2578
2579 if( aUseSelection )
2580 {
2581 const PCB_SELECTION& selection = selectionTool->GetSelection();
2582 std::set<int> netcodes;
2583
2584 for( EDA_ITEM* item : selection )
2585 {
2586 if( BOARD_CONNECTED_ITEM* ci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2587 netcodes.insert( ci->GetNetCode() );
2588 }
2589
2590 enableHighlight = !netcodes.empty();
2591
2592 if( enableHighlight && netcodes.size() > 1 )
2593 {
2594 // If we are doing a multi-highlight, cross-probing back and other stuff is not
2595 // yet supported
2596 settings->SetHighlight( netcodes );
2597 board->ResetNetHighLight();
2598
2599 for( int multiNet : netcodes )
2600 board->SetHighLightNet( multiNet, true );
2601
2602 board->HighLightON();
2603 m_toolMgr->GetView()->UpdateAllLayersColor();
2604 m_currentlyHighlighted = netcodes;
2605 return true;
2606 }
2607 else if( enableHighlight )
2608 {
2609 net = *netcodes.begin();
2610 }
2611 }
2612
2613 // If we didn't get a net to highlight from the selection, use the cursor
2614 if( net < 0 )
2615 {
2616 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
2617 guide.SetIgnoreZoneFills( false );
2618 guide.SetIgnoreNoNets( true );
2619
2620 PCB_LAYER_ID activeLayer = static_cast<PCB_LAYER_ID>( view()->GetTopLayer() );
2621 guide.SetPreferredLayer( activeLayer );
2622
2623 GENERAL_COLLECTOR collector;
2624 collector.Collect( board, { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, PCB_SHAPE_T }, aPosition,
2625 guide );
2626
2627 if( collector.GetCount() == 0 )
2628 collector.Collect( board, { PCB_ZONE_T }, aPosition, guide );
2629
2630 // Apply the active selection filter, except we want to allow picking locked items for
2631 // highlighting even if the user has disabled them for selection
2632 PCB_SELECTION_FILTER_OPTIONS& filter = selectionTool->GetFilter();
2633
2634 bool saved = filter.lockedItems;
2635 filter.lockedItems = true;
2636
2637 selectionTool->FilterCollectedItems( collector, true, nullptr );
2638
2639 filter.lockedItems = saved;
2640
2641 // Clear the previous highlight
2642 //m_frame->SendMessageToEESCHEMA( nullptr );
2643
2644 bool highContrast = settings->GetHighContrast();
2645 PCB_LAYER_ID contrastLayer = settings->GetPrimaryHighContrastLayer();
2646
2647 for( int i = collector.GetCount() - 1; i >= 0; i-- )
2648 {
2649 LSET itemLayers = collector[i]->GetLayerSet();
2650
2651 if( ( itemLayers & LSET::AllCuMask() ).none() ||
2652 ( highContrast && !itemLayers.Contains( contrastLayer ) ) )
2653 {
2654 collector.Remove( i );
2655 continue;
2656 }
2657 }
2658
2659 enableHighlight = ( collector.GetCount() > 0 );
2660
2661 // Obtain net code for the clicked item
2662 if( enableHighlight )
2663 {
2664 BOARD_CONNECTED_ITEM* targetItem = static_cast<BOARD_CONNECTED_ITEM*>( collector[0] );
2665
2666 if( targetItem->Type() == PCB_PAD_T )
2667 m_frame->SendCrossProbeItem( targetItem );
2668
2669 net = targetItem->GetNetCode();
2670 }
2671 }
2672
2673 const std::set<int>& netcodes = settings->GetHighlightNetCodes();
2674
2675 if( !aUseSelection && net >= 0 && netcodes.size() == 1 && netcodes.contains( net ) && settings->IsHighlightEnabled() )
2676 {
2677 if( BOARD* board2 = m_frame->GetBoard() )
2678 {
2679 if( NETINFO_ITEM* netinfo = board2->FindNet( net ) )
2680 {
2681 wxString sig = netinfo->GetNetChain();
2682 if( !sig.IsEmpty() )
2683 {
2684 int count = 0;
2685 for( NETINFO_ITEM* n : board2->GetNetInfo() )
2686 if( n->GetNetChain() == sig )
2687 count++;
2688
2689 if( count > 1 )
2690 {
2691 std::set<int> sigCodes;
2692
2693
2694 for( NETINFO_ITEM* n : board2->GetNetInfo() )
2695 if( n->GetNetChain() == sig )
2696 sigCodes.insert( n->GetNetCode() );
2697
2698 settings->SetHighlight( sigCodes, true );
2699 m_toolMgr->GetView()->UpdateAllLayersColor();
2700 m_currentlyHighlighted = sigCodes;
2702
2703 board2->ResetNetHighLight();
2704 for( int c : sigCodes )
2705 board2->SetHighLightNet( c, true );
2706 board2->HighLightON();
2707
2708 if( auto pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2709 pcbSettings->SetHighlightedNetChain( sig );
2710
2711 return true;
2712 }
2713 }
2714 }
2715 }
2716
2717 enableHighlight = !settings->IsHighlightEnabled();
2718 }
2719 else if( !aUseSelection && netcodes.size() == 1 && netcodes.contains( net ) )
2720 {
2721 enableHighlight = !settings->IsHighlightEnabled();
2722 }
2723
2724 if( enableHighlight != settings->IsHighlightEnabled() || !netcodes.count( net ) )
2725 {
2726 if( !netcodes.empty() )
2727 m_lastHighlighted = netcodes;
2728
2729 settings->SetHighlight( enableHighlight, net );
2730 m_toolMgr->GetView()->UpdateAllLayersColor();
2731 }
2732
2733 // Store the highlighted netcode in the current board (for dialogs for instance)
2734 if( enableHighlight && net >= 0 )
2735 {
2736 m_currentlyHighlighted = netcodes;
2737 board->SetHighLightNet( net );
2738 board->HighLightON();
2739
2740 NETINFO_ITEM* netinfo = board->FindNet( net );
2741
2742 if( netinfo )
2743 {
2744 std::vector<MSG_PANEL_ITEM> items;
2745 netinfo->GetMsgPanelInfo( m_frame, items );
2746 m_frame->SetMsgPanel( items );
2747 m_frame->SendCrossProbeNetName( netinfo->GetNetname() );
2748 }
2749 }
2750 else
2751 {
2752 m_currentlyHighlighted.clear();
2753 board->ResetNetHighLight();
2754 m_frame->SetMsgPanel( board );
2755 m_frame->SendCrossProbeNetName( "" );
2756 }
2757
2758 return true;
2759}
2760
2761
2763{
2764 int netcode = aEvent.Parameter<int>();
2765 KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings();
2766 const std::set<int>& highlighted = settings->GetHighlightNetCodes();
2767
2768 if( netcode > 0 )
2769 {
2770 m_lastHighlighted = highlighted;
2771 settings->SetHighlight( true, netcode );
2772 m_toolMgr->GetView()->UpdateAllLayersColor();
2773 m_currentlyHighlighted.clear();
2774 m_currentlyHighlighted.insert( netcode );
2775
2776 // If this net belongs to a multi-net chain and was already highlighted, escalate to chain highlight
2777 if( BOARD* board = m_frame->GetBoard() )
2778 {
2779 if( NETINFO_ITEM* net = board->FindNet( netcode ) )
2780 {
2781 wxString sig = net->GetNetChain();
2782 if( !sig.IsEmpty() )
2783 {
2784 int count = 0;
2785 for( NETINFO_ITEM* n : board->GetNetInfo() )
2786 if( n->GetNetChain() == sig )
2787 count++;
2788 bool alreadyHighlighted = highlighted.count( netcode );
2789 if( count > 1 && alreadyHighlighted )
2790 {
2791 if( m_highlightedNetChain != sig )
2792 {
2793 TOOL_EVENT sigEvt = PCB_ACTIONS::highlightNetChain.MakeEvent();
2794 HighlightNetChain( sigEvt );
2795 }
2796 }
2797 }
2798 }
2799 }
2800 }
2801 else if( aEvent.IsAction( &PCB_ACTIONS::highlightNetSelection ) )
2802 {
2803 // Highlight selection (cursor position will be ignored)
2804 highlightNet( getViewControls()->GetMousePosition(), true );
2805 }
2806 else if( aEvent.IsAction( &PCB_ACTIONS::toggleLastNetHighlight ) )
2807 {
2808 std::set<int> temp = highlighted;
2809 settings->SetHighlight( m_lastHighlighted );
2810 m_toolMgr->GetView()->UpdateAllLayersColor();
2812 m_lastHighlighted = std::move( temp );
2813 }
2814 else if( aEvent.IsAction( &PCB_ACTIONS::toggleNetHighlight ) )
2815 {
2816 bool turnOn = highlighted.empty() && !m_currentlyHighlighted.empty();
2817 settings->SetHighlight( m_currentlyHighlighted, turnOn );
2818 m_toolMgr->GetView()->UpdateAllLayersColor();
2819 }
2820 else // Highlight the net belonging to the item under the cursor
2821 {
2822 highlightNet( getViewControls()->GetMousePosition(), false );
2823 }
2824
2825 return 0;
2826}
2827
2828
2830{
2832 VECTOR2D cursorPos = controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
2833 BOARD_ITEM* item = nullptr;
2834
2835 {
2836 // Collect nearest connectable item at cursor position
2837 BOARD* board = m_frame->GetBoard();
2838 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
2839 GENERAL_COLLECTOR collector;
2840 collector.Collect( board, { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, PCB_SHAPE_T }, cursorPos, guide );
2841
2842 if( collector.GetCount() > 0 )
2843 item = collector[0];
2844 }
2845
2846 wxString sig;
2847
2848 if( item )
2849 {
2850 NETINFO_ITEM* net = nullptr;
2851
2852 if( PAD* pad = dynamic_cast<PAD*>( item ) )
2853 net = pad->GetNet();
2854 else if( BOARD_CONNECTED_ITEM* ci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2855 net = ci->GetNet();
2856
2857 if( net )
2858 sig = net->GetNetChain();
2859 }
2860
2861 KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings();
2862
2863 // If no chain under cursor and a chain is currently highlighted, toggle off
2864 if( sig.IsEmpty() && !m_highlightedNetChain.IsEmpty() )
2865 {
2866 m_highlightedNetChain.clear();
2867 settings->SetHighlight( false );
2868 m_currentlyHighlighted.clear();
2869 m_toolMgr->GetView()->UpdateAllLayersColor();
2870
2871 if( KIGFX::PCB_RENDER_SETTINGS* pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2872 pcbSettings->SetHighlightedNetChain( wxString() );
2873
2874 return 0;
2875 }
2876
2877
2878 // If same chain already highlighted, clear highlight
2879 if( !sig.IsEmpty() && sig == m_highlightedNetChain )
2880 {
2881 m_highlightedNetChain.clear();
2882 settings->SetHighlight( false );
2883 m_currentlyHighlighted.clear();
2884 m_toolMgr->GetView()->UpdateAllLayersColor();
2885
2886 if( KIGFX::PCB_RENDER_SETTINGS* pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2887 pcbSettings->SetHighlightedNetChain( wxString() );
2888
2889 return 0;
2890 }
2891
2892 // If we have a net highlight active but no chain highlight, convert to chain highlight
2893 if( !sig.IsEmpty() && m_highlightedNetChain.IsEmpty() && !m_currentlyHighlighted.empty() )
2894 {
2895 // Determine chain from first highlighted net if cursor item had no chain
2896 if( sig.IsEmpty() )
2897 {
2898 int firstCode = *m_currentlyHighlighted.begin();
2899
2900 if( NETINFO_ITEM* net = m_frame->GetBoard()->FindNet( firstCode ) )
2901 sig = net->GetNetChain();
2902 }
2903 }
2904
2906
2907 if( KIGFX::PCB_RENDER_SETTINGS* pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2908 pcbSettings->SetHighlightedNetChain( sig );
2909
2910 std::set<int> codes;
2911
2912 if( !sig.IsEmpty() )
2913 {
2914 for( NETINFO_ITEM* net : m_frame->GetBoard()->GetNetInfo() )
2915 {
2916 if( net->GetNetChain() == sig )
2917 codes.insert( net->GetNetCode() );
2918 }
2919 }
2920
2921 settings->SetHighlight( codes, true );
2922 m_toolMgr->GetView()->UpdateAllLayersColor();
2923 m_currentlyHighlighted = codes;
2924
2925 return 0;
2926}
2927
2928
2930{
2931 if( m_highlightedNetChain.IsEmpty() )
2932 return 0;
2933
2934 // Parameters are passed as a single pair<old,new>
2935 std::pair<wxString, wxString> ids = aEvent.Parameter<std::pair<wxString, wxString>>();
2936 KIID oldId( ids.first );
2937 KIID newId( ids.second );
2938 m_frame->GetBoard()->ReplaceNetChainTerminalPad( m_highlightedNetChain, oldId, newId );
2939 return 0;
2940}
2941
2942
2944{
2945 BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
2946 KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings();
2947
2948 m_currentlyHighlighted.clear();
2949 m_lastHighlighted.clear();
2950
2951 board->ResetNetHighLight();
2952 settings->SetHighlight( false );
2953
2954 // Also clear any chain-specific state
2955 if( KIGFX::PCB_RENDER_SETTINGS* pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2956 pcbSettings->SetHighlightedNetChain( wxString() );
2957
2958 m_toolMgr->GetView()->UpdateAllLayersColor();
2959 m_frame->SetMsgPanel( board );
2960 m_frame->SendCrossProbeNetName( "" );
2961 return 0;
2962}
2963
2964
2966{
2967 PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
2968
2969 // Deactivate other tools; particularly important if another PICKER is currently running
2970 Activate();
2971
2972 picker->SetCursor( KICURSOR::BULLSEYE );
2973 picker->SetSnapping( false );
2974 picker->ClearHandlers();
2975
2976 picker->SetClickHandler(
2977 [this]( const VECTOR2D& pt ) -> bool
2978 {
2979 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2980
2981 m_toolMgr->RunAction( ACTIONS::selectionClear );
2983 EDIT_TOOL::PadFilter );
2984
2985 PCB_SELECTION& selection = selectionTool->GetSelection();
2986
2987 if( selection.Empty() )
2988 {
2990 EDIT_TOOL::FootprintFilter );
2991 selection = selectionTool->GetSelection();
2992 }
2993
2994 if( selection.Empty() )
2995 {
2996 // Clear the previous local ratsnest if we click off all items
2997 for( FOOTPRINT* fp : getModel<BOARD>()->Footprints() )
2998 {
2999 for( PAD* pad : fp->Pads() )
3000 pad->SetLocalRatsnestVisible( displayOptions().m_ShowGlobalRatsnest );
3001 }
3002 }
3003 else
3004 {
3005 for( EDA_ITEM* item : selection )
3006 {
3007 if( PAD* pad = dyn_cast<PAD*>( item) )
3008 {
3009 pad->SetLocalRatsnestVisible( !pad->GetLocalRatsnestVisible() );
3010 }
3011 else if( FOOTPRINT* fp = dyn_cast<FOOTPRINT*>( item) )
3012 {
3013 if( !fp->Pads().empty() )
3014 {
3015 bool enable = !fp->Pads()[0]->GetLocalRatsnestVisible();
3016
3017 for( PAD* childPad : fp->Pads() )
3018 childPad->SetLocalRatsnestVisible( enable );
3019 }
3020 }
3021 }
3022 }
3023
3024 m_toolMgr->GetView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
3025
3026 return true;
3027 } );
3028
3029 picker->SetFinalizeHandler(
3030 [this]( int aCondition )
3031 {
3032 if( aCondition != PCB_PICKER_TOOL::END_ACTIVATE )
3033 {
3034 for( FOOTPRINT* fp : getModel<BOARD>()->Footprints() )
3035 {
3036 for( PAD* pad : fp->Pads() )
3037 pad->SetLocalRatsnestVisible( displayOptions().m_ShowGlobalRatsnest );
3038 }
3039 }
3040 } );
3041
3042 m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
3043
3044 return 0;
3045}
3046
3047
3049{
3050 VECTOR2I delta = aEvent.Parameter<VECTOR2I>();
3051
3052 if( delta == VECTOR2I() )
3053 {
3054 // We can delete the existing map to force a recalculation
3055 delete m_dynamicData;
3056 m_dynamicData = nullptr;
3057 }
3058
3059 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3060 PCB_SELECTION& selection = selectionTool->GetSelection();
3061 std::shared_ptr<CONNECTIVITY_DATA> connectivity = getModel<BOARD>()->GetConnectivity();
3062
3063 if( selection.Empty() )
3064 {
3065 connectivity->ClearLocalRatsnest();
3066 delete m_dynamicData;
3067 m_dynamicData = nullptr;
3068 }
3069 else
3070 {
3072 }
3073
3074 return 0;
3075}
3076
3077
3079{
3080 getModel<BOARD>()->GetConnectivity()->ClearLocalRatsnest();
3081 delete m_dynamicData;
3082 m_dynamicData = nullptr;
3083
3084 return 0;
3085}
3086
3087
3089{
3090 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3091 SELECTION& selection = selectionTool->GetSelection();
3092 std::shared_ptr<CONNECTIVITY_DATA> connectivity = board()->GetConnectivity();
3093 std::vector<BOARD_ITEM*> items;
3094 std::deque<EDA_ITEM*> queued_items( selection.begin(), selection.end() );
3095
3096 for( std::size_t i = 0; i < queued_items.size(); ++i )
3097 {
3098 if( !queued_items[i]->IsBOARD_ITEM() )
3099 continue;
3100
3101 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( queued_items[i] );
3102
3103 wxCHECK2( item, continue );
3104
3105 if( item->Type() == PCB_FOOTPRINT_T )
3106 {
3107 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
3108 {
3109 if( pad->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
3110 items.push_back( pad );
3111 }
3112 }
3113 else if( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T )
3114 {
3115 item->RunOnChildren( [ &queued_items ]( BOARD_ITEM *aItem )
3116 {
3117 queued_items.push_back( aItem );
3118 },
3120 }
3121 else if( BOARD_CONNECTED_ITEM* boardItem = dyn_cast<BOARD_CONNECTED_ITEM*>( item ) )
3122 {
3123 if( boardItem->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
3124 items.push_back( boardItem );
3125 }
3126 }
3127
3128 if( items.empty() || std::none_of( items.begin(), items.end(),
3129 []( const BOARD_ITEM* aItem )
3130 {
3131 return( aItem->Type() == PCB_TRACE_T
3132 || aItem->Type() == PCB_PAD_T
3133 || aItem->Type() == PCB_ARC_T
3134 || aItem->Type() == PCB_ZONE_T
3135 || aItem->Type() == PCB_FOOTPRINT_T
3136 || aItem->Type() == PCB_VIA_T
3137 || aItem->Type() == PCB_SHAPE_T );
3138 } ) )
3139 {
3140 return;
3141 }
3142
3143 if( !m_dynamicData )
3144 {
3145 m_dynamicData = new CONNECTIVITY_DATA( board()->GetConnectivity(), items, true );
3146 connectivity->BlockRatsnestItems( items );
3147 }
3148 else
3149 {
3150 m_dynamicData->Move( aDelta );
3151 }
3152
3153 connectivity->ComputeLocalRatsnest( items, m_dynamicData );
3154}
3155
3156
3158{
3159 doHideRatsnestNet( aEvent.Parameter<int>(), true );
3160 return 0;
3161}
3162
3163
3165{
3166 doHideRatsnestNet( aEvent.Parameter<int>(), false );
3167 return 0;
3168}
3169
3170
3171void BOARD_INSPECTION_TOOL::doHideRatsnestNet( int aNetCode, bool aHide )
3172{
3174 m_toolMgr->GetView()->GetPainter()->GetSettings() );
3175
3176 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3177 SELECTION& selection = selectionTool->GetSelection();
3178
3179 if( aNetCode <= 0 && !selection.Empty() )
3180 {
3181 for( EDA_ITEM* item : selection )
3182 {
3183 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
3184 {
3185 if( bci->GetNetCode() > 0 )
3186 doHideRatsnestNet( bci->GetNetCode(), aHide );
3187 }
3188 }
3189
3190 return;
3191 }
3192
3193 if( aHide )
3194 rs->GetHiddenNets().insert( aNetCode );
3195 else
3196 rs->GetHiddenNets().erase( aNetCode );
3197
3198 if( !m_frame->GetAppearancePanel()->IsTogglingNetclassRatsnestVisibility() )
3199 {
3200 m_frame->GetCanvas()->RedrawRatsnest();
3201 m_frame->GetCanvas()->Refresh();
3202
3203 m_frame->GetAppearancePanel()->OnNetVisibilityChanged( aNetCode, !aHide );
3204 }
3205}
3206
3207
3209{
3213
3222
3231
3234}
std::function< void(const VECTOR2I &, GENERAL_COLLECTOR &, PCB_SELECTION_TOOL *)> CLIENT_SELECTION_FILTER
Definition actions.h:33
#define EVAL_RULES(constraint, a, b, layer, r)
wxString reportMax(PCB_BASE_FRAME *aFrame, DRC_CONSTRAINT &aConstraint)
bool isNPTHPad(BOARD_ITEM *aItem)
wxString reportMin(PCB_BASE_FRAME *aFrame, DRC_CONSTRAINT &aConstraint)
wxString reportOpt(PCB_BASE_FRAME *aFrame, DRC_CONSTRAINT &aConstraint)
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition actions.h:223
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition actions.h:213
static TOOL_ACTION pickerTool
Definition actions.h:249
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
Default constructor.
void SetTitle(const wxString &aTitle) override
Set title for the menu.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
void InspectDRCError(const std::shared_ptr< RC_ITEM > &aDRCItem)
Show the clearance resolution for two selected items.
int HighlightNet(const TOOL_EVENT &aEvent)
void calculateSelectionRatsnest(const VECTOR2I &aDelta)
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
int ShowBoardStatistics(const TOOL_EVENT &aEvent)
Show dialog with board statistics.
wxString InspectDRCErrorMenuText(const std::shared_ptr< RC_ITEM > &aDRCItem)
bool highlightNet(const VECTOR2D &aPosition, bool aUseSelection)
Look for a BOARD_CONNECTED_ITEM in a given spot and if one is found - it enables highlight for its ne...
int HighlightNetChain(const TOOL_EVENT &aEvent)
int DiffFootprint(const TOOL_EVENT &aEvent)
void reportClearance(BOARD_ITEM *aItemA, BOARD_ITEM *aItemB)
int LocalRatsnestTool(const TOOL_EVENT &aEvent)
Hide the ratsnest for a given net.
bool Init() override
Init() is called once upon a registration of the tool.
int ShowNetInRatsnest(const TOOL_EVENT &aEvent)
void reportCompileError(REPORTER *r)
int ClearHighlight(const TOOL_EVENT &aEvent)
Perform the appropriate action in response to an Eeschema cross-probe.
std::unique_ptr< DRC_ENGINE > makeDRCEngine(bool *aCompileError, bool *aCourtyardError)
int HideNetInRatsnest(const TOOL_EVENT &aEvent)
Show the ratsnest for a given net.
int InspectConstraints(const TOOL_EVENT &aEvent)
void doHideRatsnestNet(int aNetCode, bool aHide)
Bind handlers to corresponding TOOL_ACTIONs.
BOARD_ITEM * pickItemForInspection(const TOOL_EVENT &aEvent, const wxString &aPrompt, const std::vector< KICAD_T > &aTypes, BOARD_ITEM *aLockedHighlight)
wxString getItemDescription(BOARD_ITEM *aItem)
CONNECTIVITY_DATA * m_dynamicData
int showBoardComparison(const wxString &aOtherPath, const wxString &aProjectPath, const wxString &aComparisonLabel)
Diff the board at aOtherPath against the live one and show the dialog.
int ReplaceTerminalPad(const TOOL_EVENT &aEvent)
Clear all board highlights.
int ShowFootprintLinks(const TOOL_EVENT &aEvent)
void reportHeader(const wxString &aTitle, BOARD_ITEM *a, REPORTER *r)
void filterCollectorForInspection(GENERAL_COLLECTOR &aCollector, const VECTOR2I &aPos)
int CompareBoardWithFile(const TOOL_EVENT &aEvent)
Diff the current board against a user-selected .kicad_pcb file.
FOOTPRINT_DIFF_WIDGET * constructDiffPanel(wxPanel *aParentPanel)
int InspectClearance(const TOOL_EVENT &aEvent)
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
std::set< int > m_currentlyHighlighted
int UpdateLocalRatsnest(const TOOL_EVENT &aEvent)
Hide ratsnest for selected items. Called when there are no items selected.
int CompareBoardWithHistory(const TOOL_EVENT &aEvent)
Diff the current board against the most recent local-history commit.
int HideLocalRatsnest(const TOOL_EVENT &aEvent)
Show local ratsnest of a component.
int HighlightItem(const TOOL_EVENT &aEvent)
Update ratsnest for selected items.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:295
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:159
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:404
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:377
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:315
virtual bool IsTented(PCB_LAYER_ID aLayer) const
Checks if the given object is tented (its copper shape is covered by solder mask) on a given side of ...
Definition board_item.h:198
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition board_item.h:233
virtual bool HasDrilledHole() const
Definition board_item.h:186
virtual bool IsOnCopperLayer() const
Definition board_item.h:176
virtual bool HasHole() const
Definition board_item.h:181
static std::unique_ptr< BOARD > Load(const wxString &aFileName, PCB_IO_MGR::PCB_FILE_T aFormat, PROJECT *aProject, const OPTIONS &aOptions)
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
const wxString & GetFileName() const
Definition board.h:410
PROJECT * GetProject() const
Definition board.h:662
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:1928
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:646
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition collector.h:107
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition collector.h:97
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
Dialog to show common board info.
wxPanel * AddBlankPage(const wxString &aTitle)
WX_HTML_REPORT_BOX * AddHTMLPage(const wxString &aTitle)
void SetUserItemID(const KIID &aID)
Dialog to show footprint library and symbol links.
std::function< void(WIDGET_DIFF_CANVAS &, const KIID_PATH &)> SHEET_SWITCHER
bool Show(bool show) override
int ShowModal() override
wxString GetName() const
Definition drc_rule.h:204
int m_DisallowFlags
Definition drc_rule.h:241
ZONE_CONNECTION m_ZoneConnection
Definition drc_rule.h:242
MINOPTMAX< int > m_Value
Definition drc_rule.h:240
bool IsNull() const
Definition drc_rule.h:191
static int MatchDpSuffix(const wxString &aNetName, wxString &aComplementNet, wxString &aBaseDpName)
Check if the given net is a diff pair, returning its polarity and complement if so.
DIALOG_DRC * GetDRCDialog()
Definition drc_tool.h:60
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:50
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Return a user-visible description string of this item.
Definition eda_item.cpp:169
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Test if aPosition is inside or on the boundary of this item.
Definition eda_item.h:243
void DisplayDiff(FOOTPRINT *aBoardFootprint, std::shared_ptr< FOOTPRINT > &aLibFootprint)
Set the currently displayed symbol.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
FOOTPRINT * LoadFootprint(const wxString &aNickname, const wxString &aName, bool aKeepUUID)
Load a FOOTPRINT having aName from the library given by aNickname.
bool FootprintNeedsUpdate(const FOOTPRINT *aLibFP, int aCompareFlags=0, REPORTER *aReporter=nullptr)
Return true if a board footprint differs from the library version.
const LIB_ID & GetFPID() const
Definition footprint.h:444
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
A general implementation of a COLLECTORS_GUIDE.
Definition collectors.h:320
void SetIgnoreZoneFills(bool ignore)
Definition collectors.h:469
void SetPreferredLayer(PCB_LAYER_ID aLayer)
Definition collectors.h:386
void SetIgnoreNoNets(bool ignore)
Definition collectors.h:472
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
void Collect(BOARD_ITEM *aItem, const std::vector< KICAD_T > &aScanList, const VECTOR2I &aRefPos, const COLLECTORS_GUIDE &aGuide)
Scan a BOARD_ITEM using this class's Inspector method, which does the collection.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
Diff two already-parsed BOARDs and produce a DOCUMENT_DIFF.
Definition pcb_differ.h:52
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
PCB specific render settings.
Definition pcb_painter.h:80
std::set< int > & GetHiddenNets()
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const std::set< int > & GetHighlightNetCodes() const
Return the netcode of currently highlighted net.
PCB_LAYER_ID GetPrimaryHighContrastLayer() const
Return the board layer which is in high-contrast mode.
bool IsHighlightEnabled() const
Return current highlight setting.
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual int GetTopLayer() const
Definition view.cpp:899
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition view.h:225
Definition kiid.h:46
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library tables.
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
Simple local history manager built on libgit2.
std::vector< LOCAL_HISTORY_SNAPSHOT_INFO > GetSnapshots(const wxString &aProjectPath)
Snapshots (commits) for the project, newest first.
wxString TreeFingerprint(const wxString &aProjectPath, const wxString &aHash, const wxString &aExtension)
Fingerprint of all files ending in aExtension recorded by commit aHash (sorted path:blob pairs).
bool ExtractAllFilesAtCommit(const wxString &aProjectPath, const wxString &aHash, const wxString &aDestDir, const std::vector< wxString > &aExtensions={})
Write files recorded at aHash into aDestDir, recreating the project's relative folder structure.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:309
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:693
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
T Min() const
Definition minoptmax.h:29
bool HasMax() const
Definition minoptmax.h:35
bool HasMin() const
Definition minoptmax.h:34
T Max() const
Definition minoptmax.h:30
T Opt() const
Definition minoptmax.h:31
bool HasOpt() const
Definition minoptmax.h:36
const wxString GetHumanReadableName() const
Gets the consolidated name of this netclass (which may be an aggregate).
Definition netclass.cpp:320
Handle the data for a net.
Definition netinfo.h:46
const wxString & GetNetChain() const
Definition netinfo.h:112
const wxString & GetNetname() const
Definition netinfo.h:100
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Return the information about the NETINFO_ITEM in aList to display in the message panel.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
Definition pad.h:61
PAD_ATTRIB GetAttribute() const
Definition pad.h:555
VECTOR2I GetDrillSize() const
Definition pad.h:315
static TOOL_ACTION highlightItem
static TOOL_ACTION toggleNetHighlight
static TOOL_ACTION setTerminalPad
static TOOL_ACTION highlightNet
static TOOL_ACTION hideNetInRatsnest
static TOOL_ACTION hideLocalRatsnest
static TOOL_ACTION showNetInRatsnest
static TOOL_ACTION highlightNetChain
static TOOL_ACTION toggleLastNetHighlight
static TOOL_ACTION compareBoardWithHistory
static TOOL_ACTION inspectConstraints
static TOOL_ACTION clearHighlight
static TOOL_ACTION inspectClearance
static TOOL_ACTION updateLocalRatsnest
static TOOL_ACTION compareBoardWithFile
static TOOL_ACTION diffFootprint
static TOOL_ACTION showFootprintAssociations
static TOOL_ACTION highlightNetSelection
static TOOL_ACTION boardStatistics
static TOOL_ACTION localRatsnestTool
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
PCB_FILE_T
The set of file types that the PCB_IO_MGR knows about, and for which there has been a plugin written,...
Definition pcb_io_mgr.h:52
static PCB_FILE_T FindPluginTypeFromBoardPath(const wxString &aFileName, int aCtl=0)
Return a plugin type given a path for a board file.
Generic tool for picking an item.
The selection tool: currently supports:
void GuessSelectionCandidates(GENERAL_COLLECTOR &aCollector, const VECTOR2I &aWhere) const
Try to guess best selection candidates in case multiple items are clicked, by doing some brain-dead h...
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter)
Return the current selection, filtered according to aClientFilter.
bool Selectable(const BOARD_ITEM *aItem, bool checkVisibilityOnly=false) const
PCB_SELECTION_FILTER_OPTIONS & GetFilter()
Set up handlers for various events.
PCB_SELECTION & GetSelection()
void FilterCollectedItems(GENERAL_COLLECTOR &aCollector, bool aMultiSelect, PCB_SELECTION_FILTER_OPTIONS *aRejected=nullptr)
Apply the SELECTION_FITLER_OPTIONS to the collector.
T * frame() const
KIGFX::PCB_VIEW * view() const
KIGFX::VIEW_CONTROLS * controls() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
BOARD * board() const
PCBNEW_SETTINGS::DISPLAY_OPTIONS & displayOptions() const
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
void SetClickHandler(CLICK_HANDLER aHandler)
Set a handler for mouse click event.
Definition picker_tool.h:77
void SetSnapping(bool aSnap)
Definition picker_tool.h:62
void SetCursor(KICURSOR aCursor)
Definition picker_tool.h:60
void SetFinalizeHandler(FINALIZE_HANDLER aHandler)
Set a handler for the finalize event.
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Container for project specific data.
Definition project.h:63
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:72
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:101
void BrightenItem(EDA_ITEM *aItem)
void UnbrightenItem(EDA_ITEM *aItem)
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
bool IsProjectLoaded(PROJECT *aProject) const
True if aProject is still owned by the manager.
PROJECT * GetProject(const wxString &aFullPath) const
Retrieve a loaded project by name.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
Extension of STATUS_POPUP for displaying a single line text.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
T * getModel() const
Return the model object if it matches the requested type.
Definition tool_base.h:195
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:40
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:34
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
Generic, UI-independent tool event.
Definition tool_event.h:167
bool DisableGridSnapping() const
Definition tool_event.h:367
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:469
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_MENU & GetToolMenu()
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
CONDITIONAL_MENU & GetMenu()
Definition tool_menu.cpp:40
void RegisterSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Store a submenu of this menu model.
Definition tool_menu.cpp:46
wxString StringFromValue(double aValue, bool aAddUnitLabel=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
Converts aValue in internal units into a united string.
GAL-backed canvas for visualizing a KICAD_DIFF::DIFF_SCENE.
A slimmed down version of WX_HTML_REPORT_PANEL.
void Clear() override
Delete the stored messages.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
void SetUnits(EDA_UNITS aUnits)
void Flush()
Build the HTML messages page.
Handle a list of polygons defining a copper zone.
Definition zone.h:70
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:1012
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:750
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
@ BULLSEYE
Definition cursors.h:54
@ DRCE_DP_UNCOUPLED_LENGTH_TOO_LONG
Definition drc_item.h:109
@ DRCE_HOLE_CLEARANCE
Definition drc_item.h:52
@ DRCE_VIA_DIAMETER
Definition drc_item.h:59
@ DRCE_TRACK_WIDTH
Definition drc_item.h:53
@ DRCE_DRILL_OUT_OF_RANGE
Definition drc_item.h:58
@ DRCE_EDGE_CLEARANCE
Definition drc_item.h:44
@ DRCE_STARVED_THERMAL
Definition drc_item.h:47
@ DRCE_TRACK_SEGMENT_LENGTH
Definition drc_item.h:55
@ DRCE_TRACK_ANGLE
Definition drc_item.h:54
@ DRCE_CLEARANCE
Definition drc_item.h:41
@ DRCE_DRILLED_HOLES_TOO_CLOSE
Definition drc_item.h:50
@ DRCE_MICROVIA_DRILL_OUT_OF_RANGE
Definition drc_item.h:62
@ DRCE_TEXT_HEIGHT
Definition drc_item.h:99
@ DRCE_ASSERTION_FAILURE
Definition drc_item.h:87
@ DRCE_LIB_FOOTPRINT_MISMATCH
Definition drc_item.h:81
@ DRCE_TEXT_THICKNESS
Definition drc_item.h:100
@ DRCE_CONNECTION_WIDTH
Definition drc_item.h:57
@ DRCE_ANNULAR_WIDTH
Definition drc_item.h:56
@ ANNULAR_WIDTH_CONSTRAINT
Definition drc_rule.h:63
@ COURTYARD_CLEARANCE_CONSTRAINT
Definition drc_rule.h:57
@ VIA_DIAMETER_CONSTRAINT
Definition drc_rule.h:72
@ DIFF_PAIR_GAP_CONSTRAINT
Definition drc_rule.h:78
@ DISALLOW_CONSTRAINT
Definition drc_rule.h:71
@ TRACK_WIDTH_CONSTRAINT
Definition drc_rule.h:61
@ SILK_CLEARANCE_CONSTRAINT
Definition drc_rule.h:58
@ EDGE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:55
@ MIN_RESOLVED_SPOKES_CONSTRAINT
Definition drc_rule.h:67
@ TRACK_SEGMENT_LENGTH_CONSTRAINT
Definition drc_rule.h:62
@ TEXT_THICKNESS_CONSTRAINT
Definition drc_rule.h:60
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:83
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:51
@ THERMAL_SPOKE_WIDTH_CONSTRAINT
Definition drc_rule.h:66
@ CONNECTION_WIDTH_CONSTRAINT
Definition drc_rule.h:85
@ THERMAL_RELIEF_GAP_CONSTRAINT
Definition drc_rule.h:65
@ MAX_UNCOUPLED_CONSTRAINT
Definition drc_rule.h:79
@ HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:53
@ SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
Definition drc_rule.h:69
@ SOLDER_MASK_EXPANSION_CONSTRAINT
Definition drc_rule.h:68
@ TRACK_ANGLE_CONSTRAINT
Definition drc_rule.h:86
@ HOLE_SIZE_CONSTRAINT
Definition drc_rule.h:56
@ TEXT_HEIGHT_CONSTRAINT
Definition drc_rule.h:59
@ PHYSICAL_CLEARANCE_CONSTRAINT
Definition drc_rule.h:82
@ SOLDER_PASTE_REL_MARGIN_CONSTRAINT
Definition drc_rule.h:70
@ HOLE_TO_HOLE_CONSTRAINT
Definition drc_rule.h:54
#define _(s)
@ RECURSE
Definition eda_item.h:49
#define MALFORMED_COURTYARDS
EDA_UNITS
Definition eda_units.h:44
static const std::string ProjectFileExtension
static wxString PcbFileWildcard()
#define KICTL_KICAD_ONLY
chosen file is from KiCad according to user
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:786
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:683
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ Edge_Cuts
Definition layer_ids.h:108
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ Margin
Definition layer_ids.h:109
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
void ConfigurePcbDiffCanvasContext(WIDGET_DIFF_CANVAS &aCanvas, BOARD *aReference, BOARD *aComparison, const KIGFX::COLOR4D &aColor, const std::map< KIID, KIGFX::COLOR4D > &aOverrides, const std::vector< KIGFX::VIEW_ITEM * > &aExtraItems, const std::map< KIID, KICAD_DIFF::CATEGORY > &aCategories)
std::vector< KIGFX::VIEW_ITEM * > CollectFootprintDiffContextItems(FOOTPRINT &aFootprint)
CATEGORY CategoryFor(CHANGE_KIND aKind)
Map a CHANGE_KIND to the visual category it belongs to.
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition definitions.h:35
void ReparentWindow(wxNonOwnedWindow *aWindow, wxTopLevelWindow *aParent)
Definition wxgtk/ui.cpp:260
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:839
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
Class to handle a set of BOARD_ITEMs.
SEVERITY
@ RPT_SEVERITY_UNDEFINED
std::vector< FAB_LAYER_COLOR > dummy
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
The full set of changes between two parsed documents of one type.
std::vector< ITEM_CHANGE > changes
Aggregate of background geometry extracted from one source document.
Definition diff_scene.h:163
One change record on a single item.
std::vector< ITEM_CHANGE > children
A filename or source description, a problem input line, a line number, a byte offset,...
This file contains data structures that are saved in the project file or project local settings file ...
int clearance
wxString result
Test unit parsing edge cases and error handling.
int delta
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:71
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:81
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:84
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:104
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:86
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:85
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:83
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:56
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
Definition of file extensions used in Kicad.
@ THERMAL
Use thermal relief for pads.
Definition zones.h:46
@ NONE
Pads are not covered.
Definition zones.h:45