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_DIFF_PAIR_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 = [&]( PCB_LAYER_ID layer, REPORTER* rep )
1212 {
1213 reportHeader( _( "Clearance resolution for:" ), a, b, layer, rep );
1214
1215 if( sameNet )
1216 {
1217 rep->Report( _( "Items belong to the same net. Min clearance is 0." ) );
1218 return;
1219 }
1220
1221 constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, a, b, layer, rep );
1222 clearance = constraint.m_Value.Min();
1223
1224 if( compileError )
1225 reportCompileError( rep );
1226
1227 rep->Report( "" );
1228
1229 if( constraint.IsNull() )
1230 {
1231 rep->Report( _( "Min clearance is 0." ) );
1232 }
1233 else if( clearance < 0 )
1234 {
1235 rep->Report( wxString::Format( _( "Resolved clearance: %s; clearance will "
1236 "not be tested." ),
1237 m_frame->StringFromValue( clearance, true ) ) );
1238 }
1239 else
1240 {
1241 rep->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1242 m_frame->StringFromValue( clearance, true ) ) );
1243 }
1244 };
1245
1246 if( layers.size() == 1 )
1247 {
1248 PCB_LAYER_ID layer = layers.front();
1249
1250 r = dialog->AddHTMLPage( m_frame->GetBoard()->GetLayerName( layer ) );
1251 fillReport( layer, r );
1252 r->Flush();
1253 }
1254 else
1255 {
1256 auto perLayerMessages = std::make_shared<std::vector<std::vector<wxString>>>();
1257 perLayerMessages->reserve( layers.size() );
1258
1259 for( PCB_LAYER_ID layer : layers )
1260 {
1261 VECTOR_REPORTER tmp;
1262 fillReport( layer, &tmp );
1263 perLayerMessages->push_back( std::move( tmp.m_messages ) );
1264 }
1265
1266 wxPanel* panel = dialog->AddBlankPage( _( "Clearance" ) );
1267 wxBoxSizer* vbox = new wxBoxSizer( wxVERTICAL );
1268
1269 wxChoice* choice = new wxChoice( panel, wxID_ANY );
1270
1271 for( PCB_LAYER_ID layer : layers )
1272 choice->Append( m_frame->GetBoard()->GetLayerName( layer ) );
1273
1274 choice->SetSelection( 0 );
1275
1276 WX_HTML_REPORT_BOX* reportBox = new WX_HTML_REPORT_BOX( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
1277 wxHW_SCROLLBAR_AUTO | wxBORDER_SIMPLE );
1278 reportBox->SetUnits( m_frame->GetUserUnits() );
1279
1280 wxStaticText* layerLabel = new wxStaticText( panel, wxID_ANY, _( "Layer:" ) );
1281
1282 vbox->Add( layerLabel, 0, wxLEFT | wxRIGHT | wxTOP, 5 );
1283 vbox->Add( choice, 0, wxEXPAND | wxALL, 5 );
1284 vbox->Add( reportBox, 1, wxEXPAND | wxALL, 5 );
1285 panel->SetSizer( vbox );
1286 panel->Layout();
1287
1288 auto refresh = [reportBox, perLayerMessages]( int sel )
1289 {
1290 reportBox->Clear();
1291
1292 if( sel >= 0 && sel < (int) perLayerMessages->size() )
1293 {
1294 for( const wxString& line : ( *perLayerMessages )[sel] )
1295 reportBox->Report( line );
1296 }
1297
1298 reportBox->Flush();
1299 };
1300
1301 choice->Bind( wxEVT_CHOICE,
1302 [refresh]( wxCommandEvent& evt )
1303 {
1304 refresh( evt.GetSelection() );
1305 } );
1306
1307 refresh( 0 );
1308 }
1309 }
1310
1311 if( ac && bc )
1312 {
1313 NETINFO_ITEM* refNet = ac->GetNet();
1314 wxString coupledNet;
1315 wxString dummy;
1316
1317 if( DRC_ENGINE::MatchDpSuffix( refNet->GetNetname(), coupledNet, dummy )
1318 && bc->GetNetname() == coupledNet )
1319 {
1320 LSET dpIntersection = ac->GetLayerSet() & bc->GetLayerSet() & LSET::AllCuMask();
1321 PCB_LAYER_ID dpLayer = active;
1322
1323 if( !dpIntersection.test( dpLayer ) && dpIntersection.any() )
1324 dpLayer = dpIntersection.Seq().front();
1325
1326 r = dialog->AddHTMLPage( _( "Diff Pair" ) );
1327 reportHeader( _( "Diff-pair gap resolution for:" ), ac, bc, dpLayer, r );
1328
1329 constraint = drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, ac, bc, dpLayer, r );
1330
1331 r->Report( "" );
1332 r->Report( wxString::Format( _( "Resolved gap constraints: min %s; opt %s; max %s." ),
1333 reportMin( m_frame, constraint ),
1334 reportOpt( m_frame, constraint ),
1335 reportMax( m_frame, constraint ) ) );
1336
1337 r->Report( "" );
1338 r->Report( "" );
1339 r->Report( "" );
1340 reportHeader( _( "Diff-pair max uncoupled length resolution for:" ), ac, bc, dpLayer, r );
1341
1342 if( !drcEngine->HasRulesForConstraintType( MAX_UNCOUPLED_CONSTRAINT ) )
1343 {
1344 r->Report( "" );
1345 r->Report( _( "No 'diff_pair_uncoupled' constraints defined." ) );
1346 }
1347 else
1348 {
1349 constraint = drcEngine->EvalRules( MAX_UNCOUPLED_CONSTRAINT, ac, bc, dpLayer, r );
1350
1351 r->Report( "" );
1352 r->Report( wxString::Format( _( "Resolved max uncoupled length: %s." ),
1353 reportMax( m_frame, constraint ) ) );
1354 }
1355
1356 r->Flush();
1357 }
1358 }
1359
1360 auto isOnCorrespondingLayer=
1361 [&]( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, wxString* aWarning )
1362 {
1363 if( aItem->IsOnLayer( aLayer ) )
1364 return true;
1365
1366 PCB_LAYER_ID correspondingMask = IsFrontLayer( aLayer ) ? F_Mask : B_Mask;
1367 PCB_LAYER_ID correspondingCopper = IsFrontLayer( aLayer ) ? F_Cu : B_Cu;
1368
1369 if( aItem->IsOnLayer( aLayer ) )
1370 return true;
1371
1372 if( aItem->IsOnLayer( correspondingMask ) )
1373 return true;
1374
1375 if( aItem->IsTented( correspondingMask ) && aItem->IsOnLayer( correspondingCopper ) )
1376 {
1377 *aWarning = wxString::Format( _( "Note: %s is tented; clearance will only be "
1378 "applied to holes." ),
1379 getItemDescription( aItem ) );
1380 return true;
1381 }
1382
1383 return false;
1384 };
1385
1386 for( PCB_LAYER_ID layer : { F_SilkS, B_SilkS } )
1387 {
1388 wxString warning;
1389
1390 if( ( a->IsOnLayer( layer ) && isOnCorrespondingLayer( b, layer, &warning ) )
1391 || ( b->IsOnLayer( layer ) && isOnCorrespondingLayer( a, layer, &warning ) ) )
1392 {
1393 r = dialog->AddHTMLPage( m_frame->GetBoard()->GetLayerName( layer ) );
1394 reportHeader( _( "Silkscreen clearance resolution for:" ), a, b, layer, r );
1395
1396 constraint = drcEngine->EvalRules( SILK_CLEARANCE_CONSTRAINT, a, b, layer, r );
1397 clearance = constraint.m_Value.Min();
1398
1399 if( compileError )
1400 reportCompileError( r );
1401
1402 r->Report( "" );
1403
1404 if( !warning.IsEmpty() )
1405 r->Report( warning );
1406
1407 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1408 m_frame->StringFromValue( clearance, true ) ) );
1409
1410 r->Flush();
1411 }
1412 }
1413
1414 for( PCB_LAYER_ID layer : { F_CrtYd, B_CrtYd } )
1415 {
1416 bool aCourtyard = aFP && !aFP->GetCourtyard( layer ).IsEmpty();
1417 bool bCourtyard = bFP && !bFP->GetCourtyard( layer ).IsEmpty();
1418
1419 if( aCourtyard && bCourtyard )
1420 {
1421 r = dialog->AddHTMLPage( m_frame->GetBoard()->GetLayerName( layer ) );
1422 reportHeader( _( "Courtyard clearance resolution for:" ), a, b, layer, r );
1423
1424 constraint = drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, a, b, layer, r );
1425 clearance = constraint.m_Value.Min();
1426
1427 if( compileError )
1428 reportCompileError( r );
1429
1430 r->Report( "" );
1431 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1432 m_frame->StringFromValue( clearance, true ) ) );
1433
1434 r->Flush();
1435 }
1436 }
1437
1438 if( a->HasHole() || b->HasHole() )
1439 {
1441 bool pageAdded = false;
1442
1443 if( a->HasHole() && b->IsOnLayer( active ) && IsCopperLayer( active ) )
1444 layer = active;
1445 else if( b->HasHole() && a->IsOnLayer( active ) && IsCopperLayer( active ) )
1446 layer = active;
1447 else if( a->HasHole() && b->IsOnCopperLayer() )
1448 layer = b->GetLayer();
1449 else if( b->HasHole() && a->IsOnCopperLayer() )
1450 layer = a->GetLayer();
1451
1452 if( layer >= 0 )
1453 {
1454 r = dialog->AddHTMLPage( _( "Hole" ) );
1455 pageAdded = true;
1456
1457 reportHeader( _( "Hole clearance resolution for:" ), a, b, layer, r );
1458
1459 constraint = drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
1460 clearance = constraint.m_Value.Min();
1461
1462 if( compileError )
1463 reportCompileError( r );
1464
1465 r->Report( "" );
1466 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1467 m_frame->StringFromValue( clearance, true ) ) );
1468
1469 r->Flush();
1470 }
1471
1472 if( a->HasDrilledHole() || b->HasDrilledHole() )
1473 {
1474 if( !pageAdded )
1475 {
1476 r = dialog->AddHTMLPage( _( "Hole" ) );
1477 pageAdded = true;
1478 }
1479 else
1480 {
1481 r->Report( "" );
1482 r->Report( "" );
1483 r->Report( "" );
1484 }
1485
1486 reportHeader( _( "Hole-to-hole clearance resolution for:" ), a, b, r );
1487
1488 constraint = drcEngine->EvalRules( HOLE_TO_HOLE_CONSTRAINT, a, b, UNDEFINED_LAYER, r );
1489 clearance = constraint.m_Value.Min();
1490
1491 if( compileError )
1492 reportCompileError( r );
1493
1494 r->Report( "" );
1495 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1496 m_frame->StringFromValue( clearance, true ) ) );
1497
1498 r->Flush();
1499 }
1500 }
1501
1502 for( PCB_LAYER_ID edgeLayer : { Edge_Cuts, Margin } )
1503 {
1505
1506 if( a->IsOnLayer( edgeLayer ) && b->Type() != PCB_FOOTPRINT_T )
1507 {
1508 if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
1509 layer = active;
1510 else if( IsCopperLayer( b->GetLayer() ) )
1511 layer = b->GetLayer();
1512 }
1513 else if( b->IsOnLayer( edgeLayer ) && a->Type() != PCB_FOOTPRINT_T )
1514 {
1515 if( a->IsOnLayer( active ) && IsCopperLayer( active ) )
1516 layer = active;
1517 else if( IsCopperLayer( a->GetLayer() ) )
1518 layer = a->GetLayer();
1519 }
1520
1521 if( layer >= 0 )
1522 {
1523 wxString layerName = m_frame->GetBoard()->GetLayerName( edgeLayer );
1524 r = dialog->AddHTMLPage( layerName + wxS( " " ) + _( "Clearance" ) );
1525 reportHeader( _( "Edge clearance resolution for:" ), a, b, layer, r );
1526
1527 constraint = drcEngine->EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, layer, r );
1528 clearance = constraint.m_Value.Min();
1529
1530 if( compileError )
1531 reportCompileError( r );
1532
1533 r->Report( "" );
1534 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1535 m_frame->StringFromValue( clearance, true ) ) );
1536
1537 r->Flush();
1538 }
1539 }
1540
1541 r = dialog->AddHTMLPage( _( "Physical Clearances" ) );
1542
1543 if( compileError )
1544 {
1545 reportCompileError( r );
1546 }
1547 else if( !drcEngine->HasRulesForConstraintType( PHYSICAL_CLEARANCE_CONSTRAINT ) )
1548 {
1549 r->Report( "" );
1550 r->Report( _( "No 'physical_clearance' constraints defined." ) );
1551 }
1552 else
1553 {
1554 LSET reportLayers = layerIntersection;
1555 bool reported = false;
1556
1557 if( a->IsOnLayer( Edge_Cuts ) )
1558 {
1559 LSET edgeInteractingLayers = bFP ? LSET( { F_CrtYd, B_CrtYd } )
1561 reportLayers |= edgeInteractingLayers;
1562 }
1563
1564 if( b->IsOnLayer( Edge_Cuts ) )
1565 {
1566 LSET edgeInteractingLayers = aFP ? LSET( { F_CrtYd, B_CrtYd } )
1568 reportLayers |= edgeInteractingLayers;
1569 }
1570
1571 for( PCB_LAYER_ID layer : reportLayers )
1572 {
1573 reported = true;
1574 reportHeader( _( "Physical clearance resolution for:" ), a, b, layer, r );
1575
1576 constraint = drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, layer, r );
1577 clearance = constraint.m_Value.Min();
1578
1579 if( constraint.IsNull() )
1580 {
1581 r->Report( "" );
1582 r->Report( wxString::Format( _( "No 'physical_clearance' constraints in effect on %s." ),
1583 m_frame->GetBoard()->GetLayerName( layer ) ) );
1584 }
1585 else
1586 {
1587 r->Report( "" );
1588 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1589 m_frame->StringFromValue( clearance, true ) ) );
1590 }
1591
1592 r->Report( "" );
1593 r->Report( "" );
1594 r->Report( "" );
1595 }
1596
1597 if( !reported )
1598 {
1599 reportHeader( _( "Physical clearance resolution for:" ), a, b, r );
1600 r->Report( "" );
1601 r->Report( _( "Items share no relevant layers. No 'physical_clearance' constraints will "
1602 "be applied." ) );
1603 }
1604 }
1605
1606 if( a->HasHole() || b->HasHole() )
1607 {
1608 PCB_LAYER_ID layer;
1609
1610 if( a->HasHole() && b->IsOnLayer( active ) )
1611 layer = active;
1612 else if( b->HasHole() && a->IsOnLayer( active ) )
1613 layer = active;
1614 else if( a->HasHole() )
1615 layer = b->GetLayer();
1616 else
1617 layer = a->GetLayer();
1618
1619 reportHeader( _( "Physical hole clearance resolution for:" ), a, b, layer, r );
1620
1621 constraint = drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
1622 clearance = constraint.m_Value.Min();
1623
1624 if( compileError )
1625 {
1626 reportCompileError( r );
1627 }
1628 else if( !drcEngine->HasRulesForConstraintType( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT ) )
1629 {
1630 r->Report( "" );
1631 r->Report( _( "No 'physical_hole_clearance' constraints defined." ) );
1632 }
1633 else
1634 {
1635 r->Report( "" );
1636 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1637 m_frame->StringFromValue( clearance, true ) ) );
1638 }
1639 }
1640
1641 r->Flush();
1642
1643 dialog->Raise();
1644 dialog->Show( true );
1645}
1646
1647
1649{
1650#define EVAL_RULES( constraint, a, b, layer, r ) drcEngine->EvalRules( constraint, a, b, layer, r )
1651
1652 wxCHECK( m_frame, 0 );
1653
1654 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1655
1656 wxCHECK( selTool, 0 );
1657
1658 const PCB_SELECTION& selection = selTool->GetSelection();
1659 BOARD_ITEM* item = nullptr;
1660
1661 if( selection.Size() == 1 && selection.GetItem( 0 )->IsBOARD_ITEM() )
1662 {
1663 item = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) );
1664 }
1665 else if( selection.Size() == 0 )
1666 {
1667 static const std::vector<KICAD_T> constraintTypes = {
1668 PCB_PAD_T,
1669 PCB_VIA_T,
1671 PCB_ARC_T,
1672 PCB_ZONE_T,
1676 PCB_TEXT_T,
1679 };
1680
1681 Activate();
1682
1683 item = pickItemForInspection( aEvent, _( "Select item for constraints resolution..." ),
1684 constraintTypes, nullptr );
1685
1686 if( !item )
1687 return 0;
1688 }
1689 else
1690 {
1691 m_frame->ShowInfoBarError( _( "Select a single item for a constraints resolution report." ) );
1692 return 0;
1693 }
1694
1695 DIALOG_BOOK_REPORTER* dialog = m_frame->GetInspectConstraintsDialog();
1696
1697 wxCHECK( dialog, 0 );
1698
1699 dialog->DeleteAllPages();
1700 DRC_CONSTRAINT constraint;
1701
1702 bool compileError = false;
1703 bool courtyardError = false;
1704 std::unique_ptr<DRC_ENGINE> drcEngine = makeDRCEngine( &compileError, &courtyardError );
1705
1706 WX_HTML_REPORT_BOX* r = nullptr;
1707
1708 if( item->Type() == PCB_TRACE_T )
1709 {
1710 r = dialog->AddHTMLPage( _( "Track Width" ) );
1711 reportHeader( _( "Track width resolution for:" ), item, r );
1712
1713 constraint = EVAL_RULES( TRACK_WIDTH_CONSTRAINT, item, nullptr, item->GetLayer(), r );
1714
1715 if( compileError )
1716 reportCompileError( r );
1717
1718 r->Report( "" );
1719 r->Report( wxString::Format( _( "Resolved width constraints: min %s; opt %s; max %s." ),
1720 reportMin( m_frame, constraint ),
1721 reportOpt( m_frame, constraint ),
1722 reportMax( m_frame, constraint ) ) );
1723
1724 r->Flush();
1725 }
1726
1727 if( item->Type() == PCB_VIA_T )
1728 {
1729 r = dialog->AddHTMLPage( _( "Via Diameter" ) );
1730 reportHeader( _( "Via diameter resolution for:" ), item, r );
1731
1732 // PADSTACKS TODO: once we have padstacks we'll need to run this per-layer....
1733 constraint = EVAL_RULES( VIA_DIAMETER_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1734
1735 if( compileError )
1736 reportCompileError( r );
1737
1738 r->Report( "" );
1739 r->Report( wxString::Format( _( "Resolved diameter constraints: min %s; opt %s; max %s." ),
1740 reportMin( m_frame, constraint ),
1741 reportOpt( m_frame, constraint ),
1742 reportMax( m_frame, constraint ) ) );
1743
1744 r->Flush();
1745
1746 r = dialog->AddHTMLPage( _( "Via Annular Width" ) );
1747 reportHeader( _( "Via annular width resolution for:" ), item, r );
1748
1749 // PADSTACKS TODO: once we have padstacks we'll need to run this per-layer....
1750 constraint = EVAL_RULES( ANNULAR_WIDTH_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1751
1752 if( compileError )
1753 reportCompileError( r );
1754
1755 r->Report( "" );
1756 r->Report( wxString::Format( _( "Resolved annular width constraints: min %s; opt %s; max %s." ),
1757 reportMin( m_frame, constraint ),
1758 reportOpt( m_frame, constraint ),
1759 reportMax( m_frame, constraint ) ) );
1760
1761 r->Flush();
1762 }
1763
1764 if( ( item->Type() == PCB_PAD_T && static_cast<PAD*>( item )->GetDrillSize().x > 0 )
1765 || item->Type() == PCB_VIA_T )
1766 {
1767 r = dialog->AddHTMLPage( _( "Hole Size" ) );
1768 reportHeader( _( "Hole size resolution for:" ), item, r );
1769
1770 constraint = EVAL_RULES( HOLE_SIZE_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1771
1772 if( compileError )
1773 reportCompileError( r );
1774
1775 r->Report( "" );
1776 r->Report( wxString::Format( _( "Resolved hole size constraints: min %s; opt %s; max %s." ),
1777 reportMin( m_frame, constraint ),
1778 reportOpt( m_frame, constraint ),
1779 reportMax( m_frame, constraint ) ) );
1780
1781 r->Flush();
1782 }
1783
1784 if( item->Type() == PCB_PAD_T || item->Type() == PCB_SHAPE_T || dynamic_cast<PCB_TRACK*>( item ) )
1785 {
1786 r = dialog->AddHTMLPage( _( "Solder Mask" ) );
1787 reportHeader( _( "Solder mask expansion resolution for:" ), item, r );
1788
1789 constraint = EVAL_RULES( SOLDER_MASK_EXPANSION_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1790
1791 if( compileError )
1792 reportCompileError( r );
1793
1794 r->Report( "" );
1795 r->Report( wxString::Format( _( "Resolved solder mask expansion: %s." ),
1796 reportOpt( m_frame, constraint ) ) );
1797
1798 r->Flush();
1799 }
1800
1801 if( item->Type() == PCB_PAD_T )
1802 {
1803 r = dialog->AddHTMLPage( _( "Solder Paste" ) );
1804 reportHeader( _( "Solder paste absolute clearance resolution for:" ), item, r );
1805
1806 constraint = EVAL_RULES( SOLDER_PASTE_ABS_MARGIN_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1807
1808 if( compileError )
1809 reportCompileError( r );
1810
1811 r->Report( "" );
1812 r->Report( wxString::Format( _( "Resolved solder paste absolute clearance: %s." ),
1813 reportOpt( m_frame, constraint ) ) );
1814
1815 reportHeader( _( "Solder paste relative clearance resolution for:" ), item, r );
1816
1817 constraint = EVAL_RULES( SOLDER_PASTE_REL_MARGIN_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1818
1819 if( compileError )
1820 reportCompileError( r );
1821
1822 r->Report( "" );
1823 r->Report( "" );
1824 r->Report( "" );
1825 r->Report( wxString::Format( _( "Resolved solder paste relative clearance: %s." ),
1826 reportOpt( m_frame, constraint ) ) );
1827
1828 r->Flush();
1829 }
1830
1831 if( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T || item->Type() == PCB_TEXTBOX_T )
1832 {
1833 r = dialog->AddHTMLPage( _( "Text Size" ) );
1834 reportHeader( _( "Text height resolution for:" ), item, r );
1835
1836 constraint = EVAL_RULES( TEXT_HEIGHT_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1837
1838 if( compileError )
1839 reportCompileError( r );
1840
1841 r->Report( "" );
1842 r->Report( wxString::Format( _( "Resolved height constraints: min %s; opt %s; max %s." ),
1843 reportMin( m_frame, constraint ),
1844 reportOpt( m_frame, constraint ),
1845 reportMax( m_frame, constraint ) ) );
1846
1847 r->Report( "" );
1848 r->Report( "" );
1849 r->Report( "" );
1850 reportHeader( _( "Text thickness resolution for:" ), item, r );
1851
1852 constraint = EVAL_RULES( TEXT_THICKNESS_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1853
1854 if( compileError )
1855 reportCompileError( r );
1856
1857 r->Report( "" );
1858 r->Report( wxString::Format( _( "Resolved thickness constraints: min %s; opt %s; max %s." ),
1859 reportMin( m_frame, constraint ),
1860 reportOpt( m_frame, constraint ),
1861 reportMax( m_frame, constraint ) ) );
1862
1863 r->Flush();
1864 }
1865
1866 r = dialog->AddHTMLPage( _( "Keepouts" ) );
1867 reportHeader( _( "Keepout resolution for:" ), item, r );
1868
1869 constraint = EVAL_RULES( DISALLOW_CONSTRAINT, item, nullptr, item->GetLayer(), r );
1870
1871 if( compileError )
1872 reportCompileError( r );
1873
1874 if( courtyardError )
1875 {
1876 r->Report( "" );
1877 r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
1878 + wxS( "&nbsp;&nbsp;" )
1879 + wxS( "<a href='$DRC'>" ) + _( "Run DRC for a full analysis." )
1880 + wxS( "</a>" ) );
1881 }
1882
1883 r->Report( "" );
1884
1885 if( constraint.m_DisallowFlags )
1886 r->Report( _( "Item <b>disallowed</b> at current location." ) );
1887 else
1888 r->Report( _( "Item allowed at current location." ) );
1889
1890 r->Flush();
1891
1892 r = dialog->AddHTMLPage( _( "Assertions" ) );
1893 reportHeader( _( "Assertions for:" ), item, r );
1894
1895 if( compileError )
1896 reportCompileError( r );
1897
1898 if( courtyardError )
1899 {
1900 r->Report( "" );
1901 r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
1902 + wxS( "&nbsp;&nbsp;" )
1903 + wxS( "<a href='$DRC'>" ) + _( "Run DRC for a full analysis." )
1904 + wxS( "</a>" ) );
1905 }
1906
1907 drcEngine->ProcessAssertions( item, []( const DRC_CONSTRAINT* c ){}, r );
1908 r->Flush();
1909
1910 dialog->Raise();
1911 dialog->Show( true );
1912 return 0;
1913}
1914
1915
1917{
1918 wxCHECK( m_frame, 0 );
1919
1920 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1921
1922 wxCHECK( selTool, 0 );
1923
1924 const PCB_SELECTION& selection = selTool->RequestSelection(
1925 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1926 {
1927 // Iterate from the back so we don't have to worry about removals.
1928 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1929 {
1930 BOARD_ITEM* item = aCollector[ i ];
1931
1932 if( !dynamic_cast<FOOTPRINT*>( item ) )
1933 aCollector.Remove( item );
1934 }
1935 } );
1936
1937 if( selection.Size() == 1 )
1938 DiffFootprint( static_cast<FOOTPRINT*>( selection.GetItem( 0 ) ) );
1939 else
1940 m_frame->ShowInfoBarError( _( "Select a footprint to diff with its library equivalent." ) );
1941
1942 return 0;
1943}
1944
1945
1947{
1948 wxCHECK( m_frame, 0 );
1949
1950 wxFileDialog dlg( m_frame, _( "Choose Board to Compare With" ), wxEmptyString,
1951 wxEmptyString, FILEEXT::PcbFileWildcard(),
1952 wxFD_OPEN | wxFD_FILE_MUST_EXIST );
1953
1954 if( dlg.ShowModal() != wxID_OK )
1955 return 0;
1956
1957 wxFileName otherFn( dlg.GetPath() );
1958 otherFn.MakeAbsolute();
1959
1960 const wxString otherPath = otherFn.GetFullPath();
1961
1962 wxFileName projectFn = otherFn;
1963 projectFn.SetExt( FILEEXT::ProjectFileExtension );
1964 const wxString projectPath = projectFn.GetFullPath();
1965
1966 wxFileName activeProjectFn( m_frame->Prj().GetProjectFullName() );
1967 activeProjectFn.MakeAbsolute();
1968
1969 // Refuse the self-compare before touching SETTINGS_MANAGER — attaching the
1970 // active PROJECT to a second BOARD would clobber its m_BoardSettings. Use
1971 // wxFileName::SameAs (path normalization + case folding) rather than a raw
1972 // string compare, matching SCH_INSPECTION_TOOL::CompareSchematic.
1973 if( projectFn.SameAs( activeProjectFn ) )
1974 {
1975 m_frame->ShowInfoBarError(
1976 _( "Select a board file from another project to compare." ) );
1977 return 0;
1978 }
1979
1980 return showBoardComparison( otherPath, projectPath, otherPath );
1981}
1982
1983
1984namespace
1985{
1986// Everything needed to show one board-vs-board comparison: the diff, the clones
1987// of removed live-board items (kept alive because the canvas references them),
1988// and the canvas-context switcher. Moveable so it can be swapped on reload.
1989struct PCB_DIFF_VIEW
1990{
1992 std::vector<std::unique_ptr<BOARD_ITEM>> clones;
1994};
1995
1996
1997PCB_DIFF_VIEW buildPcbDiffView( BOARD* aLive, BOARD* aOther, const wxString& aOtherPath )
1998{
1999 PCB_DIFF_VIEW view;
2000
2001 KICAD_DIFF::PCB_DIFFER differ( aLive, aOther, aOtherPath );
2002 view.result = differ.Diff();
2003
2004 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2005
2006 std::map<KIID, KIGFX::COLOR4D> refOverrides;
2007 std::map<KIID, KIGFX::COLOR4D> compOverrides;
2008 std::map<KIID, KICAD_DIFF::CATEGORY> kiidCategories;
2009
2010 std::function<void( const KICAD_DIFF::ITEM_CHANGE& )> collect = [&]( const KICAD_DIFF::ITEM_CHANGE& aChange )
2011 {
2012 if( !aChange.id.empty() )
2013 {
2014 const KIID& kiid = aChange.id.back();
2015 kiidCategories[kiid] = KICAD_DIFF::CategoryFor( aChange.kind );
2016
2017 switch( aChange.kind )
2018 {
2019 case KICAD_DIFF::CHANGE_KIND::ADDED: compOverrides[kiid] = theme.added; break;
2021 refOverrides[kiid] = theme.removed;
2022 compOverrides[kiid] = theme.removed;
2023 break;
2025 refOverrides[kiid] = theme.modified;
2026 compOverrides[kiid] = theme.modified;
2027 break;
2028 default:
2029 refOverrides[kiid] = theme.conflict;
2030 compOverrides[kiid] = theme.conflict;
2031 break;
2032 }
2033 }
2034
2035 for( const KICAD_DIFF::ITEM_CHANGE& child : aChange.children )
2036 collect( child );
2037 };
2038
2039 for( const KICAD_DIFF::ITEM_CHANGE& change : view.result.changes )
2040 collect( change );
2041
2042 // Clone removed items from the live board because their originals are
2043 // pinned to the editor's VIEW.
2044 for( const auto& [kiid, color] : refOverrides )
2045 {
2046 if( color != theme.removed )
2047 continue;
2048
2049 if( BOARD_ITEM* found = aLive->ResolveItem( kiid, /*aAllowNullptrReturn=*/true ) )
2050 {
2051 if( auto* clone = dynamic_cast<BOARD_ITEM*>( found->Clone() ) )
2052 view.clones.emplace_back( clone );
2053 }
2054 }
2055
2056 std::vector<KIGFX::VIEW_ITEM*> extraItems;
2057
2058 for( const auto& clone : view.clones )
2059 {
2060 // A footprint draws nothing itself. Its pads, graphics and fields are
2061 // independent view items, so add them too or the clone is invisible.
2062 if( FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( clone.get() ) )
2063 {
2064 std::vector<KIGFX::VIEW_ITEM*> fpItems = KICAD_DIFF::CollectFootprintDiffContextItems( *fp );
2065 extraItems.insert( extraItems.end(), fpItems.begin(), fpItems.end() );
2066 }
2067 else
2068 {
2069 extraItems.push_back( clone.get() );
2070 }
2071 }
2072
2073 view.switcher = [other = aOther, color = theme.modified, overrides = compOverrides,
2074 extras = std::move( extraItems ),
2075 categories = kiidCategories]( WIDGET_DIFF_CANVAS& aCanvas, const KIID_PATH& )
2076 {
2077 KICAD_DIFF::ConfigurePcbDiffCanvasContext( aCanvas, nullptr, other, color, overrides, extras, categories );
2078 };
2079
2080 return view;
2081}
2082} // namespace
2083
2084
2085int BOARD_INSPECTION_TOOL::showBoardComparison( const wxString& aOtherPath, const wxString& aProjectPath,
2086 const wxString& aComparisonLabel )
2087{
2088 // Load the comparison board into its own non-active PROJECT so the live
2089 // project settings are never overwritten. A missing .kicad_pro is fine
2090 // (LoadProject returns false but still inserts a defaults-only slot); a
2091 // present-but-malformed .kicad_pro is treated as failure.
2092 SETTINGS_MANAGER* mgr = m_frame->GetSettingsManager();
2093 bool projectLoadOk = mgr && mgr->LoadProject( aProjectPath, false );
2094 PROJECT* otherPrj = mgr ? mgr->GetProject( aProjectPath ) : nullptr;
2095
2096 if( !otherPrj )
2097 {
2098 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
2099 return 0;
2100 }
2101
2102 if( !projectLoadOk && wxFileName( aProjectPath ).FileExists() )
2103 {
2104 mgr->UnloadProject( otherPrj, false );
2105 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
2106 return 0;
2107 }
2108
2110
2111 // Skip editor-frame initialization (drawing sheet singleton, BASE_SCREEN
2112 // globals, DRC engine, connectivity) so the read-only compare cannot leak
2113 // state into the live frame.
2114 BOARD_LOADER::OPTIONS loadOptions;
2115 loadOptions.initialize_after_load = false;
2116
2117 std::unique_ptr<BOARD> otherBoard;
2118
2119 try
2120 {
2121 otherBoard = BOARD_LOADER::Load( aOtherPath, pluginType, otherPrj, loadOptions );
2122 }
2123 catch( const IO_ERROR& ioe )
2124 {
2125 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load %s: %s" ), aOtherPath, ioe.What() ) );
2126 mgr->UnloadProject( otherPrj, false );
2127 return 0;
2128 }
2129 catch( ... )
2130 {
2131 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
2132 mgr->UnloadProject( otherPrj, false );
2133 return 0;
2134 }
2135
2136 if( !otherBoard )
2137 {
2138 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
2139 mgr->UnloadProject( otherPrj, false );
2140 return 0;
2141 }
2142
2143 BOARD* board = m_frame->GetBoard();
2144 PCB_DIFF_VIEW view = buildPcbDiffView( board, otherBoard.get(), aOtherPath );
2145
2146 auto dlgDiff = std::make_unique<DIALOG_KICAD_DIFF>( m_frame, board->GetFileName(), aComparisonLabel, view.result,
2148 KICAD_DIFF::DOCUMENT_GEOMETRY{}, view.switcher );
2149 dlgDiff->ShowModal();
2150
2151 // Destroy the dialog (its canvas drops its references to the board's items)
2152 // before the board is freed, or teardown dereferences freed items.
2153 dlgDiff.reset();
2154
2155 otherBoard->ClearProject();
2156 otherBoard.reset();
2157
2158 mgr->UnloadProject( otherPrj, false );
2159
2160 return 0;
2161}
2162
2163
2165{
2166 wxCHECK( m_frame, 0 );
2167
2168 BOARD* liveBoard = m_frame->GetBoard();
2169
2170 if( !liveBoard || liveBoard->GetFileName().IsEmpty() )
2171 {
2172 m_frame->ShowInfoBarError( _( "Save the board before comparing against local history." ) );
2173 return 0;
2174 }
2175
2176 const wxString projectPath = m_frame->Prj().GetProjectPath();
2177 LOCAL_HISTORY& history = m_frame->Kiway().LocalHistory();
2178
2179 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> snapshots = history.GetSnapshots( projectPath );
2180
2181 if( snapshots.empty() )
2182 {
2183 m_frame->ShowInfoBarError( _( "No local history snapshots for this project." ) );
2184 return 0;
2185 }
2186
2187 // The history repo stores files by project-relative path with forward slashes.
2188 wxFileName boardFn( liveBoard->GetFileName() );
2189 boardFn.MakeRelativeTo( projectPath );
2190 const wxString relPath = boardFn.GetFullPath( wxPATH_UNIX );
2191
2192 wxFileName projFn( m_frame->Prj().GetProjectFullName() );
2193 projFn.MakeRelativeTo( projectPath );
2194 const wxString projRel = projFn.GetFullPath( wxPATH_UNIX );
2195
2196 // One entry per distinct board version: newest-first, skipping commits with
2197 // no board or whose board content matches the previously kept one. This
2198 // drops schematic-only saves and collapses runs that left the board untouched.
2199 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> boardSnapshots;
2200 wxString prevFingerprint;
2201
2202 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
2203 {
2204 wxString fingerprint = history.TreeFingerprint( projectPath, s.hash, wxS( ".kicad_pcb" ) );
2205
2206 if( fingerprint.IsEmpty() || fingerprint == prevFingerprint )
2207 continue;
2208
2209 prevFingerprint = fingerprint;
2210 boardSnapshots.push_back( s );
2211 }
2212
2213 if( boardSnapshots.empty() )
2214 {
2215 m_frame->ShowInfoBarError( _( "No local history snapshots change this board." ) );
2216 return 0;
2217 }
2218
2219 snapshots = std::move( boardSnapshots );
2220
2221 SETTINGS_MANAGER* mgr = m_frame->GetSettingsManager();
2222
2223 std::vector<wxString> labels;
2224
2225 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
2226 {
2227 wxString summary = s.summary.IsEmpty() ? s.message.BeforeFirst( '\n' ) : s.summary;
2228 labels.push_back( wxString::Format( wxS( "%s (%s)" ), summary, s.hash.Left( 8 ) ) );
2229 }
2230
2231 // State for the revision currently shown. Swapped on each dropdown change.
2232 std::unique_ptr<BOARD> curBoard;
2233 PROJECT* curPrj = nullptr;
2234 wxString curTempDir;
2235
2236 auto cleanupCurrent = [&]()
2237 {
2238 if( curBoard )
2239 {
2240 curBoard->ClearProject();
2241 curBoard.reset();
2242 }
2243
2244 // Skip if the project was already evicted from the manager.
2245 if( curPrj && mgr->IsProjectLoaded( curPrj ) )
2246 mgr->UnloadProject( curPrj, false );
2247
2248 curPrj = nullptr;
2249
2250 if( !curTempDir.IsEmpty() )
2251 {
2252 wxFileName::Rmdir( curTempDir, wxPATH_RMDIR_RECURSIVE );
2253 curTempDir.Clear();
2254 }
2255 };
2256
2257 // Extract + load snapshot aIndex into the out-params. Cleans up its own temp
2258 // on any failure.
2259 auto loadRevision = [&]( int aIndex, std::unique_ptr<BOARD>& aBoard, PROJECT*& aPrj, wxString& aTempDir ) -> bool
2260 {
2261 const wxString hash = snapshots[aIndex].hash;
2262 wxFileName dirFn;
2263 dirFn.AssignDir( wxFileName::GetTempDir() );
2264 dirFn.AppendDir( wxS( "kicad-history-" ) + hash.Left( 8 ) );
2265 const wxString tempDir = dirFn.GetPath();
2266
2267 // Extract just the board and project file (its real net classes and
2268 // design settings), skipping the schematic, 3D models, gerbers, etc.
2269 if( !history.ExtractAllFilesAtCommit( projectPath, hash, tempDir,
2270 { wxS( ".kicad_pcb" ), wxS( ".kicad_pro" ) } ) )
2271 {
2272 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2273 return false;
2274 }
2275
2276 const wxString boardPath = tempDir + wxS( "/" ) + relPath;
2277 const wxString proPath = tempDir + wxS( "/" ) + projRel;
2278
2279 mgr->LoadProject( proPath, false );
2280 PROJECT* prj = mgr->GetProject( proPath );
2281
2282 if( !prj )
2283 {
2284 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2285 return false;
2286 }
2287
2288 BOARD_LOADER::OPTIONS loadOptions;
2289 loadOptions.initialize_after_load = false;
2290
2291 std::unique_ptr<BOARD> loaded;
2292
2293 try
2294 {
2295 loaded = BOARD_LOADER::Load( boardPath,
2297 loadOptions );
2298 }
2299 catch( ... )
2300 {
2301 // A historical board may be malformed or in a format this build
2302 // cannot parse. Skip it rather than letting the throw escape.
2303 mgr->UnloadProject( prj, false );
2304 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2305 return false;
2306 }
2307
2308 if( !loaded )
2309 {
2310 mgr->UnloadProject( prj, false );
2311 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2312 return false;
2313 }
2314
2315 aBoard = std::move( loaded );
2316 aPrj = prj;
2317 aTempDir = tempDir;
2318 return true;
2319 };
2320
2321 // Load a revision and build its diff view, guarding both the parse and the
2322 // diff so a bad snapshot is skipped instead of crashing.
2323 auto loadView = [&]( int aIndex, std::unique_ptr<BOARD>& aBoard, PROJECT*& aPrj, wxString& aTempDir,
2324 PCB_DIFF_VIEW& aView ) -> bool
2325 {
2326 if( !loadRevision( aIndex, aBoard, aPrj, aTempDir ) )
2327 return false;
2328
2329 try
2330 {
2331 aView = buildPcbDiffView( liveBoard, aBoard.get(), aTempDir + wxS( "/" ) + relPath );
2332 }
2333 catch( ... )
2334 {
2335 aBoard->ClearProject();
2336 aBoard.reset();
2337 mgr->UnloadProject( aPrj, false );
2338 aPrj = nullptr;
2339 wxFileName::Rmdir( aTempDir, wxPATH_RMDIR_RECURSIVE );
2340 aTempDir.Clear();
2341 return false;
2342 }
2343
2344 return true;
2345 };
2346
2347 PCB_DIFF_VIEW view;
2348 int startIndex = 0;
2349
2350 if( !loadView( 0, curBoard, curPrj, curTempDir, view ) )
2351 {
2352 m_frame->ShowInfoBarError( _( "Could not compare against the selected snapshot." ) );
2353 return 0;
2354 }
2355
2356 // Default to the first commit back from HEAD that actually differs from the
2357 // current board, so opening lands on real changes rather than an in-sync HEAD.
2358 while( view.result.Empty() && startIndex + 1 < static_cast<int>( snapshots.size() ) )
2359 {
2360 std::unique_ptr<BOARD> nextBoard;
2361 PROJECT* nextPrj = nullptr;
2362 wxString nextTempDir;
2363 PCB_DIFF_VIEW nextView;
2364
2365 if( !loadView( startIndex + 1, nextBoard, nextPrj, nextTempDir, nextView ) )
2366 break;
2367
2368 cleanupCurrent();
2369 view = std::move( nextView );
2370 curBoard = std::move( nextBoard );
2371 curPrj = nextPrj;
2372 curTempDir = nextTempDir;
2373 startIndex++;
2374 }
2375
2376 auto dlgDiff = std::make_unique<DIALOG_KICAD_DIFF>( m_frame, liveBoard->GetFileName(), labels[startIndex],
2378 KICAD_DIFF::DOCUMENT_GEOMETRY{}, view.switcher );
2379
2380 dlgDiff->SetRevisionChooser(
2381 labels, startIndex,
2382 [&]( int aIndex )
2383 {
2384 std::unique_ptr<BOARD> newBoard;
2385 PROJECT* newPrj = nullptr;
2386 wxString newTempDir;
2387 PCB_DIFF_VIEW newView;
2388
2389 if( !loadView( aIndex, newBoard, newPrj, newTempDir, newView ) )
2390 {
2391 m_frame->ShowInfoBarError( _( "Could not compare against the selected snapshot." ) );
2392 return;
2393 }
2394
2395 dlgDiff->Reload( liveBoard->GetFileName(), labels[aIndex], newView.result,
2396 /*aReferenceGeometry=*/{}, /*aComparisonGeometry=*/{}, newView.switcher, {} );
2397
2398 // The canvas now references newBoard, so the previous one can go.
2399 cleanupCurrent();
2400 view = std::move( newView );
2401 curBoard = std::move( newBoard );
2402 curPrj = newPrj;
2403 curTempDir = newTempDir;
2404 } );
2405
2406 dlgDiff->ShowModal();
2407
2408 // Destroy the dialog before the board it references is freed.
2409 dlgDiff.reset();
2410
2411 cleanupCurrent();
2412 return 0;
2413}
2414
2415
2417{
2418 wxCHECK( m_frame, 0 );
2419
2420 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2421
2422 wxCHECK( selTool, 0 );
2423
2424 const PCB_SELECTION& selection = selTool->GetSelection();
2425
2426 if( selection.Size() != 1 || selection.Front()->Type() != PCB_FOOTPRINT_T )
2427 {
2428 m_frame->ShowInfoBarError( _( "Select a footprint for a footprint associations report." ) );
2429 return 0;
2430 }
2431
2432 DIALOG_FOOTPRINT_ASSOCIATIONS dlg( m_frame, static_cast<FOOTPRINT*>( selection.Front() ) );
2433
2434 dlg.ShowModal();
2435
2436 return 0;
2437}
2438
2439
2440void BOARD_INSPECTION_TOOL::DiffFootprint( FOOTPRINT* aFootprint, wxTopLevelWindow* aReparentTo )
2441{
2442 DIALOG_BOOK_REPORTER* dialog = m_frame->GetFootprintDiffDialog();
2443
2444 wxCHECK( dialog, /* void */ );
2445
2446 dialog->DeleteAllPages();
2447 dialog->SetUserItemID( aFootprint->m_Uuid );
2448
2449 LIB_ID fpID = aFootprint->GetFPID();
2450 wxString libName = fpID.GetLibNickname();
2451 wxString fpName = fpID.GetLibItemName();
2452 WX_HTML_REPORT_BOX* r = nullptr;
2453
2454 r = dialog->AddHTMLPage( _( "Summary" ) );
2455
2456 r->Report( wxS( "<h7>" ) + _( "Board vs library diff for:" ) + wxS( "</h7>" ) );
2457 r->Report( wxS( "<ul><li>" ) + EscapeHTML( getItemDescription( aFootprint ) ) + wxS( "</li>" )
2458 + wxS( "<li>" ) + _( "Library: " ) + EscapeHTML( libName ) + wxS( "</li>" )
2459 + wxS( "<li>" ) + _( "Library item: " ) + EscapeHTML( fpName ) + wxS( "</li></ul>" ) );
2460
2461 r->Report( "" );
2462
2463 PROJECT* project = aFootprint->GetBoard()->GetProject();
2465
2466 if( !adapter->HasLibrary( libName, false ) )
2467 {
2468 r->Report( _( "The library is not included in the current configuration." )
2469 + wxS( "&nbsp;&nbsp;&nbsp" )
2470 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Footprint Libraries" )
2471 + wxS( "</a>" ) );
2472
2473 }
2474 else if( !adapter->HasLibrary( libName, true ) )
2475 {
2476 r->Report( _( "The library is not enabled in the current configuration." )
2477 + wxS( "&nbsp;&nbsp;&nbsp" )
2478 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Footprint Libraries" )
2479 + wxS( "</a>" ) );
2480
2481 }
2482 else
2483 {
2484 std::shared_ptr<FOOTPRINT> libFootprint;
2485
2486 try
2487 {
2488 libFootprint.reset( adapter->LoadFootprint( libName, fpName, true ) );
2489 }
2490 catch( const IO_ERROR& )
2491 {
2492 }
2493
2494 if( !libFootprint )
2495 {
2496 r->Report( wxString::Format( _( "The library no longer contains the item %s." ),
2497 fpName) );
2498 }
2499 else
2500 {
2501 if( !aFootprint->FootprintNeedsUpdate( libFootprint.get(), 0, r ) )
2502 r->Report( _( "No relevant differences detected." ) );
2503
2504 wxPanel* panel = dialog->AddBlankPage( _( "Visual" ) );
2506
2507 diff->DisplayDiff( aFootprint, libFootprint );
2508 }
2509 }
2510
2511 r->Flush();
2512
2513 if( aReparentTo )
2514 KIPLATFORM::UI::ReparentWindow( dialog, aReparentTo );
2515 else
2516 dialog->Raise();
2517
2518 dialog->Show( true );
2519}
2520
2521
2523{
2524 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
2525
2526 FOOTPRINT_DIFF_WIDGET* diffWidget = new FOOTPRINT_DIFF_WIDGET( aParentPanel, m_frame->Kiway() );
2527
2528 sizer->Add( diffWidget, 1, wxEXPAND | wxALL, 5 );
2529 aParentPanel->SetSizer( sizer );
2530 aParentPanel->Layout();
2531
2532 return diffWidget;
2533}
2534
2535
2537{
2538 BOARD_ITEM* item = aEvent.Parameter<BOARD_ITEM*>();
2539
2540 m_frame->m_ProbingSchToPcb = true; // recursion guard
2541 {
2542 m_toolMgr->RunAction( ACTIONS::selectionClear );
2543
2544 if( item )
2545 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, item );
2546 }
2547 m_frame->m_ProbingSchToPcb = false;
2548
2549 bool request3DviewRedraw = frame()->GetPcbNewSettings()->m_Display.m_Live3DRefresh;
2550
2551 if( item && item->Type() != PCB_FOOTPRINT_T )
2552 request3DviewRedraw = false;
2553
2554 // Update 3D viewer highlighting
2555 if( request3DviewRedraw )
2556 m_frame->Update3DView( false, true );
2557
2558 return 0;
2559}
2560
2561
2562 bool BOARD_INSPECTION_TOOL::highlightNet( const VECTOR2D& aPosition, bool aUseSelection )
2563{
2564 BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
2566 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2567
2568 int net = -1;
2569 bool enableHighlight = false;
2570
2571 if( aUseSelection )
2572 {
2573 const PCB_SELECTION& selection = selectionTool->GetSelection();
2574 std::set<int> netcodes;
2575
2576 for( EDA_ITEM* item : selection )
2577 {
2578 if( BOARD_CONNECTED_ITEM* ci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2579 netcodes.insert( ci->GetNetCode() );
2580 }
2581
2582 enableHighlight = !netcodes.empty();
2583
2584 if( enableHighlight && netcodes.size() > 1 )
2585 {
2586 // If we are doing a multi-highlight, cross-probing back and other stuff is not
2587 // yet supported
2588 settings->SetHighlight( netcodes );
2589 board->ResetNetHighLight();
2590
2591 for( int multiNet : netcodes )
2592 board->SetHighLightNet( multiNet, true );
2593
2594 board->HighLightON();
2595 m_toolMgr->GetView()->UpdateAllLayersColor();
2596 m_currentlyHighlighted = netcodes;
2597 return true;
2598 }
2599 else if( enableHighlight )
2600 {
2601 net = *netcodes.begin();
2602 }
2603 }
2604
2605 // If we didn't get a net to highlight from the selection, use the cursor
2606 if( net < 0 )
2607 {
2608 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
2609 guide.SetIgnoreZoneFills( false );
2610 guide.SetIgnoreNoNets( true );
2611
2612 PCB_LAYER_ID activeLayer = static_cast<PCB_LAYER_ID>( view()->GetTopLayer() );
2613 guide.SetPreferredLayer( activeLayer );
2614
2615 GENERAL_COLLECTOR collector;
2616 collector.Collect( board, { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, PCB_SHAPE_T }, aPosition,
2617 guide );
2618
2619 if( collector.GetCount() == 0 )
2620 collector.Collect( board, { PCB_ZONE_T }, aPosition, guide );
2621
2622 // Apply the active selection filter, except we want to allow picking locked items for
2623 // highlighting even if the user has disabled them for selection
2624 PCB_SELECTION_FILTER_OPTIONS& filter = selectionTool->GetFilter();
2625
2626 bool saved = filter.lockedItems;
2627 filter.lockedItems = true;
2628
2629 selectionTool->FilterCollectedItems( collector, true, nullptr );
2630
2631 filter.lockedItems = saved;
2632
2633 // Clear the previous highlight
2634 //m_frame->SendMessageToEESCHEMA( nullptr );
2635
2636 bool highContrast = settings->GetHighContrast();
2637 PCB_LAYER_ID contrastLayer = settings->GetPrimaryHighContrastLayer();
2638
2639 for( int i = collector.GetCount() - 1; i >= 0; i-- )
2640 {
2641 LSET itemLayers = collector[i]->GetLayerSet();
2642
2643 if( ( itemLayers & LSET::AllCuMask() ).none() ||
2644 ( highContrast && !itemLayers.Contains( contrastLayer ) ) )
2645 {
2646 collector.Remove( i );
2647 continue;
2648 }
2649 }
2650
2651 enableHighlight = ( collector.GetCount() > 0 );
2652
2653 // Obtain net code for the clicked item
2654 if( enableHighlight )
2655 {
2656 BOARD_CONNECTED_ITEM* targetItem = static_cast<BOARD_CONNECTED_ITEM*>( collector[0] );
2657
2658 if( targetItem->Type() == PCB_PAD_T )
2659 m_frame->SendCrossProbeItem( targetItem );
2660
2661 net = targetItem->GetNetCode();
2662 }
2663 }
2664
2665 const std::set<int>& netcodes = settings->GetHighlightNetCodes();
2666
2667 if( !aUseSelection && net >= 0 && netcodes.size() == 1 && netcodes.contains( net ) && settings->IsHighlightEnabled() )
2668 {
2669 if( BOARD* board2 = m_frame->GetBoard() )
2670 {
2671 if( NETINFO_ITEM* netinfo = board2->FindNet( net ) )
2672 {
2673 wxString sig = netinfo->GetNetChain();
2674 if( !sig.IsEmpty() )
2675 {
2676 int count = 0;
2677 for( NETINFO_ITEM* n : board2->GetNetInfo() )
2678 if( n->GetNetChain() == sig )
2679 count++;
2680
2681 if( count > 1 )
2682 {
2683 std::set<int> sigCodes;
2684
2685
2686 for( NETINFO_ITEM* n : board2->GetNetInfo() )
2687 if( n->GetNetChain() == sig )
2688 sigCodes.insert( n->GetNetCode() );
2689
2690 settings->SetHighlight( sigCodes, true );
2691 m_toolMgr->GetView()->UpdateAllLayersColor();
2692 m_currentlyHighlighted = sigCodes;
2694
2695 board2->ResetNetHighLight();
2696 for( int c : sigCodes )
2697 board2->SetHighLightNet( c, true );
2698 board2->HighLightON();
2699
2700 if( auto pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2701 pcbSettings->SetHighlightedNetChain( sig );
2702
2703 return true;
2704 }
2705 }
2706 }
2707 }
2708
2709 enableHighlight = !settings->IsHighlightEnabled();
2710 }
2711 else if( !aUseSelection && netcodes.size() == 1 && netcodes.contains( net ) )
2712 {
2713 enableHighlight = !settings->IsHighlightEnabled();
2714 }
2715
2716 if( enableHighlight != settings->IsHighlightEnabled() || !netcodes.count( net ) )
2717 {
2718 if( !netcodes.empty() )
2719 m_lastHighlighted = netcodes;
2720
2721 settings->SetHighlight( enableHighlight, net );
2722 m_toolMgr->GetView()->UpdateAllLayersColor();
2723 }
2724
2725 // Store the highlighted netcode in the current board (for dialogs for instance)
2726 if( enableHighlight && net >= 0 )
2727 {
2728 m_currentlyHighlighted = netcodes;
2729 board->SetHighLightNet( net );
2730 board->HighLightON();
2731
2732 NETINFO_ITEM* netinfo = board->FindNet( net );
2733
2734 if( netinfo )
2735 {
2736 std::vector<MSG_PANEL_ITEM> items;
2737 netinfo->GetMsgPanelInfo( m_frame, items );
2738 m_frame->SetMsgPanel( items );
2739 m_frame->SendCrossProbeNetName( netinfo->GetNetname() );
2740 }
2741 }
2742 else
2743 {
2744 m_currentlyHighlighted.clear();
2745 board->ResetNetHighLight();
2746 m_frame->SetMsgPanel( board );
2747 m_frame->SendCrossProbeNetName( "" );
2748 }
2749
2750 return true;
2751}
2752
2753
2755{
2756 int netcode = aEvent.Parameter<int>();
2757 KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings();
2758 const std::set<int>& highlighted = settings->GetHighlightNetCodes();
2759
2760 if( netcode > 0 )
2761 {
2762 m_lastHighlighted = highlighted;
2763 settings->SetHighlight( true, netcode );
2764 m_toolMgr->GetView()->UpdateAllLayersColor();
2765 m_currentlyHighlighted.clear();
2766 m_currentlyHighlighted.insert( netcode );
2767
2768 // If this net belongs to a multi-net chain and was already highlighted, escalate to chain highlight
2769 if( BOARD* board = m_frame->GetBoard() )
2770 {
2771 if( NETINFO_ITEM* net = board->FindNet( netcode ) )
2772 {
2773 wxString sig = net->GetNetChain();
2774 if( !sig.IsEmpty() )
2775 {
2776 int count = 0;
2777 for( NETINFO_ITEM* n : board->GetNetInfo() )
2778 if( n->GetNetChain() == sig )
2779 count++;
2780 bool alreadyHighlighted = highlighted.count( netcode );
2781 if( count > 1 && alreadyHighlighted )
2782 {
2783 if( m_highlightedNetChain != sig )
2784 {
2785 TOOL_EVENT sigEvt = PCB_ACTIONS::highlightNetChain.MakeEvent();
2786 HighlightNetChain( sigEvt );
2787 }
2788 }
2789 }
2790 }
2791 }
2792 }
2793 else if( aEvent.IsAction( &PCB_ACTIONS::highlightNetSelection ) )
2794 {
2795 // Highlight selection (cursor position will be ignored)
2796 highlightNet( getViewControls()->GetMousePosition(), true );
2797 }
2798 else if( aEvent.IsAction( &PCB_ACTIONS::toggleLastNetHighlight ) )
2799 {
2800 std::set<int> temp = highlighted;
2801 settings->SetHighlight( m_lastHighlighted );
2802 m_toolMgr->GetView()->UpdateAllLayersColor();
2804 m_lastHighlighted = std::move( temp );
2805 }
2806 else if( aEvent.IsAction( &PCB_ACTIONS::toggleNetHighlight ) )
2807 {
2808 bool turnOn = highlighted.empty() && !m_currentlyHighlighted.empty();
2809 settings->SetHighlight( m_currentlyHighlighted, turnOn );
2810 m_toolMgr->GetView()->UpdateAllLayersColor();
2811 }
2812 else // Highlight the net belonging to the item under the cursor
2813 {
2814 highlightNet( getViewControls()->GetMousePosition(), false );
2815 }
2816
2817 return 0;
2818}
2819
2820
2822{
2824 VECTOR2D cursorPos = controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
2825 BOARD_ITEM* item = nullptr;
2826 {
2827 // Collect nearest connectable item at cursor position
2828 BOARD* board = m_frame->GetBoard();
2829 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
2830 GENERAL_COLLECTOR collector;
2831 collector.Collect( board, { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, PCB_SHAPE_T }, cursorPos,
2832 guide );
2833
2834 if( collector.GetCount() > 0 )
2835 item = static_cast<BOARD_ITEM*>( collector[0] );
2836 }
2837 wxString sig;
2838
2839 if( item )
2840 {
2841 NETINFO_ITEM* net = nullptr;
2842
2843 if( auto pad = dynamic_cast<PAD*>( item ) )
2844 net = pad->GetNet();
2845 else if( auto ci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2846 net = ci->GetNet();
2847
2848 if( net )
2849 sig = net->GetNetChain();
2850 }
2851
2852 KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings();
2853
2854 // If no chain under cursor and a chain is currently highlighted, toggle off
2855 if( sig.IsEmpty() && !m_highlightedNetChain.IsEmpty() )
2856 {
2857 m_highlightedNetChain.clear();
2858 settings->SetHighlight( false );
2859 m_currentlyHighlighted.clear();
2860 m_toolMgr->GetView()->UpdateAllLayersColor();
2861 if( auto pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2862 pcbSettings->SetHighlightedNetChain( wxString() );
2863 return 0;
2864 }
2865
2866 // If same chain already highlighted, clear highlight
2867 if( !sig.IsEmpty() && sig == m_highlightedNetChain )
2868 {
2869 m_highlightedNetChain.clear();
2870 settings->SetHighlight( false );
2871 m_currentlyHighlighted.clear();
2872 m_toolMgr->GetView()->UpdateAllLayersColor();
2873 if( auto pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2874 pcbSettings->SetHighlightedNetChain( wxString() );
2875 return 0;
2876 }
2877
2878 // If we have a net highlight active but no chain highlight, convert to chain highlight
2879 if( !sig.IsEmpty() && m_highlightedNetChain.IsEmpty() && !m_currentlyHighlighted.empty() )
2880 {
2881 // Determine chain from first highlighted net if cursor item had no chain
2882 if( sig.IsEmpty() )
2883 {
2884 int firstCode = *m_currentlyHighlighted.begin();
2885 if( NETINFO_ITEM* net = m_frame->GetBoard()->FindNet( firstCode ) )
2886 sig = net->GetNetChain();
2887 }
2888 }
2889
2891 if( auto pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2892 pcbSettings->SetHighlightedNetChain( sig );
2893
2894 std::set<int> codes;
2895 if( !sig.IsEmpty() )
2896 {
2897 for( NETINFO_ITEM* net : m_frame->GetBoard()->GetNetInfo() )
2898 {
2899 if( net->GetNetChain() == sig )
2900 codes.insert( net->GetNetCode() );
2901 }
2902 }
2903
2904 settings->SetHighlight( codes, true );
2905 m_toolMgr->GetView()->UpdateAllLayersColor();
2906 m_currentlyHighlighted = codes;
2907
2908 return 0;
2909}
2910
2911
2913{
2914 if( m_highlightedNetChain.IsEmpty() )
2915 return 0;
2916
2917 // Parameters are passed as a single pair<old,new>
2918 auto ids = aEvent.Parameter<std::pair<wxString, wxString>>();
2919 KIID oldId( ids.first );
2920 KIID newId( ids.second );
2921 m_frame->GetBoard()->ReplaceNetChainTerminalPad( m_highlightedNetChain, oldId, newId );
2922 return 0;
2923}
2924
2925
2927{
2928 BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
2929 KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings();
2930
2931 m_currentlyHighlighted.clear();
2932 m_lastHighlighted.clear();
2933
2934 board->ResetNetHighLight();
2935 settings->SetHighlight( false );
2936 // Also clear any chain-specific state
2937 if( auto pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2938 pcbSettings->SetHighlightedNetChain( wxString() );
2939 m_toolMgr->GetView()->UpdateAllLayersColor();
2940 m_frame->SetMsgPanel( board );
2941 m_frame->SendCrossProbeNetName( "" );
2942 return 0;
2943}
2944
2945
2947{
2948 PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
2949
2950 // Deactivate other tools; particularly important if another PICKER is currently running
2951 Activate();
2952
2953 picker->SetCursor( KICURSOR::BULLSEYE );
2954 picker->SetSnapping( false );
2955 picker->ClearHandlers();
2956
2957 picker->SetClickHandler(
2958 [this]( const VECTOR2D& pt ) -> bool
2959 {
2960 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2961
2962 m_toolMgr->RunAction( ACTIONS::selectionClear );
2964 EDIT_TOOL::PadFilter );
2965
2966 PCB_SELECTION& selection = selectionTool->GetSelection();
2967
2968 if( selection.Empty() )
2969 {
2971 EDIT_TOOL::FootprintFilter );
2972 selection = selectionTool->GetSelection();
2973 }
2974
2975 if( selection.Empty() )
2976 {
2977 // Clear the previous local ratsnest if we click off all items
2978 for( FOOTPRINT* fp : getModel<BOARD>()->Footprints() )
2979 {
2980 for( PAD* pad : fp->Pads() )
2981 pad->SetLocalRatsnestVisible( displayOptions().m_ShowGlobalRatsnest );
2982 }
2983 }
2984 else
2985 {
2986 for( EDA_ITEM* item : selection )
2987 {
2988 if( PAD* pad = dyn_cast<PAD*>( item) )
2989 {
2990 pad->SetLocalRatsnestVisible( !pad->GetLocalRatsnestVisible() );
2991 }
2992 else if( FOOTPRINT* fp = dyn_cast<FOOTPRINT*>( item) )
2993 {
2994 if( !fp->Pads().empty() )
2995 {
2996 bool enable = !fp->Pads()[0]->GetLocalRatsnestVisible();
2997
2998 for( PAD* childPad : fp->Pads() )
2999 childPad->SetLocalRatsnestVisible( enable );
3000 }
3001 }
3002 }
3003 }
3004
3005 m_toolMgr->GetView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
3006
3007 return true;
3008 } );
3009
3010 picker->SetFinalizeHandler(
3011 [this]( int aCondition )
3012 {
3013 if( aCondition != PCB_PICKER_TOOL::END_ACTIVATE )
3014 {
3015 for( FOOTPRINT* fp : getModel<BOARD>()->Footprints() )
3016 {
3017 for( PAD* pad : fp->Pads() )
3018 pad->SetLocalRatsnestVisible( displayOptions().m_ShowGlobalRatsnest );
3019 }
3020 }
3021 } );
3022
3023 m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
3024
3025 return 0;
3026}
3027
3028
3030{
3031 VECTOR2I delta = aEvent.Parameter<VECTOR2I>();
3032
3033 if( delta == VECTOR2I() )
3034 {
3035 // We can delete the existing map to force a recalculation
3036 delete m_dynamicData;
3037 m_dynamicData = nullptr;
3038 }
3039
3040 auto selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3041 auto& selection = selectionTool->GetSelection();
3042 auto connectivity = getModel<BOARD>()->GetConnectivity();
3043
3044 if( selection.Empty() )
3045 {
3046 connectivity->ClearLocalRatsnest();
3047 delete m_dynamicData;
3048 m_dynamicData = nullptr;
3049 }
3050 else
3051 {
3053 }
3054
3055 return 0;
3056}
3057
3058
3060{
3061 getModel<BOARD>()->GetConnectivity()->ClearLocalRatsnest();
3062 delete m_dynamicData;
3063 m_dynamicData = nullptr;
3064
3065 return 0;
3066}
3067
3068
3070{
3071 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3072 SELECTION& selection = selectionTool->GetSelection();
3073 std::shared_ptr<CONNECTIVITY_DATA> connectivity = board()->GetConnectivity();
3074 std::vector<BOARD_ITEM*> items;
3075 std::deque<EDA_ITEM*> queued_items( selection.begin(), selection.end() );
3076
3077 for( std::size_t i = 0; i < queued_items.size(); ++i )
3078 {
3079 if( !queued_items[i]->IsBOARD_ITEM() )
3080 continue;
3081
3082 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( queued_items[i] );
3083
3084 wxCHECK2( item, continue );
3085
3086 if( item->Type() == PCB_FOOTPRINT_T )
3087 {
3088 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
3089 {
3090 if( pad->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
3091 items.push_back( pad );
3092 }
3093 }
3094 else if( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T )
3095 {
3096 item->RunOnChildren( [ &queued_items ]( BOARD_ITEM *aItem )
3097 {
3098 queued_items.push_back( aItem );
3099 },
3101 }
3102 else if( BOARD_CONNECTED_ITEM* boardItem = dyn_cast<BOARD_CONNECTED_ITEM*>( item ) )
3103 {
3104 if( boardItem->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
3105 items.push_back( boardItem );
3106 }
3107 }
3108
3109 if( items.empty() || std::none_of( items.begin(), items.end(),
3110 []( const BOARD_ITEM* aItem )
3111 {
3112 return( aItem->Type() == PCB_TRACE_T
3113 || aItem->Type() == PCB_PAD_T
3114 || aItem->Type() == PCB_ARC_T
3115 || aItem->Type() == PCB_ZONE_T
3116 || aItem->Type() == PCB_FOOTPRINT_T
3117 || aItem->Type() == PCB_VIA_T
3118 || aItem->Type() == PCB_SHAPE_T );
3119 } ) )
3120 {
3121 return;
3122 }
3123
3124 if( !m_dynamicData )
3125 {
3126 m_dynamicData = new CONNECTIVITY_DATA( board()->GetConnectivity(), items, true );
3127 connectivity->BlockRatsnestItems( items );
3128 }
3129 else
3130 {
3131 m_dynamicData->Move( aDelta );
3132 }
3133
3134 connectivity->ComputeLocalRatsnest( items, m_dynamicData );
3135}
3136
3137
3139{
3140 doHideRatsnestNet( aEvent.Parameter<int>(), true );
3141 return 0;
3142}
3143
3144
3146{
3147 doHideRatsnestNet( aEvent.Parameter<int>(), false );
3148 return 0;
3149}
3150
3151
3152void BOARD_INSPECTION_TOOL::doHideRatsnestNet( int aNetCode, bool aHide )
3153{
3155 m_toolMgr->GetView()->GetPainter()->GetSettings() );
3156
3157 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3158 SELECTION& selection = selectionTool->GetSelection();
3159
3160 if( aNetCode <= 0 && !selection.Empty() )
3161 {
3162 for( EDA_ITEM* item : selection )
3163 {
3164 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
3165 {
3166 if( bci->GetNetCode() > 0 )
3167 doHideRatsnestNet( bci->GetNetCode(), aHide );
3168 }
3169 }
3170
3171 return;
3172 }
3173
3174 if( aHide )
3175 rs->GetHiddenNets().insert( aNetCode );
3176 else
3177 rs->GetHiddenNets().erase( aNetCode );
3178
3179 if( !m_frame->GetAppearancePanel()->IsTogglingNetclassRatsnestVisibility() )
3180 {
3181 m_frame->GetCanvas()->RedrawRatsnest();
3182 m_frame->GetCanvas()->Refresh();
3183
3184 m_frame->GetAppearancePanel()->OnNetVisibilityChanged( aNetCode, !aHide );
3185 }
3186}
3187
3188
3190{
3194
3203
3212
3215}
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:80
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:264
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:154
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:371
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:346
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:284
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:193
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition board_item.h:228
virtual bool HasDrilledHole() const
Definition board_item.h:181
virtual bool IsOnCopperLayer() const
Definition board_item.h:171
virtual bool HasHole() const
Definition board_item.h:176
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:320
const wxString & GetFileName() const
Definition board.h:357
PROJECT * GetProject() const
Definition board.h:598
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:1809
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:582
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:425
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:78
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:44
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:34
bool HasMin() const
Definition minoptmax.h:33
T Max() const
Definition minoptmax.h:30
T Opt() const
Definition minoptmax.h:31
bool HasOpt() const
Definition minoptmax.h:35
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
const VECTOR2I & GetDrillSize() const
Definition pad.h:333
PAD_ATTRIB GetAttribute() const
Definition pad.h:579
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:49
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:62
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:100
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:932
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:730
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_HOLE_CLEARANCE
Definition drc_item.h:51
@ DRCE_VIA_DIAMETER
Definition drc_item.h:58
@ DRCE_TRACK_WIDTH
Definition drc_item.h:52
@ DRCE_DRILL_OUT_OF_RANGE
Definition drc_item.h:57
@ DRCE_EDGE_CLEARANCE
Definition drc_item.h:43
@ DRCE_STARVED_THERMAL
Definition drc_item.h:46
@ DRCE_TRACK_SEGMENT_LENGTH
Definition drc_item.h:54
@ DRCE_TRACK_ANGLE
Definition drc_item.h:53
@ DRCE_CLEARANCE
Definition drc_item.h:40
@ DRCE_DRILLED_HOLES_TOO_CLOSE
Definition drc_item.h:49
@ DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG
Definition drc_item.h:106
@ DRCE_MICROVIA_DRILL_OUT_OF_RANGE
Definition drc_item.h:61
@ DRCE_TEXT_HEIGHT
Definition drc_item.h:97
@ DRCE_ASSERTION_FAILURE
Definition drc_item.h:85
@ DRCE_LIB_FOOTPRINT_MISMATCH
Definition drc_item.h:80
@ DRCE_TEXT_THICKNESS
Definition drc_item.h:98
@ DRCE_CONNECTION_WIDTH
Definition drc_item.h:56
@ DRCE_ANNULAR_WIDTH
Definition drc_item.h:55
@ 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:778
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:675
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:187
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:766
@ 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