KiCad PCB EDA Suite
Loading...
Searching...
No Matches
router_tool.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Tomasz Wlostowski <[email protected]>
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <wx/filedlg.h>
24#include <wx/filefn.h>
25#include <wx/hyperlink.h>
26#include <advanced_config.h>
27#include <kiplatform/ui.h>
28
29#include <functional>
30#include <iomanip>
31#include <utility>
32#include <sstream>
33
34using namespace std::placeholders;
35#include <tool/action_manager.h>
36#include <board.h>
38#include <board_item.h>
39#include <netclass.h>
40#include <netinfo.h>
41#include <collectors.h>
42#include <footprint.h>
44#include <pad.h>
45#include <zone.h>
46#include <pcb_edit_frame.h>
47#include <pcb_track.h>
48#include <pcbnew_id.h>
53#include <math/vector2wx.h>
54#include <paths.h>
56#include <confirm.h>
57#include <kidialog.h>
58#include <widgets/wx_infobar.h>
63#include <view/view_controls.h>
64#include <bitmaps.h>
65#include <string_utils.h>
66#include <gal/painter.h>
67#include <tool/tool_action.h>
68#include <tool/action_menu.h>
69#include <tool/tool_manager.h>
70#include <tool/tool_menu.h>
71#include <tools/pcb_actions.h>
73#include <board_commit.h>
75#include <tools/drawing_tool.h>
77#include <tools/drc_tool.h>
80
81#include <project.h>
84
85#include <io/io_utils.h>
86
87#include "router_tool.h"
89#include "pns_router.h"
90#include "pns_itemset.h"
91#include "pns_line.h"
92#include "pns_linked_item.h"
93#include "pns_logger.h"
94#include "pns_node.h"
95#include "pns_optimizer.h"
96#include "pns_placement_algo.h"
97#include "pns_segment.h"
98#include "pns_drag_algo.h"
99#include "pns_kicad_iface.h"
100
102
104
105using namespace KIGFX;
106
107
108const VIA_STACK_PRESET* MatchPendingStackExpansion( PCB_VIA* aVia, const std::set<KIID>& aPreRoute,
109 const std::vector<PENDING_STACK_EXPANSION>& aPending )
110{
111 if( aPreRoute.count( aVia->m_Uuid ) )
112 return nullptr;
113
114 for( const PENDING_STACK_EXPANSION& exp : aPending )
115 {
116 // Net and span alone cannot tell the drop's via from any other the route left behind.
117 if( !exp.m_Pos || *exp.m_Pos != aVia->GetPosition() )
118 continue;
119
120 if( aVia->GetNetCode() != exp.m_Net )
121 continue;
122
123 if( ( aVia->TopLayer() == exp.m_Start && aVia->BottomLayer() == exp.m_End )
124 || ( aVia->TopLayer() == exp.m_End && aVia->BottomLayer() == exp.m_Start ) )
125 {
126 return &exp.m_Preset;
127 }
128 }
129
130 return nullptr;
131}
132
133
135{
136 if( aStart == aEnd )
137 return UNDEFINED_LAYER;
138
139 if( aCurrent == aStart )
140 return aEnd;
141
142 // From the end layer the stack goes back the way it came.
143 if( aCurrent == aEnd )
144 return aStart;
145
146 return UNDEFINED_LAYER;
147}
148
149
150namespace
151{
152
153// Saves and restores the global wxUpdateUIEvent interval so it cannot leak on
154// early returns or exceptions.
155class UI_UPDATE_INTERVAL_GUARD
156{
157public:
158 UI_UPDATE_INTERVAL_GUARD( long aNewInterval ) :
159 m_saved( wxUpdateUIEvent::GetUpdateInterval() )
160 {
161 wxUpdateUIEvent::SetUpdateInterval( aNewInterval );
162 }
163
164 ~UI_UPDATE_INTERVAL_GUARD()
165 {
166 wxUpdateUIEvent::SetUpdateInterval( m_saved );
167 }
168
169private:
170 long m_saved;
171};
172
173} // anonymous namespace
174
179{
180 // Via type
181 VIA_MASK = 0x07,
182 VIA = 0x00,
183 BLIND_VIA = 0x01,
184 BURIED_VIA = 0x02,
185 MICROVIA = 0x04,
186
187 // Select layer
189};
190
191
192// Actions, being statically-defined, require specialized I18N handling. We continue to
193// use the _() macro so that string harvesting by the I18N framework doesn't have to be
194// specialized, but we don't translate on initialization and instead do it in the getters.
195
196#undef _
197#define _(s) s
198
199// Pass all the parameters as int to allow combining flags
201 .Name( "pcbnew.InteractiveRouter.PlaceVia" )
202 .Scope( AS_CONTEXT )
203 .DefaultHotkey( 'V' )
204 .LegacyHotkeyName( "Add Through Via" )
205 .FriendlyName( _( "Place Through Via" ) )
206 .Tooltip( _( "Adds a through-hole via at the end of currently routed track." ) )
207 .Icon( BITMAPS::via )
208 .Flags( AF_NONE )
209 .Parameter<int>( VIA_ACTION_FLAGS::VIA ) );
210
212 .Name( "pcbnew.InteractiveRouter.PlaceBlindVia" )
213 .Scope( AS_CONTEXT )
214 .DefaultHotkey( MD_ALT + MD_SHIFT + 'V' )
215 .LegacyHotkeyName( "Add Blind/Buried Via" )
216 .FriendlyName( _( "Place Blind/Buried Via" ) )
217 .Tooltip( _( "Adds a blind or buried via at the end of currently routed track.") )
218 .Icon( BITMAPS::via_buried )
219 .Flags( AF_NONE )
220 .Parameter<int>( VIA_ACTION_FLAGS::BLIND_VIA ) );
221
223 .Name( "pcbnew.InteractiveRouter.PlaceMicroVia" )
224 .Scope( AS_CONTEXT )
225 .DefaultHotkey( MD_CTRL + 'V' )
226 .LegacyHotkeyName( "Add MicroVia" )
227 .FriendlyName( _( "Place Microvia" ) )
228 .Tooltip( _( "Adds a microvia at the end of currently routed track." ) )
229 .Icon( BITMAPS::via_microvia )
230 .Flags( AF_NONE )
231 .Parameter<int>( VIA_ACTION_FLAGS::MICROVIA ) );
232
235 .Name( "pcbnew.InteractiveRouter.PlaceViaStack" )
236 .Scope( AS_CONTEXT )
237 .DefaultHotkey( MD_CTRL + MD_SHIFT + 'V' )
238 .FriendlyName( _( "Place Microvia Stack at Track End" ) )
239 .Tooltip( _( "Drops the active microvia stack preset at the end of the currently routed track "
240 "and continues on the target layer." ) )
242 .Flags( AF_NONE ) );
243
245 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceVia" )
246 .Scope( AS_CONTEXT )
247 .DefaultHotkey( '<' )
248 .LegacyHotkeyName( "Select Layer and Add Through Via" )
249 .FriendlyName( _( "Select Layer and Place Through Via..." ) )
250 .Tooltip( _( "Select a layer, then add a through-hole via at the end of currently routed track." ) )
252 .Flags( AF_NONE )
254
256 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceBlindVia" )
257 .Scope( AS_CONTEXT )
258 .DefaultHotkey( MD_ALT + '<' )
259 .LegacyHotkeyName( "Select Layer and Add Blind/Buried Via" )
260 .FriendlyName( _( "Select Layer and Place Blind/Buried Via..." ) )
261 .Tooltip( _( "Select a layer, then add a blind or buried via at the end of currently routed track." ) )
263 .Flags( AF_NONE )
265
267 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceMicroVia" )
268 .Scope( AS_CONTEXT )
269 .FriendlyName( _( "Select Layer and Place Micro Via..." ) )
270 .Tooltip( _( "Select a layer, then add a micro via at the end of currently routed track." ) )
272 .Flags( AF_NONE )
274
276 .Name( "pcbnew.InteractiveRouter.CustomTrackViaSize" )
277 .Scope( AS_CONTEXT )
278 .DefaultHotkey( 'Q' )
279 .LegacyHotkeyName( "Custom Track/Via Size" )
280 .FriendlyName( _( "Custom Track/Via Size..." ) )
281 .Tooltip( _( "Shows a dialog for changing the track width and via size." ) )
282 .Icon( BITMAPS::width_track ) );
283
285 .Name( "pcbnew.InteractiveRouter.SwitchPosture" )
286 .Scope( AS_CONTEXT )
287 .DefaultHotkey( '/' )
288 .LegacyHotkeyName( "Switch Track Posture" )
289 .FriendlyName( _( "Switch Track Posture" ) )
290 .Tooltip( _( "Switches posture of the currently routed track." ) )
292
293 // This old command ( track corner switch mode) is now moved to a submenu with other corner mode options
295 .Name( "pcbnew.InteractiveRouter.SwitchRoundingToNext" )
296 .Scope( AS_CONTEXT )
297 .DefaultHotkey( MD_CTRL + '/' )
298 .FriendlyName( _( "Track Corner Mode Switch" ) )
299 .Tooltip( _( "Switches between sharp/rounded and 45°/90° corners when routing tracks." ) )
301
302// hotkeys W and Shift+W are used to switch to track width changes
304 .Name( "pcbnew.InteractiveRouter.SwitchRounding45" )
305 .Scope( AS_CONTEXT )
306 .DefaultHotkey( MD_CTRL + 'W' )
307 .FriendlyName( _( "Track Corner Mode 45" ) )
308 .Tooltip( _( "Switch to 45° corner when routing tracks." ) ) );
309
311 .Name( "pcbnew.InteractiveRouter.SwitchRounding90" )
312 .Scope( AS_CONTEXT )
313 .DefaultHotkey( MD_CTRL + MD_ALT + 'W' )
314 .FriendlyName( _( "Track Corner Mode 90" ) )
315 .Tooltip( _( "Switch to 90° corner when routing tracks." ) ) );
316
318 .Name( "pcbnew.InteractiveRouter.SwitchRoundingArc45" )
319 .Scope( AS_CONTEXT )
320 .DefaultHotkey( MD_CTRL + MD_SHIFT + 'W' )
321 .FriendlyName( _( "Track Corner Mode Arc 45" ) )
322 .Tooltip( _( "Switch to arc 45° corner when routing tracks." ) ) );
323
325 .Name( "pcbnew.InteractiveRouter.SwitchRoundingArc90" )
326 .Scope( AS_CONTEXT )
327 .DefaultHotkey( MD_ALT + 'W' )
328 .FriendlyName( _( "Track Corner Mode Arc 90" ) )
329 .Tooltip( _( "Switch to arc 90° corner when routing tracks." ) ) );
330
331#undef _
332#define _(s) wxGetTranslation((s))
333
334
336 TOOL_BASE( "pcbnew.InteractiveRouter" ),
340 m_inRouterTool( false ),
341 m_inRouteSelected( false ),
342 m_startWithVia( false )
343{
344}
345
346
348{
349public:
351 ACTION_MENU( true ),
352 m_frame( aFrame )
353 {
355 SetTitle( _( "Select Track/Via Width" ) );
356 }
357
358protected:
359 ACTION_MENU* create() const override
360 {
361 return new TRACK_WIDTH_MENU( m_frame );
362 }
363
364 void update() override
365 {
366 BOARD_DESIGN_SETTINGS& bds = m_frame.GetBoard()->GetDesignSettings();
367 bool useIndex = !bds.m_UseConnectedTrackWidth &&
369 wxString msg;
370
371 Clear();
372
373 Append( ID_POPUP_PCB_SELECT_AUTO_WIDTH, _( "Use Starting Track Width" ),
374 _( "Route using the width of the starting track." ), wxITEM_CHECK );
377
378 Append( ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES, _( "Use Net Class Values" ),
379 _( "Use track and via sizes from the net class" ), wxITEM_CHECK );
381 useIndex && bds.GetTrackWidthIndex() == 0 && bds.GetViaSizeIndex() == 0 );
382
383 Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
384 _( "Specify custom track and via sizes" ), wxITEM_CHECK );
386
387 AppendSeparator();
388
389 // Append the list of tracks & via sizes
390 for( unsigned i = 0; i < bds.m_TrackWidthList.size(); i++ )
391 {
392 int width = bds.m_TrackWidthList[i];
393
394 if( i == 0 )
395 msg = _( "Track netclass width" );
396 else
397 msg.Printf( _( "Track %s" ), m_frame.MessageTextFromValue( width ) );
398
399 int menuIdx = ID_POPUP_PCB_SELECT_WIDTH1 + i;
400 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
401 Check( menuIdx, useIndex && bds.GetTrackWidthIndex() == (int) i );
402 }
403
404 AppendSeparator();
405
406 for( unsigned i = 0; i < bds.m_ViasDimensionsList.size(); i++ )
407 {
409
410 if( i == 0 )
411 msg = _( "Via netclass values" );
412 else
413 {
414 if( via.m_Drill > 0 )
415 {
416 msg.Printf( _("Via %s, hole %s" ),
417 m_frame.MessageTextFromValue( via.m_Diameter ),
418 m_frame.MessageTextFromValue( via.m_Drill ) );
419 }
420 else
421 {
422 msg.Printf( _( "Via %s" ),
423 m_frame.MessageTextFromValue( via.m_Diameter ) );
424 }
425 }
426
427 int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
428 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
429 Check( menuIdx, useIndex && bds.GetViaSizeIndex() == (int) i );
430 }
431 }
432
433 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
434 {
435 BOARD_DESIGN_SETTINGS &bds = m_frame.GetBoard()->GetDesignSettings();
436 int id = aEvent.GetId();
437
438 // On Windows, this handler can be called with an event ID not existing in any
439 // menuitem, so only set flags when we have an ID match.
440
442 {
443 bds.UseCustomTrackViaSize( true );
444 bds.m_TempOverrideTrackWidth = true;
445 m_frame.GetToolManager()->RunAction( ACT_CustomTrackWidth );
446 }
447 else if( id == ID_POPUP_PCB_SELECT_AUTO_WIDTH )
448 {
449 bds.UseCustomTrackViaSize( false );
450 bds.m_UseConnectedTrackWidth = true;
451 bds.m_TempOverrideTrackWidth = false;
452 }
454 {
455 bds.UseCustomTrackViaSize( false );
456 bds.m_UseConnectedTrackWidth = false;
457 bds.SetViaSizeIndex( 0 );
458 bds.SetTrackWidthIndex( 0 );
459 }
461 {
462 bds.UseCustomTrackViaSize( false );
464 }
466 {
467 bds.UseCustomTrackViaSize( false );
468 bds.m_TempOverrideTrackWidth = true;
470 }
471
473 }
474
475private:
477};
478
479
481{
482public:
484 ACTION_MENU( true ),
485 m_frame( aFrame )
486 {
488 SetTitle( _( "Select Differential Pair Dimensions" ) );
489 }
490
491protected:
492 ACTION_MENU* create() const override
493 {
494 return new DIFF_PAIR_MENU( m_frame );
495 }
496
497 void update() override
498 {
499 const BOARD_DESIGN_SETTINGS& bds = m_frame.GetBoard()->GetDesignSettings();
500
501 Clear();
502
503 Append( ID_POPUP_PCB_SELECT_USE_NETCLASS_DIFFPAIR, _( "Use Net Class Values" ),
504 _( "Use differential pair dimensions from the net class" ), wxITEM_CHECK );
506 !bds.UseCustomDiffPairDimensions() && bds.GetDiffPairIndex() == 0 );
507
508 Append( ID_POPUP_PCB_SELECT_CUSTOM_DIFFPAIR, _( "Use Custom Values..." ),
509 _( "Specify custom differential pair dimensions" ), wxITEM_CHECK );
511
512 AppendSeparator();
513
514 // Append the list of differential pair dimensions
515
516 // Drop index 0 which is the current netclass dimensions (which are handled above)
517 for( unsigned i = 1; i < bds.m_DiffPairDimensionsList.size(); ++i )
518 {
520 wxString msg;
521
522 if( diffPair.m_Gap <= 0 )
523 {
524 if( diffPair.m_ViaGap <= 0 )
525 {
526 msg.Printf( _( "Width %s" ),
527 m_frame.MessageTextFromValue( diffPair.m_Width ) );
528 }
529 else
530 {
531 msg.Printf( _( "Width %s, via gap %s" ),
532 m_frame.MessageTextFromValue( diffPair.m_Width ),
533 m_frame.MessageTextFromValue( diffPair.m_ViaGap ) );
534 }
535 }
536 else
537 {
538 if( diffPair.m_ViaGap <= 0 )
539 {
540 msg.Printf( _( "Width %s, gap %s" ),
541 m_frame.MessageTextFromValue( diffPair.m_Width ),
542 m_frame.MessageTextFromValue( diffPair.m_Gap ) );
543 }
544 else
545 {
546 msg.Printf( _( "Width %s, gap %s, via gap %s" ),
547 m_frame.MessageTextFromValue( diffPair.m_Width ),
548 m_frame.MessageTextFromValue( diffPair.m_Gap ),
549 m_frame.MessageTextFromValue( diffPair.m_ViaGap ) );
550 }
551 }
552
553 int menuIdx = ID_POPUP_PCB_SELECT_DIFFPAIR1 + i - 1;
554 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
555 Check( menuIdx, !bds.UseCustomDiffPairDimensions() && bds.GetDiffPairIndex() == (int) i );
556 }
557 }
558
559 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
560 {
561 BOARD_DESIGN_SETTINGS &bds = m_frame.GetBoard()->GetDesignSettings();
562 int id = aEvent.GetId();
563
564 // On Windows, this handler can be called with an event ID not existing in any
565 // menuitem, so only set flags when we have an ID match.
566
568 {
569 bds.UseCustomDiffPairDimensions( true );
570 TOOL_MANAGER* toolManager = m_frame.GetToolManager();
572 }
574 {
575 bds.UseCustomDiffPairDimensions( false );
576 bds.SetDiffPairIndex( 0 );
577 }
579 {
580 bds.UseCustomDiffPairDimensions( false );
581 // remember that the menu doesn't contain index 0 (which is the netclass values)
583 }
584
586 }
587
588private:
590};
591
592
596
597
599{
601
603
604 wxASSERT( frame );
605
606 auto& menu = m_menu->GetMenu();
607 menu.SetUntranslatedTitle( _HKI( "Interactive Router" ) );
608
609 m_trackViaMenu = std::make_shared<TRACK_WIDTH_MENU>( *frame );
610 m_trackViaMenu->SetTool( this );
611 m_menu->RegisterSubMenu( m_trackViaMenu );
612
613 m_diffPairMenu = std::make_shared<DIFF_PAIR_MENU>( *frame );
614 m_diffPairMenu->SetTool( this );
615 m_menu->RegisterSubMenu( m_diffPairMenu );
616
617 ACTION_MANAGER* mgr = frame->GetToolManager()->GetActionManager();
618
619 auto haveHighlight =
620 [this]( const SELECTION& sel )
621 {
622 KIGFX::RENDER_SETTINGS* cfg = m_toolMgr->GetView()->GetPainter()->GetSettings();
623
624 return !cfg->GetHighlightNetCodes().empty();
625 };
626
627 auto notRoutingCond =
628 [this]( const SELECTION& )
629 {
630 return !m_router->RoutingInProgress();
631 };
632
633 auto inRouteSelected =
634 [this]( const SELECTION& )
635 {
636 return m_inRouteSelected;
637 };
638
639 auto hasOtherEnd =
640 [this]( const SELECTION& )
641 {
642 std::vector<PNS::NET_HANDLE> currentNets = m_router->GetCurrentNets();
643
644 if( currentNets.empty() || currentNets[0] == nullptr )
645 return false;
646
647 // Need to have something unconnected to finish to
648 NETINFO_ITEM* netInfo = static_cast<NETINFO_ITEM*>( currentNets[0] );
649 int currentNet = netInfo->GetNetCode();
651 RN_NET* ratsnest = board->GetConnectivity()->GetRatsnestForNet( currentNet );
652
653 return ratsnest && !ratsnest->GetEdges().empty();
654 };
655
657 menu.AddItem( PCB_ACTIONS::cancelCurrentItem, inRouteSelected, 1 );
658 menu.AddSeparator( 1 );
659
660 menu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 2 );
661 menu.AddSeparator( haveHighlight, 2 );
662
663 menu.AddItem( PCB_ACTIONS::routeSingleTrack, notRoutingCond );
664 menu.AddItem( PCB_ACTIONS::routeDiffPair, notRoutingCond );
667 menu.AddItem( PCB_ACTIONS::routerContinueFromEnd, hasOtherEnd );
668 menu.AddItem( PCB_ACTIONS::routerAttemptFinish, hasOtherEnd );
669 menu.AddItem( PCB_ACTIONS::routerAutorouteSelected, notRoutingCond
671 menu.AddItem( PCB_ACTIONS::routerOptimizeSelected, notRoutingCond
673 menu.AddItem( PCB_ACTIONS::breakTrack, notRoutingCond );
674
675 menu.AddItem( PCB_ACTIONS::drag45Degree, notRoutingCond );
676 menu.AddItem( PCB_ACTIONS::dragFreeAngle, notRoutingCond );
677
686
687 // Add submenu for track corner mode handling
688 CONDITIONAL_MENU* submenuCornerMode = new CONDITIONAL_MENU( this );
689 submenuCornerMode->SetTitle( _( "Track Corner Mode" ) );
691
693 submenuCornerMode->AddSeparator( 1 );
698
699 menu.AddMenu( submenuCornerMode );
700
701 // Manage check/uncheck marks in this submenu items
702 auto cornerMode45Cond =
703 [this]( const SELECTION& )
704 {
705 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::MITERED_45;
706 };
707
708 auto cornerMode90Cond =
709 [this]( const SELECTION& )
710 {
711 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::MITERED_90;
712 };
713
714 auto cornerModeArc45Cond =
715 [this]( const SELECTION& )
716 {
717 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::ROUNDED_45;
718 };
719
720 auto cornerModeArc90Cond =
721 [this]( const SELECTION& )
722 {
723 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::ROUNDED_90;
724 };
725
726#define CHECK( x ) ACTION_CONDITIONS().Check( x )
727 mgr->SetConditions( ACT_SwitchCornerMode45, CHECK( cornerMode45Cond ) );
728 mgr->SetConditions( ACT_SwitchCornerMode90, CHECK( cornerMode90Cond ) );
729 mgr->SetConditions( ACT_SwitchCornerModeArc45, CHECK( cornerModeArc45Cond ) );
730 mgr->SetConditions( ACT_SwitchCornerModeArc90, CHECK( cornerModeArc90Cond ) );
731
732 auto diffPairCond =
733 [this]( const SELECTION& )
734 {
735 return m_router->Mode() == PNS::PNS_MODE_ROUTE_DIFF_PAIR;
736 };
737
738 menu.AddSeparator();
739
741 menu.AddMenu( m_diffPairMenu.get(), diffPairCond );
742
744
745 menu.AddSeparator();
746
747 frame->AddStandardSubMenus( *m_menu.get() );
748
749 return true;
750}
751
752
754{
755 if( aReason == RUN )
756 TOOL_BASE::Reset( aReason );
757}
758
759// Saves the complete event log and the dump of the PCB, allowing us to
760// recreate hard-to-find P&S quirks and bugs.
761
763{
764 wxString testCaseDir = ADVANCED_CFG::GetCfg().m_RouterTestCaseDirectory;
765 wxString logPath;
766 static size_t lastLoggerSize = 0;
767 static wxString mruPath;
768 PNS::LOGGER::LOG_DATA logData;
769
770 auto logger = m_router->Logger();
771
772 if( !logger || logger->GetEvents().size() == 0
773 || logger->GetEvents().size() == lastLoggerSize )
774 {
775 return;
776 }
777
778 if( !testCaseDir.IsEmpty() )
779 {
780 DIALOG_ROUTER_SAVE_TEST_CASE saveDlg( frame(), testCaseDir );
781 bool doExit = false;
782
783 if( saveDlg.ShowModal() == wxID_OK )
784 {
785 wxFileName path = wxFileName::DirName( testCaseDir );
786 path.AppendDir( saveDlg.getTestCaseName() );
787 logData.m_TestCaseType = saveDlg.getTestCaseType();
788
789 if( path.DirExists() )
790 {
791 doExit = !IsOK( frame(), wxString::Format( _("Test case in directory %s already exists. Overwrite?"), path.GetFullPath() ) );
792 }
793 else
794 {
795 path.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
796 }
797
798 path.SetName( wxT("pns") );
799 logPath = path.GetFullPath();
800 }
801 else
802 {
803 doExit = true;
804 }
805
806 if( doExit )
807 {
808 lastLoggerSize = logger->GetEvents().size(); // prevent re-entry
809 return;
810 }
811 }
812 else
813 {
814 if ( mruPath.IsEmpty() )
815 {
817 }
818
819 wxFileDialog dlg( frame(), _( "Save router log" ), mruPath, "pns.log",
820 "PNS log files" + AddFileExtListToFilter( { "log" } ),
821 wxFD_OVERWRITE_PROMPT | wxFD_SAVE );
822
824
825 if( dlg.ShowModal() != wxID_OK )
826 {
827 lastLoggerSize = logger->GetEvents().size(); // prevent re-entry
828 return;
829 }
830
831 logPath = dlg.GetPath();
832 }
833
834
835 wxFileName fname_log( logPath );
836 mruPath = fname_log.GetPath();
837 fname_log.SetExt( "log" );
838 wxLogTrace( wxT( "PNS" ), wxT( "save log to: %s" ), fname_log.GetFullPath() );
839
840 wxFileName fname_dump( fname_log );
841 fname_dump.SetExt( "dump" );
842
843 wxFileName fname_settings( fname_log );
844 fname_settings.SetExt( "settings" );
845
846 FILE* settings_f = wxFopen( fname_settings.GetAbsolutePath(), "wb" );
847
848 if( !settings_f )
849 {
850 DisplayError( frame(), wxString::Format( _( "Unable to write '%s'." ), fname_settings.GetAbsolutePath() ) );
851 return;
852 }
853
854 std::string settingsStr = m_router->Settings().FormatAsString();
855 fprintf( settings_f, "%s\n", settingsStr.c_str() );
856 fclose( settings_f );
857
858 // Export as *.kicad_pcb format, using a strategy which is specifically chosen
859 // as an example on how it could also be used to send it to the system clipboard.
860
861 PCB_IO_KICAD_SEXPR pcb_io;
862
863 pcb_io.SaveBoard( fname_dump.GetAbsolutePath(), *m_iface->GetBoard(), nullptr );
864
865 PROJECT* prj = m_iface->GetBoard()->GetProject();
866 prj->GetProjectFile().SaveAs( fname_dump.GetPath(), fname_dump.GetName() );
867 prj->GetLocalSettings().SaveAs( fname_dump.GetPath(), fname_dump.GetName() );
868
869 // Copy the project's custom DRC rules
871 {
872 wxString srcRules = editFrame->GetBoard()->GetDesignRulesPath();
873
874 if( !srcRules.IsEmpty() && wxFileName::FileExists( srcRules ) )
875 {
876 wxFileName fname_rules( fname_dump );
877 fname_rules.SetExt( FILEEXT::DesignRulesFileExtension );
878 wxCopyFile( srcRules, fname_rules.GetAbsolutePath() );
879 }
880 }
881
882 // Build log file:
883 std::vector<PNS::ITEM*> removed;
884 m_router->GetUpdatedItems( removed, logData.m_AddedItems, logData.m_Heads );
885
886
887 for( auto item : removed )
888 {
889 if( item->OfKind( PNS::ITEM::HOLE_T ) )
890 continue;
891
892 wxASSERT_MSG( item->Parent() != nullptr, "removed an item with no parent uuid?" );
893
894 if( item->Parent() )
895 logData.m_RemovedItems.insert( item->Parent()->m_Uuid );
896 }
897
898 logData.m_BoardHash = IO_UTILS::fileHashMMH3( fname_dump.GetAbsolutePath() );
899
900 if( !logData.m_BoardHash ) // should never happen...
901 return;
902
903 logData.m_Mode = m_router->Mode();
904 logData.m_Events = logger->GetEvents();
905
906 FILE* log_f = wxFopen( fname_log.GetAbsolutePath(), "wb" );
907 wxString logString = PNS::LOGGER::FormatLogFileAsJSON( logData );
908
909 if( !log_f )
910 {
911 DisplayError( frame(), wxString::Format( _( "Unable to write '%s'." ),
912 fname_log.GetAbsolutePath() ) );
913 return;
914 }
915
916 fprintf( log_f, "%s\n", logString.c_str().AsChar() );
917 fclose( log_f );
918
919 logger->Clear(); // prevent re-entry
920 lastLoggerSize = 0;
921}
922
923
925{
926 if( aEvent.Category() == TC_VIEW || aEvent.Category() == TC_MOUSE )
927 {
928 BOX2D viewAreaD = getView()->GetGAL()->GetVisibleWorldExtents();
929 m_router->SetVisibleViewArea( BOX2ISafe( viewAreaD ) );
930 }
931
932 if( !ADVANCED_CFG::GetCfg().m_EnableRouterDump )
933 return;
934
935 if( !aEvent.IsKeyPressed() )
936 return;
937
938 switch( aEvent.KeyCode() )
939 {
940 case '0':
942 aEvent.SetPassEvent( false );
943 break;
944
945 default:
946 break;
947 }
948}
949
951{
952 bool asChanged = false;
953
954 if( aEvent.IsAction( &ACT_SwitchCornerModeToNext ) )
955 {
956 DIRECTION_45::CORNER_MODE curr_mode = m_router->Settings().GetCornerMode();
957
959 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_45 );
960 else if( curr_mode == DIRECTION_45::CORNER_MODE::ROUNDED_45 )
961 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_90 );
962 else if( curr_mode == DIRECTION_45::CORNER_MODE::MITERED_90 )
963 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_90 );
964 else if( curr_mode == DIRECTION_45::CORNER_MODE::ROUNDED_90 )
965 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_45 );
966
967 asChanged = true;
968 }
969 else if( aEvent.IsAction( &ACT_SwitchCornerMode45 ) )
970 {
971 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_45 );
972 asChanged = true;
973 }
974 else if( aEvent.IsAction( &ACT_SwitchCornerModeArc45 ) )
975 {
976 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_45 );
977 asChanged = true;
978 }
979 else if( aEvent.IsAction( &ACT_SwitchCornerMode90 ) )
980 {
981 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_90 );
982 asChanged = true;
983 }
984 else if( aEvent.IsAction( &ACT_SwitchCornerModeArc90 ) )
985 {
986 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_90 );
987 asChanged = true;
988 }
989
990 if( asChanged )
991 {
993 updateEndItem( aEvent );
994 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
995 }
996
997 return 0;
998}
999
1000
1002{
1003 PCB_LAYER_ID tl = static_cast<PCB_LAYER_ID>( getView()->GetTopLayer() );
1004
1005 if( m_startItem )
1006 {
1007 int startLayer = m_iface->GetPNSLayerFromBoardLayer( tl );
1008 const PNS_LAYER_RANGE& ls = m_startItem->Layers();
1009
1010 if( ls.Overlaps( startLayer ) )
1011 return tl;
1012 else
1013 return m_iface->GetBoardLayerFromPNSLayer( ls.Start() );
1014 }
1015
1016 return tl;
1017}
1018
1019
1021{
1022 int activeLayer = m_iface->GetPNSLayerFromBoardLayer( frame()->GetActiveLayer() );
1023 int currentLayer = m_router->GetCurrentLayer();
1024
1025 if( currentLayer != activeLayer )
1026 m_router->SwitchLayer( activeLayer );
1027
1028 std::optional<int> newLayer = m_router->Sizes().PairedLayer( currentLayer );
1029
1030 if( !newLayer )
1031 newLayer = m_router->Sizes().GetLayerTop();
1032
1033 m_router->SwitchLayer( *newLayer );
1034 m_lastTargetLayer = m_iface->GetBoardLayerFromPNSLayer( *newLayer );
1035
1038}
1039
1040
1041// N.B. aTargetLayer is a PNS layer, not a PCB_LAYER_ID
1042void ROUTER_TOOL::updateSizesAfterRouterEvent( int aTargetLayer, const VECTOR2I& aPos )
1043{
1044 std::vector<PNS::NET_HANDLE> nets = m_router->GetCurrentNets();
1045
1046 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1048 std::shared_ptr<DRC_ENGINE>& drcEngine = bds.m_DRCEngine;
1049 DRC_CONSTRAINT constraint;
1050 PCB_LAYER_ID targetLayer = m_iface->GetBoardLayerFromPNSLayer( aTargetLayer );
1051
1052 PCB_TRACK dummyTrack( board() );
1053 dummyTrack.SetFlags( ROUTER_TRANSIENT );
1054 dummyTrack.SetLayer( targetLayer );
1055 dummyTrack.SetNet( nets.empty() ? nullptr: static_cast<NETINFO_ITEM*>( nets[0] ) );
1056 dummyTrack.SetStart( aPos );
1057 dummyTrack.SetEnd( dummyTrack.GetStart() );
1058
1059 constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, &dummyTrack, nullptr, targetLayer );
1060
1061 if( constraint.m_Value.Min() >= bds.m_MinClearance )
1062 {
1063 sizes.SetClearance( constraint.m_Value.Min() );
1064 sizes.SetClearanceSource( constraint.GetName() );
1065 }
1066 else
1067 {
1068 sizes.SetClearance( bds.m_MinClearance );
1069 sizes.SetClearanceSource( _( "board minimum clearance" ) );
1070 }
1071
1072 if( bds.UseNetClassTrack() || !sizes.TrackWidthIsExplicit() )
1073 {
1074 constraint = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, &dummyTrack, nullptr,
1075 targetLayer );
1076
1077 if( !constraint.IsNull() )
1078 {
1079 int width = sizes.TrackWidth();
1080
1081 // Only change the size if we're explicitly using the net class, or we're out of range
1082 // for our new constraints. Otherwise, just leave the track width alone so we don't
1083 // change for no reason.
1084 if( bds.UseNetClassTrack()
1085 || ( width < bds.m_TrackMinWidth )
1086 || ( width < constraint.m_Value.Min() )
1087 || ( width > constraint.m_Value.Max() ) )
1088 {
1089 sizes.SetTrackWidth( std::max( bds.m_TrackMinWidth, constraint.m_Value.Opt() ) );
1090 }
1091
1092 if( sizes.TrackWidth() == constraint.m_Value.Opt() )
1093 sizes.SetWidthSource( constraint.GetName() );
1094 else if( sizes.TrackWidth() == bds.m_TrackMinWidth )
1095 sizes.SetWidthSource( _( "board minimum track width" ) );
1096 else
1097 sizes.SetWidthSource( _( "existing track" ) );
1098 }
1099 }
1100
1101 if( nets.size() >= 2 && ( bds.UseNetClassDiffPair() || !sizes.TrackWidthIsExplicit() ) )
1102 {
1103 PCB_TRACK dummyTrackB( board() );
1104 dummyTrackB.SetFlags( ROUTER_TRANSIENT );
1105 dummyTrackB.SetLayer( targetLayer );
1106 dummyTrackB.SetNet( static_cast<NETINFO_ITEM*>( nets[1] ) );
1107 dummyTrackB.SetStart( aPos );
1108 dummyTrackB.SetEnd( dummyTrackB.GetStart() );
1109
1110 constraint = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, &dummyTrack, &dummyTrackB,
1111 targetLayer );
1112
1113 if( !constraint.IsNull() )
1114 {
1115 if( bds.UseNetClassDiffPair()
1116 || ( sizes.DiffPairWidth() < bds.m_TrackMinWidth )
1117 || ( sizes.DiffPairWidth() < constraint.m_Value.Min() )
1118 || ( sizes.DiffPairWidth() > constraint.m_Value.Max() ) )
1119 {
1120 sizes.SetDiffPairWidth( std::max( bds.m_TrackMinWidth, constraint.m_Value.Opt() ) );
1121 }
1122
1123 if( sizes.DiffPairWidth() == constraint.m_Value.Opt() )
1124 sizes.SetDiffPairWidthSource( constraint.GetName() );
1125 else
1126 sizes.SetDiffPairWidthSource( _( "board minimum track width" ) );
1127 }
1128
1129 constraint = drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, &dummyTrack, &dummyTrackB,
1130 targetLayer );
1131
1132 if( !constraint.IsNull() )
1133 {
1134 if( bds.UseNetClassDiffPair()
1135 || ( sizes.DiffPairGap() < bds.m_MinClearance )
1136 || ( sizes.DiffPairGap() < constraint.m_Value.Min() )
1137 || ( sizes.DiffPairGap() > constraint.m_Value.Max() ) )
1138 {
1139 sizes.SetDiffPairGap( std::max( bds.m_MinClearance, constraint.m_Value.Opt() ) );
1140 }
1141
1142 if( sizes.DiffPairGap() == constraint.m_Value.Opt() )
1143 sizes.SetDiffPairGapSource( constraint.GetName() );
1144 else
1145 sizes.SetDiffPairGapSource( _( "board minimum clearance" ) );
1146 }
1147 }
1148
1149 m_router->UpdateSizes( sizes );
1150}
1151
1152
1153static VIATYPE getViaTypeFromFlags( int aFlags )
1154{
1155 switch( aFlags & VIA_ACTION_FLAGS::VIA_MASK )
1156 {
1158 return VIATYPE::THROUGH;
1160 return VIATYPE::BLIND;
1162 return VIATYPE::BURIED;
1164 return VIATYPE::MICROVIA;
1165 default:
1166 wxASSERT_MSG( false, wxT( "Unhandled via type" ) );
1167 return VIATYPE::THROUGH;
1168 }
1169}
1170
1171
1173{
1174 handleLayerSwitch( aEvent, false );
1176
1177 return 0;
1178}
1179
1180
1182{
1183 if( !m_router->IsPlacingVia() )
1184 {
1185 return handleLayerSwitch( aEvent, true );
1186 }
1187 else
1188 {
1189 m_router->ToggleViaPlacement();
1190 frame()->SetActiveLayer(
1191 m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() ) );
1192 updateEndItem( aEvent );
1194 }
1195
1197 return 0;
1198}
1199
1200
1202{
1203 if( !IsToolActive() )
1204 return 0;
1205
1206 if( !m_router->RoutingInProgress() || !m_router->Placer() )
1207 return 0;
1208
1209 m_iface->SetBoard( board() );
1210
1212 const std::vector<VIA_STACK_PRESET>& presets = bds.m_ViaStackPresets;
1213
1214 if( presets.empty() )
1215 {
1216 frame()->GetInfoBar()->ShowMessageFor(
1217 _( "No microvia stack presets defined. Add one in Board Setup, Microvia Stacks." ), 3000,
1218 wxICON_INFORMATION );
1219 return 0;
1220 }
1221
1222 int idx = std::clamp( bds.GetViaStackIndex(), 0, (int) presets.size() - 1 );
1223 const VIA_STACK_PRESET& preset = presets[idx];
1224
1225 PCB_LAYER_ID currentLayer = m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() );
1226
1227 if( currentLayer == UNDEFINED_LAYER )
1228 return 0;
1229
1230 const LSET enabled = board()->GetEnabledLayers();
1231
1232 if( !enabled.Contains( preset.m_StartLayer ) || !enabled.Contains( preset.m_EndLayer ) )
1233 {
1234 frame()->GetInfoBar()->ShowMessageFor( _( "The microvia stack preset layers are not present on this board." ),
1235 3000, wxICON_ERROR );
1236 return 0;
1237 }
1238
1239 PCB_LAYER_ID targetLayer = ViaStackTargetLayer( preset.m_StartLayer, preset.m_EndLayer, currentLayer );
1240
1241 if( targetLayer == UNDEFINED_LAYER )
1242 {
1243 frame()->GetInfoBar()->ShowMessageFor(
1244 wxString::Format( _( "The microvia stack runs between %s and %s. Route on one of those layers "
1245 "to place it." ),
1246 board()->GetLayerName( preset.m_StartLayer ),
1247 board()->GetLayerName( preset.m_EndLayer ) ),
1248 3000, wxICON_ERROR );
1249 return 0;
1250 }
1251
1252 if( preset.m_Staggered )
1253 {
1254 if( m_router->IsPlacingVia() )
1255 m_router->ToggleViaPlacement();
1256
1257 // The router cannot route through a staggered stack (lateral walk + connecting traces).
1258 // Fix the track here and REMEMBER the stack, but build it only after routing tears down.
1259 // Committing to the board while the PNS world is live invalidates its nodes (crash).
1260 VECTOR2I head = m_endSnapPoint;
1261 const PNS::ITEM_SET traces = m_router->Placer()->Traces();
1262
1263 if( traces.Size() > 0 )
1264 {
1265 if( PNS::LINE* line = dynamic_cast<PNS::LINE*>( traces[0] ) )
1266 {
1267 if( line->PointCount() > 0 )
1268 head = line->CLine().CLastPoint();
1269 }
1270 }
1271
1272 if( !m_router->FixRoute( head, m_endItem, true, false ) )
1273 {
1274 frame()->GetInfoBar()->ShowMessageFor( _( "Could not end the track here for a microvia stack." ), 3000,
1275 wxICON_ERROR );
1277 return 0;
1278 }
1279
1280 m_pendingViaStack = true;
1281 m_pendingStackHead = head;
1282 m_pendingStackStart = currentLayer;
1283 m_pendingStackEnd = targetLayer;
1284
1286 return 0;
1287 }
1288
1289 // A via the route places with no net reports UNCONNECTED, not ORPHANED.
1290 int net = NETINFO_LIST::UNCONNECTED;
1291
1292 if( !m_router->GetCurrentNets().empty() )
1293 {
1294 if( NETINFO_ITEM* ni = static_cast<NETINFO_ITEM*>( m_router->GetCurrentNets()[0] ) )
1295 net = ni->GetNetCode();
1296 }
1297
1298 int viaSize;
1299 int viaDrill;
1300
1301 if( preset.m_UseNetclass )
1302 {
1303 NETINFO_ITEM* ni = board()->FindNet( net );
1304 NETCLASS* nc = ni ? ni->GetNetClass() : nullptr;
1305
1306 viaSize = ( nc && nc->HasuViaDiameter() ) ? nc->GetuViaDiameter() : bds.GetCurrentViaSize();
1307 viaDrill = ( nc && nc->HasuViaDrill() ) ? nc->GetuViaDrill() : bds.GetCurrentViaDrill();
1308
1309 // Route with the size the stack's hops will resolve to from the via rules
1310 PCB_VIA dummyVia( board() );
1311 dummyVia.SetFlags( ROUTER_TRANSIENT );
1312 dummyVia.SetViaType( VIATYPE::MICROVIA );
1313 dummyVia.SetLayerPair( currentLayer, targetLayer );
1314 dummyVia.SetNetCode( net );
1315 dummyVia.SetPosition( m_endSnapPoint );
1316 dummyVia.SetSizeFromRules( viaSize, viaDrill );
1317
1318 viaSize = dummyVia.GetWidth( PADSTACK::ALL_LAYERS );
1319 viaDrill = dummyVia.GetDrillValue();
1320 }
1321 else
1322 {
1323 viaSize = preset.m_ViaSize > 0 ? preset.m_ViaSize : bds.GetCurrentViaSize();
1324 viaDrill = preset.m_ViaDrill > 0 ? preset.m_ViaDrill : bds.GetCurrentViaDrill();
1325 }
1326
1327 // Routing to a non-adjacent layer leaves one multi-hop microvia that finishInteractive()
1328 // expands into a stack.
1329 if( m_pendingStackedExpansions.empty() )
1330 {
1332 }
1333
1334 m_pendingStackedExpansions.push_back( { currentLayer, targetLayer, net, preset, std::nullopt } );
1336
1337 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1338 sizes.ClearLayerPairs();
1339
1340 sizes.SetViaDiameter( viaSize );
1341 sizes.SetViaDrill( viaDrill );
1343 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( currentLayer ),
1344 m_iface->GetPNSLayerFromBoardLayer( targetLayer ) );
1345
1346 m_router->UpdateSizes( sizes );
1347
1348 if( !m_router->IsPlacingVia() )
1349 m_router->ToggleViaPlacement();
1350
1351 if( m_router->RoutingInProgress() )
1352 {
1353 updateEndItem( aEvent );
1355 }
1356
1358 return 0;
1359}
1360
1361
1363{
1364 if( !m_stackDropAwaitingVia || !m_router->IsPlacingVia() )
1365 return;
1366
1367 const PNS::ITEM_SET traces = m_router->Placer()->Traces();
1368
1369 if( traces.Size() == 0 )
1370 return;
1371
1372 // The via lands where the placer's trace ends, which is not where the cursor is once
1373 // the router has walked around or shoved.
1374 if( PNS::LINE* line = dynamic_cast<PNS::LINE*>( traces[0] ) )
1375 {
1376 if( line->EndsWithVia() )
1377 m_pendingStackedExpansions.back().m_Pos = line->Via().Pos();
1378 }
1379}
1380
1381
1383{
1384 m_pendingViaStack = false;
1385
1386 // Hand off to the interactive microvia stack placement, pre-anchored at the routed head, so
1387 // the user steers each staggered hop exactly like free-standing placement. Running this only
1388 // after the route has fully torn down keeps the board edit clear of the PNS world.
1389 if( DRAWING_TOOL* drawingTool = m_toolMgr->GetTool<DRAWING_TOOL>() )
1390 {
1391 drawingTool->SeedViaStackStart( m_pendingStackHead, m_pendingStackStart, m_pendingStackEnd );
1393 }
1394}
1395
1396
1397int ROUTER_TOOL::handleLayerSwitch( const TOOL_EVENT& aEvent, bool aForceVia )
1398{
1399 wxCHECK( m_router, 0 );
1400
1401 if( !IsToolActive() )
1402 return 0;
1403
1404 // Ensure PNS_KICAD_IFACE (m_iface) m_board member is up to date
1405 // For some reason, this is not always the case
1406 m_iface->SetBoard( board() );
1407
1408 // First see if this is one of the switch layer commands
1409 BOARD* brd = board();
1410 LSET enabledLayers = LSET::AllCuMask( brd->GetDesignSettings().GetCopperLayerCount() );
1411 LSEQ layers = enabledLayers.UIOrder();
1412
1413 // These layers are in Board Layer UI order not PNS layer order
1414 PCB_LAYER_ID currentLayer = m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() );
1415 PCB_LAYER_ID targetLayer = UNDEFINED_LAYER;
1416
1417 if( aEvent.IsAction( &PCB_ACTIONS::layerNext ) )
1418 {
1419 size_t idx = 0;
1420 size_t target_idx = 0;
1421
1422 for( size_t i = 0; i < layers.size(); i++ )
1423 {
1424 if( layers[i] == currentLayer )
1425 {
1426 idx = i;
1427 break;
1428 }
1429 }
1430
1431 target_idx = ( idx + 1 ) % layers.size();
1432 // issue: #14480
1433 // idx + 1 layer may be invisible, switches to next visible layer
1434 for( size_t i = 0; i < layers.size() - 1; i++ )
1435 {
1436 if( brd->IsLayerVisible( layers[target_idx] ) )
1437 {
1438 targetLayer = layers[target_idx];
1439 break;
1440 }
1441 target_idx += 1;
1442
1443 if( target_idx >= layers.size() )
1444 {
1445 target_idx = 0;
1446 }
1447 }
1448
1449 if( targetLayer == UNDEFINED_LAYER )
1450 {
1451 // if there is no visible layers
1452 return 0;
1453 }
1454 }
1455 else if( aEvent.IsAction( &PCB_ACTIONS::layerPrev ) )
1456 {
1457 size_t idx = 0;
1458 size_t target_idx = 0;
1459
1460 for( size_t i = 0; i < layers.size(); i++ )
1461 {
1462 if( layers[i] == currentLayer )
1463 {
1464 idx = i;
1465 break;
1466 }
1467 }
1468
1469 target_idx = ( idx > 0 ) ? ( idx - 1 ) : ( layers.size() - 1 );
1470
1471 for( size_t i = 0; i < layers.size() - 1; i++ )
1472 {
1473 if( brd->IsLayerVisible( layers[target_idx] ) )
1474 {
1475 targetLayer = layers[target_idx];
1476 break;
1477 }
1478
1479 if( target_idx > 0 )
1480 target_idx -= 1;
1481 else
1482 target_idx = layers.size() - 1;
1483 }
1484
1485 if( targetLayer == UNDEFINED_LAYER )
1486 {
1487 // if there is no visible layers
1488 return 0;
1489 }
1490 }
1491 else if( aEvent.IsAction( &PCB_ACTIONS::layerToggle ) )
1492 {
1493 PCB_SCREEN* screen = frame()->GetScreen();
1494
1495 if( currentLayer == screen->m_Route_Layer_TOP )
1496 targetLayer = screen->m_Route_Layer_BOTTOM;
1497 else
1498 targetLayer = screen->m_Route_Layer_TOP;
1499 }
1501 {
1502 targetLayer = aEvent.Parameter<PCB_LAYER_ID>();
1503
1504 if( !enabledLayers.test( targetLayer ) )
1505 return 0;
1506 }
1507
1508 if( targetLayer != UNDEFINED_LAYER )
1509 {
1510 if( targetLayer == currentLayer )
1511 return 0;
1512
1513 if( !aForceVia && m_router && m_router->SwitchLayer( m_iface->GetPNSLayerFromBoardLayer( targetLayer ) ) )
1514 {
1515 updateEndItem( aEvent );
1516 updateSizesAfterRouterEvent( m_iface->GetPNSLayerFromBoardLayer( targetLayer ), m_endSnapPoint );
1517 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
1518 return 0;
1519 }
1520 }
1521
1523
1524 PCB_LAYER_ID pairTop = frame()->GetScreen()->m_Route_Layer_TOP;
1525 PCB_LAYER_ID pairBottom = frame()->GetScreen()->m_Route_Layer_BOTTOM;
1526
1527 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1528
1529 VIATYPE viaType = VIATYPE::THROUGH;
1530 bool selectLayer = false;
1531
1532 // Otherwise it is one of the router-specific via commands
1533 if( targetLayer == UNDEFINED_LAYER )
1534 {
1535 const int actViaFlags = aEvent.Parameter<int>();
1536 selectLayer = actViaFlags & VIA_ACTION_FLAGS::SELECT_LAYER;
1537
1538 viaType = getViaTypeFromFlags( actViaFlags );
1539
1540 // ask the user for a target layer
1541 if( selectLayer )
1542 {
1543 // When the currentLayer is undefined, trying to place a via does not work
1544 // because it means there is no track in progress, and some other variables
1545 // values are not defined like m_endSnapPoint. So do not continue.
1546 if( currentLayer == UNDEFINED_LAYER )
1547 return 0;
1548
1549 wxPoint endPoint = ToWxPoint( view()->ToScreen( m_endSnapPoint ) );
1550 endPoint = frame()->GetCanvas()->ClientToScreen( endPoint );
1551
1552 // Build the list of not allowed layer for the target layer
1553 LSET not_allowed_ly = LSET::AllNonCuMask();
1554
1555 if( viaType != VIATYPE::THROUGH )
1556 not_allowed_ly.set( currentLayer );
1557
1558 targetLayer = frame()->SelectOneLayer( static_cast<PCB_LAYER_ID>( currentLayer ),
1559 not_allowed_ly, endPoint );
1560
1561 // Reset the cursor to the end of the track
1563
1564 if( targetLayer == UNDEFINED_LAYER ) // canceled by user
1565 return 0;
1566
1567 // One cannot place a blind/buried via on only one layer:
1568 if( viaType != VIATYPE::THROUGH )
1569 {
1570 if( currentLayer == targetLayer )
1571 return 0;
1572 }
1573 }
1574 }
1575
1576 // fixme: P&S supports more than one fixed layer pair. Update the dialog?
1577 sizes.ClearLayerPairs();
1578
1579 // Convert blind/buried via to a through hole one, if it goes through all layers
1580 if( viaType != VIATYPE::THROUGH
1581 && ( ( targetLayer == B_Cu && currentLayer == F_Cu )
1582 || ( targetLayer == F_Cu && currentLayer == B_Cu ) ) )
1583 {
1584 viaType = VIATYPE::THROUGH;
1585 }
1586
1587 if( targetLayer == UNDEFINED_LAYER )
1588 {
1589 // Implicit layer selection
1590 if( viaType == VIATYPE::THROUGH )
1591 {
1592 // Try to switch to the nearest ratnest item's layer if we have one
1593 VECTOR2I otherEnd;
1594 PNS_LAYER_RANGE otherEndLayers;
1595 PNS::ITEM* otherEndItem = nullptr;
1596
1597 if( !m_router->GetNearestRatnestAnchor( otherEnd, otherEndLayers, otherEndItem ) )
1598 {
1599 // use the default layer pair
1600 currentLayer = pairTop;
1601 targetLayer = pairBottom;
1602 }
1603 else
1604 {
1605 // use the layer of the other end, unless it is the same layer as the currently active layer, in which
1606 // case use the layer pair (if applicable)
1607 PCB_LAYER_ID otherEndLayerPcbId = m_iface->GetBoardLayerFromPNSLayer( otherEndLayers.Start() );
1608 const std::optional<int> pairedLayerPns = m_router->Sizes().PairedLayer( m_router->GetCurrentLayer() );
1609
1610 const PNS_LAYER_RANGE allCopperLayers( m_iface->GetPNSLayerFromBoardLayer( F_Cu ),
1611 m_iface->GetPNSLayerFromBoardLayer( B_Cu ) );
1612
1613 // A through anchor connects on every copper layer, so it names no single target.
1614 // Test the hole rather than the copper range, which segmented padstacks
1615 // (FRONT_INNER_BACK, custom) can report as a single layer.
1616 const bool otherEndIsThrough =
1617 otherEndLayers == allCopperLayers
1618 || ( otherEndItem && otherEndItem->HasHole()
1619 && otherEndItem->Hole()->Layers() == allCopperLayers );
1620
1621 if( otherEndIsThrough )
1622 {
1623 // Honour the user's layer pair; the anchor span start is always the top
1624 // copper layer and would ignore it. Constrained anchors fall through below.
1625 if( currentLayer == pairBottom )
1626 targetLayer = pairTop;
1627 else if( currentLayer == pairTop )
1628 targetLayer = pairBottom;
1629 else
1630 targetLayer = pairTop;
1631 }
1632 else if( currentLayer == otherEndLayerPcbId && pairedLayerPns.has_value() )
1633 {
1634 // Closest ratsnest layer is the same as the active layer - assume the via is being placed for
1635 // other routing reasons and switch the layer
1636 targetLayer = m_iface->GetBoardLayerFromPNSLayer( *pairedLayerPns );
1637 }
1638 else
1639 {
1640 targetLayer = m_iface->GetBoardLayerFromPNSLayer( otherEndLayers.Start() );
1641 }
1642 }
1643 }
1644 else
1645 {
1646 if( currentLayer == pairTop || currentLayer == pairBottom )
1647 {
1648 // the current layer is on the defined layer pair,
1649 // swap to the other side
1650 currentLayer = pairTop;
1651 targetLayer = pairBottom;
1652 }
1653 else
1654 {
1655 // the current layer is not part of the current layer pair,
1656 // so fallback and swap to the top layer of the pair by default
1657 targetLayer = pairTop;
1658 }
1659
1660 // Do not create a broken via (i.e. a via on only one copper layer)
1661 if( currentLayer == targetLayer )
1662 {
1663 WX_INFOBAR* infobar = frame()->GetInfoBar();
1664 infobar->ShowMessageFor( _( "Via needs 2 different layers." ), 5000, wxICON_ERROR,
1665 WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION );
1666 return 0;
1667 }
1668 }
1669 }
1670
1671 sizes.SetViaDiameter( bds.m_ViasMinSize );
1672 sizes.SetViaDrill( bds.m_MinThroughDrill );
1673
1674 if( bds.UseNetClassVia() || viaType == VIATYPE::MICROVIA )
1675 {
1676 PCB_VIA dummyVia( board() );
1677 dummyVia.SetViaType( viaType );
1678 dummyVia.SetLayerPair( currentLayer, targetLayer );
1679
1680 if( !m_router->GetCurrentNets().empty() )
1681 dummyVia.SetNet( static_cast<NETINFO_ITEM*>( m_router->GetCurrentNets()[0] ) );
1682
1683 DRC_CONSTRAINT constraint;
1684
1685 constraint = bds.m_DRCEngine->EvalRules( VIA_DIAMETER_CONSTRAINT, &dummyVia, nullptr,
1686 currentLayer );
1687
1688 if( !constraint.IsNull() )
1689 sizes.SetViaDiameter( constraint.m_Value.PinnedOpt() );
1690
1691 constraint = bds.m_DRCEngine->EvalRules( HOLE_SIZE_CONSTRAINT, &dummyVia, nullptr,
1692 currentLayer );
1693
1694 if( !constraint.IsNull() )
1695 sizes.SetViaDrill( constraint.m_Value.PinnedOpt() );
1696 }
1697 else
1698 {
1699 sizes.SetViaDiameter( bds.GetCurrentViaSize() );
1700 sizes.SetViaDrill( bds.GetCurrentViaDrill() );
1701 }
1702
1703 sizes.SetViaType( viaType );
1704 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( currentLayer ),
1705 m_iface->GetPNSLayerFromBoardLayer( targetLayer ) );
1706
1707 m_router->UpdateSizes( sizes );
1708
1709 if( !m_router->IsPlacingVia() )
1710 m_router->ToggleViaPlacement();
1711
1712 if( m_router->RoutingInProgress() )
1713 {
1714 updateEndItem( aEvent );
1716 }
1717 else
1718 {
1719 updateStartItem( aEvent );
1720 }
1721
1722 return 0;
1723}
1724
1725
1727{
1730
1731 // The stack handoff names the resume layer so snapping does not guess it from nearby copper.
1733 {
1734 pcbLayer = m_viaStackResumeLayer;
1736 }
1737
1738 int pnsLayer = m_iface->GetPNSLayerFromBoardLayer( pcbLayer );
1739
1740 if( !::IsCopperLayer( pcbLayer ) )
1741 {
1742 editFrame->ShowInfoBarError( _( "Tracks on Copper layers only." ) );
1743 return false;
1744 }
1745
1747 editFrame->SetActiveLayer( pcbLayer );
1748
1749 if( !getView()->IsLayerVisible( pcbLayer ) )
1750 {
1751 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1752 editFrame->GetCanvas()->Refresh();
1753 }
1754
1755 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
1756
1757 m_iface->SetStartLayerFromPCBNew( pcbLayer );
1758
1759 frame()->GetBoard()->GetDesignSettings().m_TempOverrideTrackWidth = false;
1760 m_iface->ImportSizes( sizes, m_startItem, nullptr, aStartPosition );
1761 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_TOP ),
1762 m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_BOTTOM ) );
1763
1764 m_router->UpdateSizes( sizes );
1765
1766 if( m_startItem && m_startItem->Net() )
1767 {
1769 {
1770 if( PNS::NET_HANDLE coupledNet = m_router->GetRuleResolver()->DpCoupledNet( m_startItem->Net() ) )
1771 highlightNets( true, { m_startItem->Net(), coupledNet } );
1772 }
1773 else
1774 {
1775 highlightNets( true, { m_startItem->Net() } );
1776 }
1777 }
1778
1779 controls()->SetAutoPan( true );
1780
1781 if( !m_router->StartRouting( m_startSnapPoint, m_startItem, pnsLayer ) )
1782 {
1783 // It would make more sense to leave the net highlighted as the higher-contrast mode
1784 // makes the router clearances more visible. However, since we just started routing
1785 // the conversion of the screen from low contrast to high contrast is a bit jarring and
1786 // makes the infobar coming up less noticeable.
1787 highlightNets( false );
1788
1789 frame()->ShowInfoBarError( m_router->FailureReason(), true,
1790 [&]()
1791 {
1792 m_router->ClearViewDecorations();
1793 } );
1794
1795 controls()->SetAutoPan( false );
1796 return false;
1797 }
1798
1799 m_endItem = nullptr;
1801
1803 frame()->UndoRedoBlock( true );
1804
1805 return true;
1806}
1807
1808
1810{
1811 m_router->StopRouting();
1812
1813 m_stackDropAwaitingVia = false;
1814
1815 if( !m_pendingStackedExpansions.empty() )
1816 {
1817 BOARD_COMMIT commit( frame() );
1818
1819 auto matcher = [&]( PCB_VIA* aVia ) -> const VIA_STACK_PRESET*
1820 {
1822 };
1823
1824 if( PCB_VIA_STACK::ExpandMultiHopMicrovias( board(), &commit, matcher ) > 0 )
1825 commit.Push( _( "Expand Microvia Stacks" ), APPEND_UNDO );
1826
1829 }
1830
1831 m_startItem = nullptr;
1832 m_endItem = nullptr;
1833
1834 frame()->SetActiveLayer( m_originalActiveLayer );
1836 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1837 controls()->SetAutoPan( false );
1838 controls()->ForceCursorPosition( false );
1839 frame()->UndoRedoBlock( false );
1840 highlightNets( false );
1841
1842 return true;
1843}
1844
1845
1847{
1848 m_router->ClearViewDecorations();
1849
1850 bool startWithVia = std::exchange( m_startWithVia, false );
1851
1852 if( !prepareInteractive( aStartPosition ) )
1853 return;
1854
1855 auto setCursor =
1856 [&]()
1857 {
1858 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1859 };
1860
1861 auto syncRouterAndFrameLayer =
1862 [&]()
1863 {
1864 int pnsLayer = m_router->GetCurrentLayer();
1865 PCB_LAYER_ID pcbLayer = m_iface->GetBoardLayerFromPNSLayer( pnsLayer );
1867
1868 editFrame->SetActiveLayer( pcbLayer );
1869
1870 if( !getView()->IsLayerVisible( pcbLayer ) )
1871 {
1872 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1873 editFrame->GetCanvas()->Refresh();
1874 }
1875 };
1876
1877 // Set initial cursor
1878 setCursor();
1879
1880 // A via or through pad already reaching the layer 'V' switched to must not gain a second via
1881 int viaTargetLayer = m_iface->GetPNSLayerFromBoardLayer( m_originalActiveLayer );
1882 bool startReachesViaTarget = m_startItem && m_startItem->Layers().Overlaps( viaTargetLayer );
1883
1884 // If the user pressed 'V' before starting to route, enable via placement now
1885 if( startWithVia && !startReachesViaTarget )
1886 {
1887 handleLayerSwitch( ACT_PlaceThroughVia.MakeEvent(), true );
1888 }
1889
1890 // Throttle wxEVT_UPDATE_UI during routing. The idle sweep fires between every Wait()
1891 // iteration and its cost dominates at interactive frame rates.
1892 UI_UPDATE_INTERVAL_GUARD uiGuard( 200 );
1893
1894 while( TOOL_EVENT* evt = Wait() )
1895 {
1896 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Catch any visibility
1897 // changes while routing.
1898 m_toolMgr->GetView()->SyncLayerVisibilityCache();
1899
1900 setCursor();
1901
1902 // Don't crash if we missed an operation that canceled routing.
1903 if( !m_router->RoutingInProgress() )
1904 {
1905 if( evt->IsCancelInteractive() )
1906 m_cancelled = true;
1907
1908 break;
1909 }
1910
1911 handleCommonEvents( *evt );
1912
1913 if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
1914 {
1915 updateEndItem( *evt );
1917 }
1918 else if( evt->IsAction( &PCB_ACTIONS::routerUndoLastSegment )
1919 || evt->IsAction( &ACTIONS::doDelete )
1920 || evt->IsAction( &ACTIONS::undo ) )
1921 {
1922 if( std::optional<VECTOR2I> last = m_router->UndoLastSegment() )
1923 {
1924 getViewControls()->WarpMouseCursor( last.value(), true );
1925 evt->SetMousePosition( last.value() );
1926 }
1927
1928 updateEndItem( *evt );
1930 }
1931 else if( evt->IsAction( &PCB_ACTIONS::routerAttemptFinish ) )
1932 {
1933 if( m_toolMgr->IsContextMenuActive() )
1934 m_toolMgr->WarpAfterContextMenu();
1935
1936 bool* autoRouted = evt->Parameter<bool*>();
1937
1938 if( m_router->Finish() )
1939 {
1940 // When we're routing a group of signals automatically we want
1941 // to break up the undo stack every time we have to manually route
1942 // so the user gets nice checkpoints. Remove the APPEND_UNDO flag.
1943 if( autoRouted != nullptr )
1944 *autoRouted = true;
1945
1946 break;
1947 }
1948 else
1949 {
1950 // This acts as check if we were called by the autorouter; we don't want
1951 // to reset APPEND_UNDO if we're auto finishing after route-other-end
1952 if( autoRouted != nullptr )
1953 {
1954 *autoRouted = false;
1955 m_iface->SetCommitFlags( 0 );
1956 }
1957
1958 // Warp the mouse so the user is at the point we managed to route to
1959 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1960 }
1961 }
1962 else if( evt->IsAction( &PCB_ACTIONS::routerContinueFromEnd ) )
1963 {
1964 bool needsAppend = m_router->Placer()->HasPlacedAnything();
1965
1966 if( m_router->ContinueFromEnd( &m_startItem ) )
1967 {
1968 syncRouterAndFrameLayer();
1969 m_startSnapPoint = m_router->Placer()->CurrentStart();
1970 updateEndItem( *evt );
1971
1972 // Warp the mouse to wherever we actually ended up routing to
1973 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1974
1975 // We want the next router commit to be one undo at the UI layer
1976 m_iface->SetCommitFlags( needsAppend ? APPEND_UNDO : 0 );
1977 }
1978 else
1979 {
1980 frame()->ShowInfoBarError( m_router->FailureReason(), true );
1981 }
1982 }
1983 else if( evt->IsClick( BUT_LEFT )
1984 || evt->IsAction( &ACTIONS::cursorClick )
1985 || evt->IsDrag( BUT_LEFT )
1986 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack ) )
1987 {
1988 updateEndItem( *evt );
1989 bool needLayerSwitch = m_router->IsPlacingVia();
1990 bool forceCommit = false;
1991
1993
1994 if( m_router->FixRoute( m_endSnapPoint, m_endItem, false, forceCommit ) )
1995 break;
1996
1997 if( !m_router->IsPlacingVia() )
1998 m_stackDropAwaitingVia = false;
1999
2000 if( needLayerSwitch )
2001 {
2003 }
2004 else
2005 {
2007 }
2008
2009 // Synchronize the indicated layer
2010 syncRouterAndFrameLayer();
2011
2012 updateEndItem( *evt );
2014 m_startItem = nullptr;
2015 }
2016 else if( evt->IsAction( &ACT_PlaceViaStack ) )
2017 {
2018 onViaStackCommand( *evt );
2019
2020 // A staggered drop fixes the track and queues the stack, break so the normal
2021 // teardown runs (restores the cursor) and the generator is committed after the
2022 // PNS world is gone.
2023 if( m_pendingViaStack || !m_router->RoutingInProgress() )
2024 break;
2025
2026 updateEndItem( *evt );
2028 }
2029 else if( evt->IsAction( &ACT_SwitchPosture ) )
2030 {
2031 m_router->FlipPosture();
2032 updateEndItem( *evt );
2033 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
2034 }
2035 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
2036 {
2037 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2038 controls()->SetAutoPan( false );
2039 {
2040 m_toolMgr->RunAction( ACT_CustomTrackWidth );
2041 }
2042 controls()->SetAutoPan( true );
2043 setCursor();
2045 }
2046 else if( evt->IsAction( &ACTIONS::finishInteractive )
2047 || evt->IsDblClick( BUT_LEFT )
2048 || evt->IsAction( &ACTIONS::cursorDblClick ) )
2049 {
2050 // Stop current routing:
2051 bool forceFinish = true;
2052 bool forceCommit = false;
2053
2054 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
2055 break;
2056 }
2057 else if( evt->IsCancelInteractive() || evt->IsAction( &PCB_ACTIONS::cancelCurrentItem )
2058 || evt->IsActivate()
2059 || evt->IsAction( &PCB_ACTIONS::routerInlineDrag ) )
2060 {
2061 if( evt->IsCancelInteractive() && ( m_inRouteSelected || !m_router->RoutingInProgress() ) )
2062 m_cancelled = true;
2063
2064 if( evt->IsActivate() && !evt->IsMoveTool() )
2065 m_cancelled = true;
2066
2067 m_router->AbortPlacement();
2068
2069 break;
2070 }
2071 else if( evt->IsUndoRedo() )
2072 {
2073 // We're in an UndoRedoBlock. If we get here, something's broken.
2074 wxFAIL;
2075 break;
2076 }
2077 else if( evt->IsClick( BUT_RIGHT ) )
2078 {
2079 m_menu->ShowContextMenu( selection() );
2080 }
2081 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2082 // but we don't at present have that, so we just knock out some of the egregious ones.
2083 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2084 {
2085 wxBell();
2086 }
2087 else
2088 {
2089 evt->SetPassEvent();
2090 }
2091 }
2092
2093 m_router->CommitRouting();
2094 // Reset to normal for next route
2095 m_iface->SetCommitFlags( 0 );
2096
2098
2099 // The PNS world is now torn down, so it is safe to add a queued staggered stack to the board.
2100 if( m_pendingViaStack )
2102}
2103
2104
2106{
2107 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
2108 DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( frame(), sizes );
2109
2110 if( settingsDlg.ShowModal() == wxID_OK )
2111 {
2112 m_router->UpdateSizes( sizes );
2113 m_savedSizes = sizes;
2114
2115 BOARD_DESIGN_SETTINGS& bds = frame()->GetBoard()->GetDesignSettings();
2117 bds.SetCustomDiffPairGap( sizes.DiffPairGap() );
2119 }
2120
2121 return 0;
2122}
2123
2124
2126{
2127 DIALOG_PNS_SETTINGS settingsDlg( frame(), m_router->Settings() );
2128
2129 settingsDlg.ShowModal();
2130
2132
2133 return 0;
2134}
2135
2136
2138{
2139 PNS::PNS_MODE mode = aEvent.Parameter<PNS::PNS_MODE>();
2140 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
2141
2142 settings.SetMode( mode );
2144
2145 return 0;
2146}
2147
2148
2150{
2151 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
2152 PNS::PNS_MODE mode = settings.Mode();
2153
2154 switch( mode )
2155 {
2156 case PNS::RM_MarkObstacles: mode = PNS::RM_Shove; break;
2157 case PNS::RM_Shove: mode = PNS::RM_Walkaround; break;
2158 case PNS::RM_Walkaround: mode = PNS::RM_MarkObstacles; break;
2159 }
2160
2161 settings.SetMode( mode );
2163
2164 return 0;
2165}
2166
2167
2169{
2170 return m_router->Settings().Mode();
2171}
2172
2173
2175{
2176 return m_router->RoutingInProgress();
2177}
2178
2179
2181{
2182 if( !m_startItem )
2183 return;
2184
2185 // Never split a via stack's connecting trace. The stack manages it as a unit, and a
2186 // split fragment would drop out of the group. Other generators still allow splitting.
2187 if( BOARD_ITEM* parent = m_startItem->Parent() )
2188 {
2189 if( PCB_GENERATOR* generator = dynamic_cast<PCB_GENERATOR*>( parent->GetParentGroup() ) )
2190 {
2191 if( generator->GetGeneratorType() == PCB_VIA_STACK::GENERATOR_TYPE )
2192 return;
2193 }
2194 }
2195
2197 m_router->BreakSegmentOrArc( m_startItem, m_startSnapPoint );
2198}
2199
2200
2202{
2206 PCB_LAYER_ID originalLayer = frame->GetActiveLayer();
2207 bool autoRoute = aEvent.Matches( PCB_ACTIONS::routerAutorouteSelected.MakeEvent() );
2208 bool otherEnd = aEvent.Matches( PCB_ACTIONS::routerRouteSelectedFromEnd.MakeEvent() );
2209
2210 if( m_router->RoutingInProgress() )
2211 return 0;
2212
2213 // Save selection then clear it for interactive routing
2214 PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2215
2216 if( selection.Size() == 0 )
2217 return 0;
2218
2219 m_toolMgr->RunAction( ACTIONS::selectionClear );
2220
2221 SCOPED_TOOL_PUSHER raii( frame, aEvent );
2222
2223 auto setCursor =
2224 [&]()
2225 {
2226 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2227 };
2228
2229 Activate();
2230 m_inRouteSelected = true;
2231
2232 // Must be done after Activate() so that it gets set into the correct context
2233 controls->ShowCursor( true );
2234 controls->ForceCursorPosition( false );
2235 // Set initial cursor
2236 setCursor();
2237
2238 // Get all connected board items, adding pads for any footprints selected
2239 std::vector<BOARD_CONNECTED_ITEM*> itemList;
2240
2241 for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() )
2242 {
2243 if( item->Type() == PCB_FOOTPRINT_T )
2244 {
2245 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2246 itemList.push_back( pad );
2247 }
2248 else if( dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) != nullptr )
2249 {
2250 itemList.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
2251 }
2252 }
2253
2254 std::shared_ptr<CONNECTIVITY_DATA> connectivity = frame->GetBoard()->GetConnectivity();
2255
2256 // For putting sequential tracks that successfully autoroute into one undo commit
2257 bool groupStart = true;
2258 m_cancelled = false;
2259
2260 for( BOARD_CONNECTED_ITEM* item : itemList )
2261 {
2262 // This code is similar to GetRatsnestForPad() but it only adds the anchor for
2263 // the side of the connectivity on this pad. It also checks for ratsnest points
2264 // inside the pad (like a trace end) and counts them.
2265 RN_NET* net = connectivity->GetRatsnestForNet( item->GetNetCode() );
2266
2267 if( !net )
2268 continue;
2269
2270 std::vector<std::shared_ptr<const CN_ANCHOR>> anchors;
2271
2272 for( const CN_EDGE& edge : net->GetEdges() )
2273 {
2274 std::shared_ptr<const CN_ANCHOR> target = edge.GetTargetNode();
2275 std::shared_ptr<const CN_ANCHOR> source = edge.GetSourceNode();
2276
2277 if( !source || source->Dirty() || !target || target->Dirty() )
2278 continue;
2279
2280 if( source->Parent() == item )
2281 anchors.push_back( source );
2282 else if( target->Parent() == item )
2283 anchors.push_back( target );
2284 }
2285
2286 // Route them
2287 for( std::shared_ptr<const CN_ANCHOR> anchor : anchors )
2288 {
2289 if( !anchor->Valid() )
2290 continue;
2291
2292 // Try to return to the original layer as indicating the user's preferred
2293 // layer for autorouting tracks. The layer can be changed by the user to
2294 // finish tracks that can't complete automatically, but should be changed
2295 // back after.
2296 if( frame->GetActiveLayer() != originalLayer )
2297 frame->SetActiveLayer( originalLayer );
2298
2299 m_startItem = m_router->GetWorld()->FindItemByParent( anchor->Parent() );
2300 m_startSnapPoint = anchor->Pos();
2301 m_router->SetMode( mode );
2302
2303 // Prime the interactive routing to attempt finish if we are autorouting
2304 bool autoRouted = false;
2305
2306 if( autoRoute )
2307 m_toolMgr->PostAction( PCB_ACTIONS::routerAttemptFinish, &autoRouted );
2308 else if( otherEnd )
2310
2311 // We want autorouted tracks to all be in one undo group except for
2312 // any tracks that need to be manually finished.
2313 // The undo appending for manually finished tracks is handled in peformRouting()
2314 if( groupStart )
2315 groupStart = false;
2316 else
2317 m_iface->SetCommitFlags( APPEND_UNDO );
2318
2319 // Start interactive routing. Will automatically finish if possible.
2321
2322 if( m_cancelled )
2323 break;
2324
2325 // Route didn't complete automatically, need to a new undo commit
2326 // for the next line so those can group as far as they autoroute
2327 if( !autoRouted )
2328 groupStart = true;
2329 }
2330
2331 if( m_cancelled )
2332 break;
2333 }
2334
2335 m_iface->SetCommitFlags( 0 );
2336 m_inRouteSelected = false;
2337 return 0;
2338}
2339
2340
2342{
2344 const PCB_SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2345
2346 if( selection.Size() == 0 )
2347 return 0;
2348
2349 std::vector<BOARD_CONNECTED_ITEM*> trackItems;
2350
2351 for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() )
2352 {
2353 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
2354 trackItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
2355 }
2356
2357 if( trackItems.empty() )
2358 return 0;
2359
2360 m_toolMgr->RunAction( ACTIONS::selectionClear );
2361 Activate();
2362
2363 PNS::NODE* world = m_router->GetWorld();
2364
2365 // Differential pairs can't be optimized as individual lines
2366 // TODO once we have a differential pair line primitive, we could handle them...
2367 int dpSkipped = std::erase_if( trackItems,
2368 [&]( BOARD_CONNECTED_ITEM* aItem )
2369 {
2370 PNS::RULE_RESOLVER* rr = world->GetRuleResolver();
2371 return rr && rr->DpCoupledNet( aItem->GetNet() );
2372 } );
2373
2374 if( dpSkipped > 0 )
2375 frame->ShowInfoBarMsg( _( "Differential pair members cannot be optimized." ) );
2376
2377 bool groupStart = true;
2378
2379 for( BOARD_CONNECTED_ITEM* trackItem : trackItems )
2380 {
2381 PNS::ITEM* pnsItem = world->FindItemByParent( trackItem );
2382
2383 if( !pnsItem || !pnsItem->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
2384 continue;
2385
2386 PNS::LINKED_ITEM* linkedItem = static_cast<PNS::LINKED_ITEM*>( pnsItem );
2387
2388 PNS::LINE originalLine = world->AssembleLine( linkedItem );
2389
2390 // TODO: could allow these once we have arc-aware drag/optimize
2391 if( originalLine.ArcCount() > 0 )
2392 continue;
2393
2394 PNS::NODE* branch = world->Branch();
2395 branch->Remove( originalLine );
2396
2397 PNS::LINE optimizedLine( originalLine );
2398 optimizedLine.ClearLinks();
2399
2405
2406 if( m_router->Settings().GetRestrictAngles() )
2408
2409 bool optimized = PNS::OPTIMIZER::Optimize( &optimizedLine, effort, branch );
2410
2411 if( !optimized || optimizedLine.CompareGeometry( originalLine ) )
2412 {
2413 delete branch;
2414 continue;
2415 }
2416
2417 if( branch->CheckColliding( &optimizedLine ) )
2418 {
2419 delete branch;
2420 continue;
2421 }
2422
2423 if( groupStart )
2424 groupStart = false;
2425 else
2426 m_iface->SetCommitFlags( APPEND_UNDO );
2427
2428 branch->Add( optimizedLine );
2429 m_router->CommitRouting( branch );
2430 }
2431
2432 m_iface->SetCommitFlags( 0 );
2433
2434 return 0;
2435}
2436
2437
2439{
2440 if( m_inRouterTool )
2441 return 0;
2442
2444
2448
2449 if( m_router->RoutingInProgress() )
2450 {
2451 if( m_router->Mode() == mode )
2452 return 0;
2453 else
2454 m_router->StopRouting();
2455 }
2456
2457 // Deselect all items
2458 m_toolMgr->RunAction( ACTIONS::selectionClear );
2459
2460 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
2461 SCOPED_TOOL_PUSHER raii( frame, originalEvent );
2462
2463 auto setCursor =
2464 [&]()
2465 {
2466 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2467 };
2468
2469 Activate();
2470 // Must be done after Activate() so that it gets set into the correct context
2471 controls->ShowCursor( true );
2472 controls->ForceCursorPosition( false );
2473 // Set initial cursor
2474 setCursor();
2475
2476 m_router->SetMode( mode );
2477 m_cancelled = false;
2478 m_startWithVia = false;
2479
2480 if( aEvent.HasPosition() )
2481 m_toolMgr->PrimeTool( aEvent.Position() );
2482
2483 // Main loop: keep receiving events
2484 while( TOOL_EVENT* evt = Wait() )
2485 {
2486 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Catch any visibility
2487 // changes while routing.
2488 m_toolMgr->GetView()->SyncLayerVisibilityCache();
2489
2490 if( !evt->IsDrag() )
2491 setCursor();
2492
2493 if( evt->IsCancelInteractive() )
2494 {
2495 break;
2496 }
2497 else if( evt->IsActivate() )
2498 {
2499 if( evt->IsMoveTool() || evt->IsEditorTool() )
2500 {
2501 // Make sure we come back after the move tool runs
2502 frame->PushTool( originalEvent );
2503 }
2504
2505 break;
2506 }
2507 else if( evt->Action() == TA_UNDO_REDO_PRE )
2508 {
2509 m_router->ClearWorld();
2510 }
2511 else if( evt->Action() == TA_UNDO_REDO_POST || evt->Action() == TA_MODEL_CHANGE )
2512 {
2513 m_router->SyncWorld();
2514 }
2515 else if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
2516 {
2517 updateStartItem( *evt );
2518 }
2519 else if( evt->IsAction( &PCB_ACTIONS::dragFreeAngle ) )
2520 {
2521 updateStartItem( *evt, true );
2523 }
2524 else if( evt->IsAction( &PCB_ACTIONS::drag45Degree ) )
2525 {
2526 updateStartItem( *evt, true );
2528 }
2529 else if( evt->IsAction( &PCB_ACTIONS::breakTrack ) )
2530 {
2531 updateStartItem( *evt, true );
2532 breakTrack( );
2533 evt->SetPassEvent( false );
2534 }
2535 else if( evt->IsClick( BUT_LEFT )
2536 || evt->IsAction( &ACTIONS::cursorClick )
2537 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack )
2538 || evt->IsAction( &PCB_ACTIONS::routeDiffPair ) )
2539 {
2540 updateStartItem( *evt );
2541
2542 if( evt->HasPosition() )
2543 performRouting( evt->Position() );
2544 }
2545 else if( evt->IsAction( &ACT_PlaceThroughVia ) )
2546 {
2547 m_startWithVia = true;
2548 m_toolMgr->RunAction( PCB_ACTIONS::layerToggle );
2549 }
2550 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2551 {
2552 m_router->SwitchLayer( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ) );
2553 updateStartItem( *evt );
2554 updateSizesAfterRouterEvent( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ), m_startSnapPoint );
2555 }
2556 else if( evt->IsKeyPressed() )
2557 {
2558 // wxWidgets fails to correctly translate shifted keycodes on the wxEVT_CHAR_HOOK
2559 // event so we need to process the wxEVT_CHAR event that will follow as long as we
2560 // pass the event.
2561 evt->SetPassEvent();
2562 }
2563 else if( evt->IsClick( BUT_RIGHT ) )
2564 {
2565 m_menu->ShowContextMenu( selection() );
2566 }
2567 else
2568 {
2569 evt->SetPassEvent();
2570 }
2571
2572 if( m_cancelled )
2573 break;
2574 }
2575
2576 // Store routing settings till the next invocation
2577 m_savedSizes = m_router->Sizes();
2578 m_router->ClearViewDecorations();
2579
2580 return 0;
2581}
2582
2583
2585{
2586 m_router->ClearViewDecorations();
2587
2588 view()->ClearPreview();
2589 view()->InitPreview();
2590
2592
2593 if( m_startItem && m_startItem->IsLocked() )
2594 {
2595 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
2596 wxOK | wxCANCEL | wxICON_WARNING );
2597 dlg.SetOKLabel( _( "Drag Anyway" ) );
2598 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
2599
2600 if( dlg.ShowModal() == wxID_CANCEL )
2601 return;
2602 }
2603
2604 bool dragStarted = m_router->StartDragging( m_startSnapPoint, m_startItem, aMode );
2605
2606 if( !dragStarted )
2607 {
2608 if( !m_router->FailureReason().IsEmpty() )
2609 frame()->ShowInfoBarError( m_router->FailureReason(), true );
2610
2611 return;
2612 }
2613
2614 if( m_startItem && m_startItem->Net() )
2615 highlightNets( true, { m_startItem->Net() } );
2616
2617 ctls->SetAutoPan( true );
2618 m_gridHelper->SetAuxAxes( true, m_startSnapPoint );
2619 frame()->UndoRedoBlock( true );
2620
2621 UI_UPDATE_INTERVAL_GUARD uiGuard( 200 );
2622
2623 while( TOOL_EVENT* evt = Wait() )
2624 {
2625 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Catch any visibility
2626 // changes while dragging.
2627 m_toolMgr->GetView()->SyncLayerVisibilityCache();
2628
2629 ctls->ForceCursorPosition( false );
2630
2631 if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
2632 {
2633 updateEndItem( *evt );
2635
2636 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
2637 {
2638 bool dragStatus;
2639
2640 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
2641 {
2642 view()->ClearPreview();
2643
2644 if( !dragStatus )
2645 {
2646 wxString hint;
2647 hint.Printf( _( "(%s to commit anyway.)" ),
2649
2651 statusItem->SetMessage( _( "Track violates DRC." ) );
2652 statusItem->SetHint( hint );
2653 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
2654 view()->AddToPreview( statusItem );
2655 }
2656 }
2657 }
2658 }
2659 else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorClick ) )
2660 {
2661 bool forceFinish = false;
2662 bool forceCommit = evt->Modifier( MD_CTRL );
2663
2664 if( m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit ) )
2665 break;
2666 }
2667 else if( evt->IsClick( BUT_RIGHT ) )
2668 {
2669 m_menu->ShowContextMenu( selection() );
2670 }
2671 else if( evt->IsCancelInteractive() || evt->IsAction( &PCB_ACTIONS::cancelCurrentItem )
2672 || evt->IsActivate() )
2673 {
2674 if( evt->IsCancelInteractive() && !m_startItem )
2675 m_cancelled = true;
2676
2677 if( evt->IsActivate() && !evt->IsMoveTool() )
2678 m_cancelled = true;
2679
2680 break;
2681 }
2682 else if( evt->IsUndoRedo() )
2683 {
2684 // We're in an UndoRedoBlock. If we get here, something's broken.
2685 wxFAIL;
2686 break;
2687 }
2688 else if( evt->Category() == TC_COMMAND )
2689 {
2690 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2691 // but we don't at present have that, so we just knock out some of the egregious ones.
2692 if( evt->IsAction( &ACTIONS::cut )
2693 || evt->IsAction( &ACTIONS::copy )
2694 || evt->IsAction( &ACTIONS::paste )
2695 || evt->IsAction( &ACTIONS::pasteSpecial )
2697 {
2698 wxBell();
2699 }
2700 // treat an undo as an escape
2701 else if( evt->IsAction( &ACTIONS::undo ) )
2702 {
2703 if( m_startItem )
2704 break;
2705 else
2706 wxBell();
2707 }
2708 else
2709 {
2710 evt->SetPassEvent();
2711 }
2712 }
2713 else
2714 {
2715 evt->SetPassEvent();
2716 }
2717
2718 handleCommonEvents( *evt );
2719 }
2720
2721 view()->ClearPreview();
2722 view()->ShowPreview( false );
2723
2724 if( m_router->RoutingInProgress() )
2725 m_router->StopRouting();
2726
2727 m_startItem = nullptr;
2728
2729 m_gridHelper->SetAuxAxes( false );
2730 frame()->UndoRedoBlock( false );
2731 ctls->SetAutoPan( false );
2732 ctls->ForceCursorPosition( false );
2733 highlightNets( false );
2734}
2735
2736
2738 PCB_SELECTION_TOOL* aSelTool )
2739{
2740 /*
2741 * If the collection contains a trivial line corner (two connected segments)
2742 * or a non-fanout-via (a via with no more than two connected segments), then
2743 * trim the collection down to a single item (which one won't matter since
2744 * they're all connected).
2745 */
2746
2747 // First make sure we've got something that *might* match.
2748 int vias = aCollector.CountType( PCB_VIA_T );
2749 int traces = aCollector.CountType( PCB_TRACE_T );
2750 int arcs = aCollector.CountType( PCB_ARC_T );
2751
2752 // We eliminate arcs because they are not supported in the inline drag code.
2753 if( arcs > 0 )
2754 return;
2755
2756 // We need to have at least 1 via or track
2757 if( vias + traces == 0 )
2758 return;
2759
2760 // We cannot drag more than one via at a time
2761 if( vias > 1 )
2762 return;
2763
2764 // We cannot drag more than two track segments at a time
2765 if( traces > 2 )
2766 return;
2767
2768 // Fetch first PCB_TRACK (via or trace) as our reference
2769 PCB_TRACK* reference = nullptr;
2770
2771 for( int i = 0; !reference && i < aCollector.GetCount(); i++ )
2772 reference = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2773
2774 // This should never happen, but just in case...
2775 if( !reference )
2776 return;
2777
2778 int refNet = reference->GetNetCode();
2779
2780 VECTOR2I refPoint( aPt.x, aPt.y );
2781 EDA_ITEM_FLAGS flags = reference->IsPointOnEnds( refPoint, -1 );
2782
2783 if( flags & STARTPOINT )
2784 refPoint = reference->GetStart();
2785 else if( flags & ENDPOINT )
2786 refPoint = reference->GetEnd();
2787
2788 // Check all items to ensure that any TRACKs are co-terminus with the reference and on
2789 // the same net.
2790 for( int i = 0; i < aCollector.GetCount(); i++ )
2791 {
2792 PCB_TRACK* neighbor = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2793
2794 if( neighbor && neighbor != reference )
2795 {
2796 if( neighbor->GetNetCode() != refNet )
2797 return;
2798
2799 if( neighbor->GetStart() != refPoint && neighbor->GetEnd() != refPoint )
2800 return;
2801 }
2802 }
2803
2804 // Selection meets criteria; trim it to the reference item.
2805 aCollector.Empty();
2806 aCollector.Append( reference );
2807}
2808
2809
2810bool ROUTER_TOOL::CanInlineDrag( int aDragMode )
2811{
2813 const PCB_SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2814
2815 if( selection.Size() == 1 )
2816 {
2817 return selection.Front()->IsType( GENERAL_COLLECTOR::DraggableItems );
2818 }
2819 else if( selection.CountType( PCB_FOOTPRINT_T ) == (size_t) selection.Size() )
2820 {
2821 // Footprints cannot be dragged freely.
2822 return !( aDragMode & PNS::DM_FREE_ANGLE );
2823 }
2824 else if( selection.CountType( PCB_TRACE_T ) == (size_t) selection.Size() )
2825 {
2826 return true;
2827 }
2828
2829 return false;
2830}
2831
2832
2833void ROUTER_TOOL::restoreSelection( const PCB_SELECTION& aOriginalSelection )
2834{
2835 EDA_ITEMS selItems;
2836 std::copy( aOriginalSelection.Items().begin(), aOriginalSelection.Items().end(), std::back_inserter( selItems ) );
2837 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &selItems );
2838}
2839
2840
2842{
2843 const PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2844
2845 if( selection.Empty() )
2847
2848 if( selection.Empty() || !selection.Front()->IsBOARD_ITEM() )
2849 return 0;
2850
2851 // selection gets cleared in the next action, we need a copy of the selected items.
2852 std::deque<EDA_ITEM*> selectedItems = selection.GetItems();
2853
2854 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2855
2856 if( item->Type() != PCB_TRACE_T
2857 && item->Type() != PCB_VIA_T
2858 && item->Type() != PCB_ARC_T
2859 && item->Type() != PCB_FOOTPRINT_T )
2860 {
2861 return 0;
2862 }
2863
2864 std::set<FOOTPRINT*> footprints;
2865
2866 if( item->Type() == PCB_FOOTPRINT_T )
2867 footprints.insert( static_cast<FOOTPRINT*>( item ) );
2868
2869 // We can drag multiple footprints, but not a grab-bag of items
2870 if( selection.Size() > 1 && item->Type() == PCB_FOOTPRINT_T )
2871 {
2872 for( int idx = 1; idx < selection.Size(); ++idx )
2873 {
2874 if( !selection.GetItem( idx )->IsBOARD_ITEM() )
2875 return 0;
2876
2877 if( static_cast<BOARD_ITEM*>( selection.GetItem( idx ) )->Type() != PCB_FOOTPRINT_T )
2878 return 0;
2879
2880 footprints.insert( static_cast<FOOTPRINT*>( selection.GetItem( idx ) ) );
2881 }
2882 }
2883
2884 // If we overrode locks, we want to clear the flag from the source item before SyncWorld is
2885 // called so that virtual vias are not generated for the (now unlocked) track segment. Note in
2886 // this case the lock can't be reliably re-applied, because there is no guarantee that the end
2887 // state of the drag results in the same number of segments so it's not clear which segment to
2888 // apply the lock state to.
2889 bool wasLocked = false;
2890
2891 if( item->IsLocked() )
2892 {
2893 wasLocked = true;
2894 item->SetLocked( false );
2895 }
2896
2897 m_toolMgr->RunAction( ACTIONS::selectionClear );
2898
2899 SCOPED_TOOL_PUSHER raii( frame(), aEvent );
2900 Activate();
2901
2902 m_startItem = nullptr;
2903
2904 PNS::ITEM_SET itemsToDrag;
2905
2906 bool showCourtyardConflicts = frame()->GetPcbNewSettings()->m_ShowCourtyardCollisions;
2907
2908 std::shared_ptr<DRC_ENGINE> drcEngine = m_toolMgr->GetTool<DRC_TOOL>()->GetDRCEngine();
2909 DRC_INTERACTIVE_COURTYARD_CLEARANCE courtyardClearanceDRC( drcEngine );
2910
2911 std::shared_ptr<CONNECTIVITY_DATA> connectivityData = board()->GetConnectivity();
2912 std::vector<BOARD_ITEM*> dynamicItems;
2913 std::unique_ptr<CONNECTIVITY_DATA> dynamicData = nullptr;
2914 VECTOR2I lastOffset;
2915 std::vector<PNS::ITEM*> leaderSegments;
2916 bool singleFootprintDrag = false;
2917
2918 // The PNS world may be stale if the board has been modified since the last sync (e.g. by
2919 // a Move operation). Sync it now so that FindItemByParent and joint lookups work correctly.
2920 m_router->SyncWorld();
2921
2922 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Make sure it's up-to-date.
2923 m_toolMgr->GetView()->SyncLayerVisibilityCache();
2924
2925 if( !footprints.empty() )
2926 {
2927 if( footprints.size() == 1 )
2928 singleFootprintDrag = true;
2929
2930 if( showCourtyardConflicts )
2931 courtyardClearanceDRC.Init( board() );
2932
2933 for( FOOTPRINT* footprint : footprints )
2934 {
2935 for( PAD* pad : footprint->Pads() )
2936 {
2937 PNS::ITEM* solid = m_router->GetWorld()->FindItemByParent( pad );
2938
2939 if( solid )
2940 itemsToDrag.Add( solid );
2941
2942 if( pad->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
2943 {
2944 if( connectivityData->GetRatsnestForPad( pad ).size() > 0 )
2945 dynamicItems.push_back( pad );
2946 }
2947 }
2948
2949 for( ZONE* zone : footprint->Zones() )
2950 {
2951 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( zone ) )
2952 itemsToDrag.Add( solid );
2953 }
2954
2955 for( BOARD_ITEM* shape : footprint->GraphicalItems() )
2956 {
2957 if( shape->GetLayer() == Edge_Cuts
2958 || shape->GetLayer() == Margin
2959 || IsCopperLayer( shape->GetLayer() ) )
2960 {
2961 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( shape ) )
2962 itemsToDrag.Add( solid );
2963 }
2964 }
2965
2966 if( showCourtyardConflicts )
2967 courtyardClearanceDRC.m_FpInMove.push_back( footprint );
2968 }
2969
2970 dynamicData = std::make_unique<CONNECTIVITY_DATA>( board()->GetConnectivity(), dynamicItems, true );
2971 connectivityData->BlockRatsnestItems( dynamicItems );
2972 }
2973 else
2974 {
2975 for( const EDA_ITEM* selItem : selectedItems )
2976 {
2977 if( !selItem->IsBOARD_ITEM() )
2978 continue;
2979
2980 const BOARD_ITEM* boardItem = static_cast<const BOARD_ITEM*>( selItem );
2981 PNS::ITEM* pnsItem = m_router->GetWorld()->FindItemByParent( boardItem );
2982
2983 if( !pnsItem )
2984 continue;
2985
2986 if( pnsItem->OfKind( PNS::ITEM::SEGMENT_T )
2987 || pnsItem->OfKind( PNS::ITEM::VIA_T )
2988 || pnsItem->OfKind( PNS::ITEM::ARC_T ) )
2989 {
2990 itemsToDrag.Add( pnsItem );
2991 }
2992 }
2993 }
2994
2995 GAL* gal = m_toolMgr->GetView()->GetGAL();
2996 VECTOR2I p0 = GetClampedCoords( controls()->GetCursorPosition( false ), COORDS_PADDING );
2997 VECTOR2I p = p0;
2998
2999 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
3000 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
3001
3002 if( itemsToDrag.Count() >= 1 )
3003 {
3004 // Snap to closest item. Use the frame's active layer rather than m_originalActiveLayer,
3005 // which is only set during prepareInteractive() and remains UNDEFINED_LAYER for inline
3006 // drag operations.
3007 PCB_LAYER_ID activeLayer = frame()->GetActiveLayer();
3008 int layer = m_iface->GetPNSLayerFromBoardLayer( activeLayer );
3009 PNS::ITEM* closestItem = nullptr;
3010 SEG::ecoord closestDistSq = std::numeric_limits<SEG::ecoord>::max();
3011
3012 for( PNS::ITEM* pitem : itemsToDrag.Items() )
3013 {
3014 const SHAPE* shape = pitem->Shape( layer );
3015
3016 if( !shape )
3017 continue;
3018
3019 SEG::ecoord distSq = shape->SquaredDistance( p0, 0 );
3020
3021 if( distSq < closestDistSq )
3022 {
3023 closestDistSq = distSq;
3024 closestItem = pitem;
3025 }
3026 }
3027
3028 if( closestItem )
3029 {
3030 p = snapToItem( closestItem, p0 );
3031
3032 m_startItem = closestItem;
3033
3034 if( closestItem->Net() )
3035 highlightNets( true, { closestItem->Net() } );
3036 }
3037 }
3038
3039 if( !footprints.empty() && singleFootprintDrag )
3040 {
3041 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
3042
3043 // The mouse is going to be moved on grid before dragging begins.
3044 VECTOR2I tweakedMousePos;
3046
3047 // Check if user wants to warp the mouse to origin of moved object
3048
3049 if( editFrame->GetMoveWarpsCursor() )
3050 tweakedMousePos = footprint->GetPosition(); // Use footprint anchor to warp mouse
3051 else
3052 tweakedMousePos = GetClampedCoords( controls()->GetCursorPosition(),
3053 COORDS_PADDING ); // Just use current mouse pos
3054
3055 // We tweak the mouse position using the value from above, and then use that as the
3056 // start position to prevent the footprint from jumping when we start dragging.
3057 // First we move the visual cross hair cursor...
3058 controls()->ForceCursorPosition( true, tweakedMousePos );
3059 controls()->SetCursorPosition( tweakedMousePos ); // ...then the mouse pointer
3060
3061 // Now that the mouse is in the right position, get a copy of the position to use later
3062 p = controls()->GetCursorPosition();
3063 }
3064
3065 int dragMode = aEvent.Parameter<int> ();
3066
3067 bool dragStarted = m_router->StartDragging( p, itemsToDrag, dragMode );
3068
3069 if( !dragStarted )
3070 {
3071 if( wasLocked )
3072 item->SetLocked( true );
3073
3074 if( !footprints.empty() )
3075 connectivityData->ClearLocalRatsnest();
3076
3077 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
3078 courtyardClearanceDRC.ClearConflicts( getView() );
3079
3081 controls()->ForceCursorPosition( false );
3082 highlightNets( false );
3083 return 0;
3084 }
3085
3086 m_gridHelper->SetAuxAxes( true, p );
3087 controls()->ShowCursor( true );
3088 controls()->SetAutoPan( true );
3089 frame()->UndoRedoBlock( true );
3090
3091 view()->ClearPreview();
3092 view()->InitPreview();
3093
3094 auto setCursor =
3095 [&]()
3096 {
3097 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3098 };
3099
3100 // Set initial cursor
3101 setCursor();
3102
3103 // Set the initial visible area
3104 BOX2D viewAreaD = getView()->GetGAL()->GetVisibleWorldExtents();
3105 m_router->SetVisibleViewArea( BOX2ISafe( viewAreaD ) );
3106
3107 // Send an initial movement to prime the collision detection
3108 m_router->Move( p, nullptr );
3109
3110 UI_UPDATE_INTERVAL_GUARD uiGuard( 200 );
3111
3112 bool hasMouseMoved = false;
3113 bool hasMultidragCancelled = false;
3114
3115 while( TOOL_EVENT* evt = Wait() )
3116 {
3117 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Catch any visibility
3118 // changes while dragging.
3119 m_toolMgr->GetView()->SyncLayerVisibilityCache();
3120
3121 setCursor();
3122
3123 if( evt->IsCancelInteractive()
3124 || evt->IsAction( &PCB_ACTIONS::cancelCurrentItem )
3125 || evt->IsActivate() )
3126 {
3127 if( wasLocked )
3128 item->SetLocked( true );
3129
3130 hasMultidragCancelled = true;
3131
3132 break;
3133 }
3134 else if( evt->IsMotion()
3135 || evt->IsAction( &ACTIONS::refreshPreview )
3136 || evt->IsDrag( BUT_LEFT ) )
3137 {
3138 hasMouseMoved = true;
3139 updateEndItem( *evt );
3141
3142 view()->ClearPreview();
3143
3144 if( !footprints.empty() )
3145 {
3146 VECTOR2I offset = m_endSnapPoint - p;
3147 BOARD_ITEM* previewItem;
3148
3149 for( FOOTPRINT* footprint : footprints )
3150 {
3151 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
3152 {
3153 previewItem = static_cast<BOARD_ITEM*>( drawing->Clone() );
3154 previewItem->Move( offset );
3155
3156 view()->AddToPreview( previewItem );
3157 view()->Hide( drawing, true );
3158 }
3159
3160 for( PAD* pad : footprint->Pads() )
3161 {
3162 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none()
3163 && pad->GetDrillSize().x == 0 )
3164 {
3165 previewItem = static_cast<BOARD_ITEM*>( pad->Clone() );
3166 previewItem->Move( offset );
3167
3168 view()->AddToPreview( previewItem );
3169 }
3170 else
3171 {
3172 // Pads with copper or holes are handled by the router
3173 }
3174
3175 view()->Hide( pad, true );
3176 }
3177
3178 previewItem = static_cast<BOARD_ITEM*>( footprint->Reference().Clone() );
3179 previewItem->Move( offset );
3180 view()->AddToPreview( previewItem );
3181 view()->Hide( &footprint->Reference() );
3182
3183 previewItem = static_cast<BOARD_ITEM*>( footprint->Value().Clone() );
3184 previewItem->Move( offset );
3185 view()->AddToPreview( previewItem );
3186 view()->Hide( &footprint->Value() );
3187
3188 if( showCourtyardConflicts )
3189 footprint->Move( offset );
3190 }
3191
3192 if( showCourtyardConflicts )
3193 {
3194 courtyardClearanceDRC.Run();
3195 courtyardClearanceDRC.UpdateConflicts( getView(), false );
3196
3197 for( FOOTPRINT* footprint : footprints )
3198 footprint->Move( -offset );
3199 }
3200
3201 // Update ratsnest
3202 dynamicData->Move( offset - lastOffset );
3203 lastOffset = offset;
3204 connectivityData->ComputeLocalRatsnest( dynamicItems, dynamicData.get(), offset );
3205 }
3206
3207 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
3208 {
3209 bool dragStatus;
3210
3211 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
3212 {
3213 if( !dragStatus )
3214 {
3215 wxString hint;
3216 hint.Printf( _( "(%s to commit anyway.)" ), KeyNameFromKeyCode( MD_CTRL + PSEUDO_WXK_CLICK ) );
3217
3219 statusItem->SetMessage( _( "Track violates DRC." ) );
3220 statusItem->SetHint( hint );
3221 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
3222 view()->AddToPreview( statusItem );
3223 }
3224 }
3225 }
3226 }
3227 else if( hasMouseMoved && ( evt->IsMouseUp( BUT_LEFT )
3228 || evt->IsClick( BUT_LEFT )
3229 || evt->IsAction( &ACTIONS::cursorClick ) ) )
3230 {
3231 bool forceFinish = false;
3232 bool forceCommit = evt->Modifier( MD_CTRL );
3233
3234 updateEndItem( *evt );
3235 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
3236 leaderSegments = m_router->GetLastCommittedLeaderSegments();
3237
3238 break;
3239 }
3240 else if( evt->IsUndoRedo() )
3241 {
3242 // We're in an UndoRedoBlock. If we get here, something's broken.
3243 wxFAIL;
3244 break;
3245 }
3246 else if( evt->Category() == TC_COMMAND )
3247 {
3248 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
3249 // but we don't at present have that, so we just knock out some of the egregious ones.
3250 if( evt->IsAction( &ACTIONS::cut )
3251 || evt->IsAction( &ACTIONS::copy )
3252 || evt->IsAction( &ACTIONS::paste )
3253 || evt->IsAction( &ACTIONS::pasteSpecial )
3255 {
3256 wxBell();
3257 }
3258 // treat an undo as an escape
3259 else if( evt->IsAction( &ACTIONS::undo ) )
3260 {
3261 if( wasLocked )
3262 item->SetLocked( true );
3263
3264 break;
3265 }
3266 else
3267 {
3268 evt->SetPassEvent();
3269 }
3270 }
3271 else
3272 {
3273 evt->SetPassEvent();
3274 }
3275
3276 handleCommonEvents( *evt );
3277 }
3278
3279 if( !footprints.empty() )
3280 {
3281 for( FOOTPRINT* footprint : footprints )
3282 {
3283 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
3284 view()->Hide( drawing, false );
3285
3286 view()->Hide( &footprint->Reference(), false );
3287 view()->Hide( &footprint->Value(), false );
3288
3289 for( PAD* pad : footprint->Pads() )
3290 view()->Hide( pad, false );
3291 }
3292
3293 view()->ClearPreview();
3294 view()->ShowPreview( false );
3295
3296 connectivityData->ClearLocalRatsnest();
3297 }
3298
3299 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
3300 courtyardClearanceDRC.ClearConflicts( getView() );
3301
3302 if( m_router->RoutingInProgress() )
3303 m_router->StopRouting();
3304
3305
3306 if( itemsToDrag.Size() && hasMultidragCancelled )
3307 {
3309 }
3310 else if( leaderSegments.size() )
3311 {
3312 std::vector<EDA_ITEM*> newItems;
3313
3314 for( PNS::ITEM* lseg : leaderSegments )
3315 newItems.push_back( lseg->Parent() );
3316
3317 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &newItems );
3318 }
3319
3320 m_gridHelper->SetAuxAxes( false );
3321 controls()->SetAutoPan( false );
3322 controls()->ForceCursorPosition( false );
3323 frame()->UndoRedoBlock( false );
3324 highlightNets( false );
3325 view()->ClearPreview();
3326 view()->ShowPreview( false );
3327
3328 return 0;
3329}
3330
3331
3333{
3334 const SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
3335
3336 if( selection.Size() != 1 )
3337 return 0;
3338
3339 const BOARD_CONNECTED_ITEM* item = static_cast<const BOARD_CONNECTED_ITEM*>( selection.Front() );
3340
3341 if( item->Type() != PCB_TRACE_T && item->Type() != PCB_ARC_T )
3342 return 0;
3343
3344 Activate();
3345
3346 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Make sure it's up-to-date.
3347 m_toolMgr->GetView()->SyncLayerVisibilityCache();
3348
3349 m_startItem = m_router->GetWorld()->FindItemByParent( item );
3350
3351 TOOL_MANAGER* toolManager = frame()->GetToolManager();
3352 GAL* gal = toolManager->GetView()->GetGAL();
3353
3354 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
3355 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
3356
3357 controls()->ForceCursorPosition( false );
3358
3359 if( toolManager->IsContextMenuActive() )
3360 {
3361 // If we're here from a context menu then we need to get the position of the
3362 // cursor when the context menu was invoked. This is used to figure out the
3363 // break point on the track.
3365 }
3366 else
3367 {
3368 // If we're here from a hotkey, then get the current mouse position so we know
3369 // where to break the track.
3370 m_startSnapPoint = snapToItem( m_startItem, controls()->GetCursorPosition() );
3371 }
3372
3373 if( m_startItem && m_startItem->IsLocked() )
3374 {
3375 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
3376 wxOK | wxCANCEL | wxICON_WARNING );
3377 dlg.SetOKLabel( _( "Break Track" ) );
3378 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
3379
3380 if( dlg.ShowModal() == wxID_CANCEL )
3381 return 0;
3382 }
3383
3384 m_toolMgr->RunAction( ACTIONS::selectionClear );
3385
3386 frame()->UndoRedoBlock( true );
3387 breakTrack();
3388
3389 if( m_router->RoutingInProgress() )
3390 m_router->StopRouting();
3391
3392 frame()->UndoRedoBlock( false );
3393
3394 return 0;
3395}
3396
3397
3399{
3401 DIALOG_TRACK_VIA_SIZE sizeDlg( frame(), bds );
3402
3403 if( sizeDlg.ShowModal() == wxID_OK )
3404 {
3405 bds.m_TempOverrideTrackWidth = true;
3406 bds.UseCustomTrackViaSize( true );
3407
3410 }
3411
3412 return 0;
3413}
3414
3415
3417{
3418 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
3419
3420 if( !m_router->GetCurrentNets().empty() )
3421 m_iface->ImportSizes( sizes, m_startItem, m_router->GetCurrentNets()[0], VECTOR2D() );
3422
3423 m_router->UpdateSizes( sizes );
3424
3425 // Changing the track width can affect the placement, so call the
3426 // move routine without changing the destination
3427 // Update end item first to avoid moving to an invalid/missing item
3428 updateEndItem( aEvent );
3430
3432
3433 return 0;
3434}
3435
3436
3438{
3439 std::vector<MSG_PANEL_ITEM> items;
3440
3441 if( m_router->GetState() == PNS::ROUTER::ROUTE_TRACK )
3442 {
3443 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
3444 PNS::RULE_RESOLVER* resolver = m_iface->GetRuleResolver();
3445 PNS::CONSTRAINT constraint;
3446 std::vector<PNS::NET_HANDLE> nets = m_router->GetCurrentNets();
3447 wxString description;
3448 wxString secondary;
3449 wxString mode;
3450
3452 {
3453 wxASSERT( nets.size() >= 2 );
3454
3455 NETINFO_ITEM* netA = static_cast<NETINFO_ITEM*>( nets[0] );
3456 NETINFO_ITEM* netB = static_cast<NETINFO_ITEM*>( nets[1] );
3457 wxASSERT( netA );
3458 wxASSERT( netB );
3459
3460 description = wxString::Format( _( "Routing Diff Pair: %s" ),
3461 netA->GetNetname() + wxT( ", " ) + netB->GetNetname() );
3462
3463 wxString netclass;
3464 NETCLASS* netclassA = netA->GetNetClass();
3465 NETCLASS* netclassB = netB->GetNetClass();
3466
3467 if( *netclassA == *netclassB )
3468 netclass = netclassA->GetHumanReadableName();
3469 else
3470 netclass = netclassA->GetHumanReadableName() + wxT( ", " )
3471 + netclassB->GetHumanReadableName();
3472
3473 secondary = wxString::Format( _( "Resolved Netclass: %s" ),
3474 UnescapeString( netclass ) );
3475 }
3476 else if( !nets.empty() && nets[0] )
3477 {
3478 NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( nets[0] );
3479
3480 description = wxString::Format( _( "Routing Track: %s" ),
3481 net->GetNetname() );
3482
3483 secondary = wxString::Format(
3484 _( "Resolved Netclass: %s" ),
3486 }
3487 else
3488 {
3489 description = _( "Routing Track" );
3490 secondary = _( "(no net)" );
3491 }
3492
3493 items.emplace_back( description, secondary );
3494
3495 wxString cornerMode;
3496
3497 if( m_router->Settings().GetFreeAngleMode() )
3498 {
3499 cornerMode = _( "Free-angle" );
3500 }
3501 else
3502 {
3503 switch( m_router->Settings().GetCornerMode() )
3504 {
3505 case DIRECTION_45::CORNER_MODE::MITERED_45: cornerMode = _( "45-degree" ); break;
3506 case DIRECTION_45::CORNER_MODE::ROUNDED_45: cornerMode = _( "45-degree rounded" ); break;
3507 case DIRECTION_45::CORNER_MODE::MITERED_90: cornerMode = _( "90-degree" ); break;
3508 case DIRECTION_45::CORNER_MODE::ROUNDED_90: cornerMode = _( "90-degree rounded" ); break;
3509 default: break;
3510 }
3511 }
3512
3513 items.emplace_back( _( "Corner Style" ), cornerMode );
3514
3515 switch( m_router->Settings().Mode() )
3516 {
3517 case PNS::PNS_MODE::RM_MarkObstacles: mode = _( "Highlight collisions" ); break;
3518 case PNS::PNS_MODE::RM_Walkaround: mode = _( "Walk around" ); break;
3519 case PNS::PNS_MODE::RM_Shove: mode = _( "Shove" ); break;
3520 default: break;
3521 }
3522
3523 items.emplace_back( _( "Mode" ), mode );
3524
3525#define FORMAT_VALUE( x ) frame()->MessageTextFromValue( x )
3526
3528 {
3529 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
3530 FORMAT_VALUE( sizes.DiffPairWidth() ) ),
3531 wxString::Format( _( "(from %s)" ),
3532 sizes.GetDiffPairWidthSource() ) );
3533
3534 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
3535 FORMAT_VALUE( sizes.Clearance() ) ),
3536 wxString::Format( _( "(from %s)" ),
3537 sizes.GetClearanceSource() ) );
3538
3539 items.emplace_back( wxString::Format( _( "Diff Pair Gap: %s" ),
3540 FORMAT_VALUE( sizes.DiffPairGap() ) ),
3541 wxString::Format( _( "(from %s)" ),
3542 sizes.GetDiffPairGapSource() ) );
3543
3544 const PNS::ITEM_SET& traces = m_router->Placer()->Traces();
3545 wxASSERT( traces.Count() == 2 );
3546
3548 traces[1], m_router->GetCurrentLayer(), &constraint ) )
3549 {
3550 items.emplace_back( wxString::Format( _( "DP Max Uncoupled-length: %s" ),
3551 FORMAT_VALUE( constraint.m_Value.Max() ) ),
3552 wxString::Format( _( "(from %s)" ),
3553 constraint.m_RuleName ) );
3554 }
3555 }
3556 else
3557 {
3558 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
3559 FORMAT_VALUE( sizes.TrackWidth() ) ),
3560 wxString::Format( _( "(from %s)" ),
3561 sizes.GetWidthSource() ) );
3562
3563 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
3564 FORMAT_VALUE( sizes.Clearance() ) ),
3565 wxString::Format( _( "(from %s)" ),
3566 sizes.GetClearanceSource() ) );
3567 }
3568
3569#undef FORMAT_VALUE
3570
3571 frame()->SetMsgPanel( items );
3572 }
3573 else
3574 {
3575 frame()->SetMsgPanel( board() );
3576 return;
3577 }
3578}
3579
3580
3582{
3584
3599
3607
3643
3646
3652}
std::function< void(const VECTOR2I &, GENERAL_COLLECTOR &, PCB_SELECTION_TOOL *)> CLIENT_SELECTION_FILTER
Definition actions.h:33
@ add_via_stack
@ width_track_via
@ change_entry_orient
@ switch_corner_rounding_shape
constexpr BOX2I BOX2ISafe(const BOX2D &aInput)
Definition box2.h:921
BOX2< VECTOR2D > BOX2D
Definition box2.h:915
static TOOL_ACTION paste
Definition actions.h:76
static TOOL_ACTION cancelInteractive
Definition actions.h:68
static TOOL_ACTION copy
Definition actions.h:74
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition actions.h:213
static TOOL_ACTION pasteSpecial
Definition actions.h:77
static TOOL_ACTION cursorDblClick
Definition actions.h:177
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION doDelete
Definition actions.h:81
static TOOL_ACTION cursorClick
Definition actions.h:176
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION cut
Definition actions.h:73
static TOOL_ACTION refreshPreview
Definition actions.h:155
static TOOL_ACTION finishInteractive
Definition actions.h:69
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:228
Manage TOOL_ACTION objects.
void SetConditions(const TOOL_ACTION &aAction, const ACTION_CONDITIONS &aConditions)
Set the conditions the UI elements for activating a specific tool action should use for determining t...
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
Default constructor.
void Clear()
Remove all the entries from the menu (as well as its title).
void SetTitle(const wxString &aTitle) override
Set title for the menu.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void SetLayerVisible(int aLayer, bool isVisible)
Manually update visibility for a given layer.
BASE_SET & set(size_t pos)
Definition base_set.h:126
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
NETINFO_ITEM * GetNet() const
Return NETINFO_ITEM object for a given item.
Container for design settings for a BOARD object.
void UseCustomTrackViaSize(bool aEnabled)
Enables/disables custom track/via size settings.
void SetCustomDiffPairWidth(int aWidth)
Sets custom track width for differential pairs (i.e.
void SetViaSizeIndex(int aIndex)
Set the current via size list index to aIndex.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
std::vector< DIFF_PAIR_DIMENSION > m_DiffPairDimensionsList
void SetCustomDiffPairGap(int aGap)
Sets custom gap for differential pairs (i.e.
bool UseNetClassVia() const
Return true if netclass values should be used to obtain appropriate via size.
bool UseNetClassTrack() const
Return true if netclass values should be used to obtain appropriate track width.
bool UseNetClassDiffPair() const
Return true if netclass values should be used to obtain appropriate diff pair dimensions.
void SetTrackWidthIndex(int aIndex)
Set the current track width list index to aIndex.
void UseCustomDiffPairDimensions(bool aEnabled)
Enables/disables custom differential pair dimensions.
std::vector< int > m_TrackWidthList
std::vector< VIA_STACK_PRESET > m_ViaStackPresets
std::vector< VIA_DIMENSION > m_ViasDimensionsList
void SetCustomDiffPairViaGap(int aGap)
Sets custom via gap for differential pairs (i.e.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
void SetLocked(bool aLocked) override
Definition board_item.h:418
bool IsLocked() const override
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:436
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:3001
bool IsLayerVisible(PCB_LAYER_ID aLayer) const
A proxy function that calls the correspondent function in m_BoardSettings tests whether a given layer...
Definition board.cpp:1191
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1185
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:759
CN_EDGE represents a point-to-point connection, whether realized or unrealized (ie: tracks etc.
void Empty()
Clear the list.
Definition collector.h:87
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
int CountType(KICAD_T aType)
Count the number of items matching aType.
Definition collector.h:221
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition collector.h:97
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void AddCheckItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a checked menu entry to run a TOOL_ACTION on selected items.
PNS::LOGGER::TEST_CASE_TYPE getTestCaseType() const
int ShowModal() override
Implementing DIALOG_TRACK_VIA_SIZE_BASE.
PCB_EDIT_FRAME & m_frame
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
DIFF_PAIR_MENU(PCB_EDIT_FRAME &aFrame)
void update() override
Update menu state stub.
CORNER_MODE
Corner modes.
Definition direction45.h:67
@ ROUNDED_90
H/V with filleted corners.
Definition direction45.h:71
@ MITERED_90
H/V only (90-degree corners)
Definition direction45.h:70
@ ROUNDED_45
H/V/45 with filleted corners.
Definition direction45.h:69
@ MITERED_45
H/V/45 with mitered corners (default)
Definition direction45.h:68
Tool responsible for drawing graphical elements like lines, arcs, circles, etc.
wxString GetName() const
Definition drc_rule.h:237
MINOPTMAX< int > m_Value
Definition drc_rule.h:279
bool IsNull() const
Definition drc_rule.h:197
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
void UpdateConflicts(KIGFX::VIEW *aView, bool aHighlightMoved)
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, INFOBAR_MESSAGE_TYPE aType=INFOBAR_MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
static const std::vector< KICAD_T > DraggableItems
A scan list for items that can be dragged.
Definition collectors.h:148
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:38
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
Definition kidialog.cpp:51
int ShowModal() override
Definition kidialog.cpp:89
Abstract interface for drawing on a 2D-surface.
BOX2D GetVisibleWorldExtents() const
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.
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
virtual void WarpMouseCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual void SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
void ShowPreview(bool aShow=true)
Definition view.cpp:1951
virtual int GetTopLayer() const
Definition view.cpp:941
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:209
void InitPreview()
Definition view.cpp:1930
void ClearPreview()
Definition view.cpp:1915
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:1837
void AddToPreview(VIEW_ITEM *aItem, bool aTakeOwnership=true)
Definition view.cpp:1937
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
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 UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition lset.cpp:739
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition lset.cpp:623
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
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
T PinnedOpt() const
Definition minoptmax.h:32
T Max() const
Definition minoptmax.h:30
T Opt() const
Definition minoptmax.h:31
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:43
bool HasuViaDrill() const
Definition netclass.h:173
const wxString GetHumanReadableName() const
Gets the consolidated name of this netclass (which may be an aggregate).
Definition netclass.cpp:334
int GetuViaDrill() const
Definition netclass.h:174
bool HasuViaDiameter() const
Definition netclass.h:165
int GetuViaDiameter() const
Definition netclass.h:166
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetname() const
Definition netinfo.h:110
NETCLASS * GetNetClass()
Definition netinfo.h:101
int GetNetCode() const
Definition netinfo.h:104
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:281
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
Definition pad.h:61
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
Definition paths.cpp:137
static TOOL_ACTION_GROUP layerDirectSwitchActions()
static TOOL_ACTION layerToggle
static TOOL_ACTION drag45Degree
static TOOL_ACTION layerInner12
static TOOL_ACTION routerUndoLastSegment
static TOOL_ACTION layerInner8
static TOOL_ACTION layerInner3
static TOOL_ACTION layerPrev
static TOOL_ACTION routerSettingsDialog
Activation of the Push and Shove settings dialogs.
static TOOL_ACTION layerInner2
static TOOL_ACTION routerAttemptFinish
static TOOL_ACTION routeDiffPair
Activation of the Push and Shove router (differential pair mode)
static TOOL_ACTION trackViaSizeChanged
static TOOL_ACTION layerChanged
static TOOL_ACTION layerInner25
static TOOL_ACTION breakTrack
Break a single track into two segments at the cursor.
static TOOL_ACTION routerRouteSelectedFromEnd
static TOOL_ACTION routerHighlightMode
Actions to enable switching modes via hotkey assignments.
static TOOL_ACTION routerWalkaroundMode
static TOOL_ACTION routerShoveMode
static TOOL_ACTION layerInner24
static TOOL_ACTION properties
Activation of the edit tool.
static TOOL_ACTION layerInner29
static TOOL_ACTION routerOptimizeSelected
static TOOL_ACTION routerAutorouteSelected
static TOOL_ACTION layerInner11
static TOOL_ACTION routerDiffPairDialog
static TOOL_ACTION routerContinueFromEnd
static TOOL_ACTION layerInner16
static TOOL_ACTION layerInner26
static TOOL_ACTION layerInner18
static TOOL_ACTION layerInner14
static TOOL_ACTION selectLayerPair
static TOOL_ACTION layerInner6
static TOOL_ACTION dragFreeAngle
static TOOL_ACTION clearHighlight
static TOOL_ACTION layerInner22
static TOOL_ACTION layerInner5
static TOOL_ACTION layerInner20
static TOOL_ACTION layerInner7
static TOOL_ACTION layerInner27
static TOOL_ACTION cancelCurrentItem
static TOOL_ACTION layerInner1
static TOOL_ACTION layerInner10
static TOOL_ACTION layerInner15
static TOOL_ACTION layerInner17
static TOOL_ACTION layerBottom
static TOOL_ACTION layerInner19
static TOOL_ACTION layerInner9
static TOOL_ACTION routerInlineDrag
Activation of the Push and Shove router (inline dragging mode)
static TOOL_ACTION layerInner30
static TOOL_ACTION layerTop
static TOOL_ACTION cycleRouterMode
static TOOL_ACTION layerInner4
static TOOL_ACTION routeSingleTrack
Activation of the Push and Shove router.
static TOOL_ACTION layerInner13
static TOOL_ACTION layerInner21
static TOOL_ACTION layerNext
static TOOL_ACTION routerRouteSelected
static TOOL_ACTION placeViaStack
static TOOL_ACTION layerInner23
static TOOL_ACTION layerInner28
Common, abstract interface for edit frames.
APPEARANCE_CONTROLS * GetAppearancePanel()
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
virtual PCB_LAYER_ID GetActiveLayer() const
The main frame for Pcbnew.
void SetActiveLayer(PCB_LAYER_ID aLayer) override
Change the currently active layer to aLayer and also update the APPEARANCE_CONTROLS.
A PCB_IO derivation for saving and loading Pcbnew s-expression formatted files.
void SaveBoard(const wxString &aFileName, BOARD &aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
PCB_LAYER_ID m_Route_Layer_TOP
Definition pcb_screen.h:39
PCB_LAYER_ID m_Route_Layer_BOTTOM
Definition pcb_screen.h:40
The selection tool: currently supports:
T * frame() const
KIGFX::PCB_VIEW * view() const
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
PCBNEW_SETTINGS::DISPLAY_OPTIONS & displayOptions() const
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:94
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:97
const VECTOR2I & GetStart() const
Definition pcb_track.h:98
const VECTOR2I & GetEnd() const
Definition pcb_track.h:95
EDA_ITEM_FLAGS IsPointOnEnds(const VECTOR2I &point, int min_dist=0) const
Return STARTPOINT if point if near (dist = min_dist) start point, ENDPOINT if point if near (dist = m...
static int ExpandMultiHopMicrovias(BOARD *aBoard, BOARD_COMMIT *aCommit, const std::function< const VIA_STACK_PRESET *(PCB_VIA *)> &aMatcher=nullptr)
Replace loose microvias spanning more than one hop with stacks of single hop microvias.
static std::set< KIID > CollectExpandableMicrovias(BOARD *aBoard)
Ids of the vias ExpandMultiHopMicrovias would consider right now.
static const wxString GENERATOR_TYPE
PCB_LAYER_ID BottomLayer() const
VECTOR2I GetPosition() const override
Definition pcb_track.h:599
void SetPosition(const VECTOR2I &aPoint) override
Definition pcb_track.h:600
void SetSizeFromRules(int aDiameter, int aDrill)
Give the via a uniform padstack sized from its diameter and hole rules.
void SetLayerPair(PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer)
For a via m_layer contains the top layer, the other layer is in m_bottomLayer/.
int GetWidth() const override
void SetViaType(VIATYPE aViaType)
Definition pcb_track.h:419
PCB_LAYER_ID TopLayer() const
int GetDrillValue() const
Calculate the drill value for vias (m_drill if > 0, or default drill value for the board).
int Size() const
int Count(int aKindMask=-1) const
Definition pns_itemset.h:74
void Add(const LINE &aLine)
std::vector< ITEM * > & Items()
Definition pns_itemset.h:95
Base class for PNS router board items.
Definition pns_item.h:98
const PNS_LAYER_RANGE & Layers() const
Definition pns_item.h:212
virtual NET_HANDLE Net() const
Definition pns_item.h:210
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual HOLE * Hole() const
Definition pns_item.h:308
virtual bool HasHole() const
Definition pns_item.h:307
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
int ArcCount() const
Definition pns_line.h:150
bool CompareGeometry(const LINE &aOther)
Return true if the line is geometrically identical as line aOther.
static wxString FormatLogFileAsJSON(const LOG_DATA &aLogData)
Keep the router "world" - i.e.
Definition pns_node.h:244
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition pns_node.cpp:157
OPT_OBSTACLE CheckColliding(const ITEM *aItem, int aKindMask=ITEM::ANY_T)
Check if the item collides with anything else in the world, and if found, returns the obstacle.
Definition pns_node.cpp:492
RULE_RESOLVER * GetRuleResolver() const
Definition pns_node.h:293
bool Add(std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant=false)
Add an item to the current node.
Definition pns_node.cpp:747
const LINE AssembleLine(LINKED_ITEM *aSeg, int *aOriginSegmentIndex=nullptr, bool aStopAtLockedJoints=false, bool aFollowLockedSegments=false, bool aAllowSegmentSizeMismatch=true)
Follow the joint map to assemble a line connecting two non-trivial joints starting from segment aSeg.
ITEM * FindItemByParent(const BOARD_ITEM *aParent)
void Remove(ARC *aArc)
Remove an item from this branch.
Definition pns_node.cpp:991
static bool Optimize(LINE *aLine, int aEffortLevel, NODE *aWorld, const VECTOR2I &aV=VECTOR2I(0, 0))
A quick shortcut to optimize a line without creating and setting up an optimizer.
@ SMART_PADS
Reroute pad exits.
@ FANOUT_CLEANUP
Simplify pad-pad and pad-via connections if possible.
@ MERGE_SEGMENTS
Reduce corner cost iteratively.
@ MERGE_COLINEAR
Merge co-linear segments.
@ MERGE_OBTUSE
Reduce corner cost by merging obtuse segments.
@ REQUIRE_OBTUSE_ANGLES
Try to prevent 90-degree or acute corners in a drag.
Contain all persistent settings of the router, such as the mode, optimization effort,...
void SetMode(PNS_MODE aMode)
Set the routing mode.
PNS_MODE Mode() const
Return the routing mode.
virtual bool QueryConstraint(CONSTRAINT_TYPE aType, const ITEM *aItemA, const ITEM *aItemB, int aLayer, CONSTRAINT *aConstraint)=0
virtual NET_HANDLE DpCoupledNet(NET_HANDLE aNet)=0
void SetViaType(VIATYPE aViaType)
void SetTrackWidth(int aWidth)
void SetDiffPairWidth(int aWidth)
void SetDiffPairWidthSource(const wxString &aSource)
void SetDiffPairGapSource(const wxString &aSource)
void SetDiffPairGap(int aGap)
void SetViaDrill(int aDrill)
wxString GetClearanceSource() const
wxString GetDiffPairGapSource() const
wxString GetDiffPairWidthSource() const
void AddLayerPair(int aL1, int aL2)
void SetClearance(int aClearance)
bool TrackWidthIsExplicit() const
void SetViaDiameter(int aDiameter)
void SetClearanceSource(const wxString &aSource)
wxString GetWidthSource() const
void SetWidthSource(const wxString &aSource)
virtual void updateStartItem(const TOOL_EVENT &aEvent, bool aIgnorePads=false)
const VECTOR2I snapToItem(ITEM *aSnapToItem, const VECTOR2I &aP)
virtual void highlightNets(bool aEnabled, std::set< NET_HANDLE > aNetcodes={})
SIZES_SETTINGS m_savedSizes
PNS_KICAD_IFACE * m_iface
virtual void updateEndItem(const TOOL_EVENT &aEvent)
TOOL_BASE(const std::string &aToolName)
static const unsigned int COORDS_PADDING
ROUTER * m_router
VECTOR2I m_endSnapPoint
PCB_GRID_HELPER * m_gridHelper
VECTOR2I m_startSnapPoint
Represent a contiguous set of PCB layers.
int Start() const
bool Overlaps(const PNS_LAYER_RANGE &aOther) const
bool SaveAs(const wxString &aDirectory, const wxString &aFile)
bool SaveAs(const wxString &aDirectory, const wxString &aFile)
Container for project specific data.
Definition project.h:63
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
Definition project.h:207
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:201
Describe ratsnest for a single net.
const std::vector< CN_EDGE > & GetEdges() const
void SetMessage(const wxString &aStatus)
void SetHint(const wxString &aHint)
void SetPosition(const VECTOR2I &aPos) override
int onViaCommand(const TOOL_EVENT &aEvent)
void commitPendingViaStack()
Build and commit a staggered via stack queued during routing (after the PNS world is gone).
int InlineDrag(const TOOL_EVENT &aEvent)
int onViaStackCommand(const TOOL_EVENT &aEvent)
std::shared_ptr< ACTION_MENU > m_trackViaMenu
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
int onTrackViaSizeChanged(const TOOL_EVENT &aEvent)
int CustomTrackWidthDialog(const TOOL_EVENT &aEvent)
int handlePnSCornerModeChange(const TOOL_EVENT &aEvent)
static void NeighboringSegmentFilter(const VECTOR2I &aPt, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *aSelTool)
PNS::PNS_MODE GetRouterMode()
VECTOR2I m_pendingStackHead
PCB_LAYER_ID m_pendingStackEnd
void saveRouterDebugLog()
void recordPendingStackViaPos()
Remember where the placer is about to leave the newest drop's via.
void performDragging(int aMode=PNS::DM_ANY)
bool m_stackDropAwaitingVia
std::set< KIID > m_preRouteExpandableVias
PCB_LAYER_ID m_originalActiveLayer
int onLayerCommand(const TOOL_EVENT &aEvent)
PCB_LAYER_ID getStartLayer(const PNS::ITEM *aItem)
int CycleRouterMode(const TOOL_EVENT &aEvent)
void updateSizesAfterRouterEvent(int targetLayer, const VECTOR2I &aPos)
bool m_inRouteSelected
bool m_inRouterTool
int handleLayerSwitch(const TOOL_EVENT &aEvent, bool aForceVia)
void switchLayerOnViaPlacement()
int RouteSelected(const TOOL_EVENT &aEvent)
bool m_startWithVia
PCB_LAYER_ID m_pendingStackStart
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
bool finishInteractive()
int ChangeRouterMode(const TOOL_EVENT &aEvent)
std::shared_ptr< ACTION_MENU > m_diffPairMenu
PCB_LAYER_ID m_lastTargetLayer
void handleCommonEvents(TOOL_EVENT &evt)
void performRouting(VECTOR2D aStartPosition)
bool Init() override
Init() is called once upon a registration of the tool.
int OptimizeSelected(const TOOL_EVENT &aEvent)
int InlineBreakTrack(const TOOL_EVENT &aEvent)
bool prepareInteractive(VECTOR2D aStartPosition)
void restoreSelection(const PCB_SELECTION &aOriginalSelection)
bool RoutingInProgress()
Returns whether routing is currently active.
void UpdateMessagePanel()
int MainLoop(const TOOL_EVENT &aEvent)
bool m_pendingViaStack
bool CanInlineDrag(int aDragMode)
int SettingsDialog(const TOOL_EVENT &aEvent)
PCB_LAYER_ID m_viaStackResumeLayer
int DpDimensionsDialog(const TOOL_EVENT &aEvent)
std::vector< PENDING_STACK_EXPANSION > m_pendingStackedExpansions
int SelectCopperLayerPair(const TOOL_EVENT &aEvent)
VECTOR2I::extended_type ecoord
Definition seg.h:40
static bool NotEmpty(const SELECTION &aSelection)
Test if there are any items selected.
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
std::deque< EDA_ITEM * > & Items()
Definition selection.h:181
An abstract shape on 2D plane.
Definition shape.h:124
virtual SEG::ecoord SquaredDistance(const VECTOR2I &aP, bool aOutlineOnly=false) const
Definition shape.cpp:111
bool GetMoveWarpsCursor() const
Indicate that a move operation should warp the mouse pointer to the origin of the move object.
Build up the properties of a TOOL_ACTION in an incremental manner that is static-construction safe.
Represent a single user action.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
virtual void Reset(RESET_REASON aReason)=0
Bring the tool to a known, initial state.
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
Return the instance of KIGFX::VIEW object used in the application.
Definition tool_base.cpp:34
bool IsToolActive() const
Definition tool_base.cpp:28
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 HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:256
bool DisableGridSnapping() const
Definition tool_event.h:367
int KeyCode() const
Definition tool_event.h:372
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition tool_event.h:388
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:289
bool IsKeyPressed() const
Definition tool_event.h:377
TOOL_EVENT_CATEGORY Category() const
Return the category (eg. mouse/keyboard/action) of an event.
Definition tool_event.h:243
int Modifier(int aMask=MD_MODIFIER_MASK) const
Return information about key modifiers state (Ctrl, Alt, etc.).
Definition tool_event.h:362
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
bool IsActionInGroup(const TOOL_ACTION_GROUP &aGroup) const
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:484
void SetPassEvent(bool aPass=true)
Definition tool_event.h:252
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).
friend class TOOL_MANAGER
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
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.
Master controller class:
VECTOR2D GetMenuCursorPos() const
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
bool IsContextMenuActive() const
True while processing a context menu.
KIGFX::VIEW * GetView() const
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
TRACK_WIDTH_MENU(PCB_EDIT_FRAME &aFrame)
void update() override
Update menu state stub.
PCB_EDIT_FRAME & m_frame
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:76
void ShowMessageFor(const wxString &aMessage, int aTime, int aFlags=wxICON_INFORMATION, MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the infobar with the provided message and icon for a specific period of time.
static bool IsZoneFillAction(const TOOL_EVENT *aEvent)
Handle a list of polygons defining a copper zone.
Definition zone.h:70
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:274
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
This file is part of the common library.
@ ARROW
Definition cursors.h:42
@ PENCIL
Definition cursors.h:48
#define CHECK(x)
@ VIA_DIAMETER_CONSTRAINT
Definition drc_rule.h:72
@ DIFF_PAIR_GAP_CONSTRAINT
Definition drc_rule.h:78
@ TRACK_WIDTH_CONSTRAINT
Definition drc_rule.h:61
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:51
@ HOLE_SIZE_CONSTRAINT
Definition drc_rule.h:56
#define _(s)
#define ROUTER_TRANSIENT
transient items that should NOT be cached
#define ENDPOINT
ends. (Used to support dragging.)
std::uint32_t EDA_ITEM_FLAGS
#define STARTPOINT
When a line is selected, these flags indicate which.
a few functions useful in geometry calculations.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamp a vector to values that can be negated, respecting numeric limits of coordinates data type with...
wxString m_RouterTestCaseDirectory
Router test case directory.
static const std::string DesignRulesFileExtension
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Return the key name from the key code.
#define PSEUDO_WXK_CLICK
std::string source
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ Edge_Cuts
Definition layer_ids.h:108
@ B_Cu
Definition layer_ids.h:61
@ Margin
Definition layer_ids.h:109
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ F_Cu
Definition layer_ids.h:60
std::optional< wxString > fileHashMMH3(const wxString &aFilePath)
Calculates an MMH3 hash of a given file.
Definition io_utils.cpp:91
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:30
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:533
PNS_MODE
Routing modes.
@ RM_MarkObstacles
Ignore collisions, mark obstacles.
@ RM_Walkaround
Only walk around.
@ RM_Shove
Only shove.
void * NET_HANDLE
Definition pns_item.h:55
ROUTER_MODE
Definition pns_router.h:68
@ PNS_MODE_ROUTE_DIFF_PAIR
Definition pns_router.h:70
@ DM_ANY
Definition pns_router.h:83
@ DM_FREE_ANGLE
Definition pns_router.h:81
#define _HKI(x)
Definition page_info.cpp:40
VIATYPE
@ ID_POPUP_PCB_SELECT_WIDTH1
Definition pcbnew_id.h:25
@ ID_POPUP_PCB_SELECT_DIFFPAIR16
Definition pcbnew_id.h:77
@ ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES
Definition pcbnew_id.h:24
@ ID_POPUP_PCB_SELECT_WIDTH16
Definition pcbnew_id.h:40
@ ID_POPUP_PCB_SELECT_AUTO_WIDTH
Definition pcbnew_id.h:23
@ ID_POPUP_PCB_SELECT_CUSTOM_WIDTH
Definition pcbnew_id.h:22
@ ID_POPUP_PCB_SELECT_DIFFPAIR1
Definition pcbnew_id.h:62
@ ID_POPUP_PCB_SELECT_USE_NETCLASS_DIFFPAIR
Definition pcbnew_id.h:61
@ ID_POPUP_PCB_SELECT_VIASIZE1
Definition pcbnew_id.h:41
@ ID_POPUP_PCB_SELECT_CUSTOM_DIFFPAIR
Definition pcbnew_id.h:60
@ ID_POPUP_PCB_SELECT_VIASIZE16
Definition pcbnew_id.h:56
Class that computes missing connections on a PCB.
static const TOOL_ACTION ACT_SwitchCornerModeToNext(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRoundingToNext") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+'/') .FriendlyName(_("Track Corner Mode Switch")) .Tooltip(_("Switches between sharp/rounded and 45°/90° corners when routing tracks.")) .Icon(BITMAPS::switch_corner_rounding_shape))
#define FORMAT_VALUE(x)
static const TOOL_ACTION ACT_SwitchCornerMode45(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRounding45") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+ 'W') .FriendlyName(_("Track Corner Mode 45")) .Tooltip(_("Switch to 45° corner when routing tracks.")))
static const TOOL_ACTION ACT_PlaceBlindVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.PlaceBlindVia") .Scope(AS_CONTEXT) .DefaultHotkey(MD_ALT+MD_SHIFT+ 'V') .LegacyHotkeyName("Add Blind/Buried Via") .FriendlyName(_("Place Blind/Buried Via")) .Tooltip(_("Adds a blind or buried via at the end of currently routed track.")) .Icon(BITMAPS::via_buried) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::BLIND_VIA))
PCB_LAYER_ID ViaStackTargetLayer(PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd, PCB_LAYER_ID aCurrent)
Layer a microvia stack drop lands on when invoked on aCurrent.
static VIATYPE getViaTypeFromFlags(int aFlags)
static const TOOL_ACTION ACT_SwitchCornerModeArc90(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRoundingArc90") .Scope(AS_CONTEXT) .DefaultHotkey(MD_ALT+ 'W') .FriendlyName(_("Track Corner Mode Arc 90")) .Tooltip(_("Switch to arc 90° corner when routing tracks.")))
static const TOOL_ACTION ACT_SwitchCornerMode90(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRounding90") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+MD_ALT+ 'W') .FriendlyName(_("Track Corner Mode 90")) .Tooltip(_("Switch to 90° corner when routing tracks.")))
static const TOOL_ACTION ACT_PlaceMicroVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.PlaceMicroVia") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+ 'V') .LegacyHotkeyName("Add MicroVia") .FriendlyName(_("Place Microvia")) .Tooltip(_("Adds a microvia at the end of currently routed track.")) .Icon(BITMAPS::via_microvia) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::MICROVIA))
static const TOOL_ACTION ACT_PlaceViaStack(.Name("pcbnew.InteractiveRouter.PlaceViaStack") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+MD_SHIFT+ 'V') .FriendlyName(_("Place Microvia Stack at Track End")) .Tooltip(_("Drops the active microvia stack preset at the end of the currently routed track " "and continues on the target layer.")) .Icon(BITMAPS::add_via_stack) .Flags(AF_NONE))
static const TOOL_ACTION ACT_SelLayerAndPlaceBlindVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SelLayerAndPlaceBlindVia") .Scope(AS_CONTEXT) .DefaultHotkey(MD_ALT+'<') .LegacyHotkeyName("Select Layer and Add Blind/Buried Via") .FriendlyName(_("Select Layer and Place Blind/Buried Via...")) .Tooltip(_("Select a layer, then add a blind or buried via at the end of currently routed track.")) .Icon(BITMAPS::select_w_layer) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::BLIND_VIA|VIA_ACTION_FLAGS::SELECT_LAYER))
static const TOOL_ACTION ACT_SwitchPosture(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchPosture") .Scope(AS_CONTEXT) .DefaultHotkey('/') .LegacyHotkeyName("Switch Track Posture") .FriendlyName(_("Switch Track Posture")) .Tooltip(_("Switches posture of the currently routed track.")) .Icon(BITMAPS::change_entry_orient))
VIA_ACTION_FLAGS
Flags used by via tool actions.
@ BLIND_VIA
blind via
@ BURIED_VIA
buried via
@ SELECT_LAYER
Ask user to select layer before adding via.
@ MICROVIA
Microvia.
@ VIA_MASK
@ VIA
Normal via.
const VIA_STACK_PRESET * MatchPendingStackExpansion(PCB_VIA *aVia, const std::set< KIID > &aPreRoute, const std::vector< PENDING_STACK_EXPANSION > &aPending)
Preset a route's stacked drop wants for aVia, or nullptr when the via is not one of them.
static const TOOL_ACTION ACT_SelLayerAndPlaceThroughVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SelLayerAndPlaceVia") .Scope(AS_CONTEXT) .DefaultHotkey('<') .LegacyHotkeyName("Select Layer and Add Through Via") .FriendlyName(_("Select Layer and Place Through Via...")) .Tooltip(_("Select a layer, then add a through-hole via at the end of currently routed track.")) .Icon(BITMAPS::select_w_layer) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::VIA|VIA_ACTION_FLAGS::SELECT_LAYER))
static const TOOL_ACTION ACT_SwitchCornerModeArc45(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRoundingArc45") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+MD_SHIFT+ 'W') .FriendlyName(_("Track Corner Mode Arc 45")) .Tooltip(_("Switch to arc 45° corner when routing tracks.")))
static const TOOL_ACTION ACT_SelLayerAndPlaceMicroVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SelLayerAndPlaceMicroVia") .Scope(AS_CONTEXT) .FriendlyName(_("Select Layer and Place Micro Via...")) .Tooltip(_("Select a layer, then add a micro via at the end of currently routed track.")) .Icon(BITMAPS::select_w_layer) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::MICROVIA|VIA_ACTION_FLAGS::SELECT_LAYER))
static const TOOL_ACTION ACT_PlaceThroughVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.PlaceVia") .Scope(AS_CONTEXT) .DefaultHotkey( 'V') .LegacyHotkeyName("Add Through Via") .FriendlyName(_("Place Through Via")) .Tooltip(_("Adds a through-hole via at the end of currently routed track.")) .Icon(BITMAPS::via) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::VIA))
#define _(s)
static const TOOL_ACTION ACT_CustomTrackWidth(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.CustomTrackViaSize") .Scope(AS_CONTEXT) .DefaultHotkey( 'Q') .LegacyHotkeyName("Custom Track/Via Size") .FriendlyName(_("Custom Track/Via Size...")) .Tooltip(_("Shows a dialog for changing the track width and via size.")) .Icon(BITMAPS::width_track))
PCB_LAYER_ID ViaStackTargetLayer(PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd, PCB_LAYER_ID aCurrent)
Layer a microvia stack drop lands on when invoked on aCurrent.
const VIA_STACK_PRESET * MatchPendingStackExpansion(PCB_VIA *aVia, const std::set< KIID > &aPreRoute, const std::vector< PENDING_STACK_EXPANSION > &aPending)
Preset a route's stacked drop wants for aVia, or nullptr when the via is not one of them.
#define APPEND_UNDO
Definition sch_commit.h:39
std::vector< EDA_ITEM * > EDA_ITEMS
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
Container to handle a stock of specific differential pairs each with unique track width,...
An abstract function object, returning a design rule (clearance, diff pair gap, etc) required between...
Definition pns_node.h:74
wxString m_RuleName
Definition pns_node.h:78
MINOPTMAX< int > m_Value
Definition pns_node.h:76
std::optional< wxString > m_BoardHash
Definition pns_logger.h:101
std::optional< TEST_CASE_TYPE > m_TestCaseType
Definition pns_logger.h:106
std::vector< ITEM * > m_AddedItems
Definition pns_logger.h:102
std::vector< EVENT_ENTRY > m_Events
Definition pns_logger.h:105
std::set< KIID > m_RemovedItems
Definition pns_logger.h:103
std::vector< ITEM * > m_Heads
Definition pns_logger.h:104
Container to handle a stock of specific vias each with unique diameter and drill sizes in the BOARD c...
A named microvia stack definition, chosen while routing instead of entering the values each time.
std::string path
@ AS_CONTEXT
Action belongs to a particular tool (i.e. a part of a pop-up menu)
Definition tool_action.h:43
@ AF_NONE
Definition tool_action.h:51
@ TA_MODEL_CHANGE
Model has changed (partial update).
Definition tool_event.h:117
@ TA_UNDO_REDO_PRE
This event is sent before undo/redo command is performed.
Definition tool_event.h:102
@ TA_UNDO_REDO_POST
This event is sent after undo/redo command is performed.
Definition tool_event.h:105
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:652
@ MD_ALT
Definition tool_event.h:141
@ MD_CTRL
Definition tool_event.h:140
@ MD_SHIFT
Definition tool_event.h:139
@ TC_COMMAND
Definition tool_event.h:53
@ TC_MOUSE
Definition tool_event.h:51
@ TC_VIEW
Definition tool_event.h:55
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707
wxPoint ToWxPoint(const VECTOR2I &aSize)
Definition vector2wx.h:46
wxString AddFileExtListToFilter(const std::vector< std::string > &aExts)
Build the wildcard extension file dialog wildcard filter to add to the base message dialog.
Definition of file extensions used in Kicad.