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_block_dragger.h"
90#include "pns_router.h"
91#include "pns_itemset.h"
92#include "pns_line.h"
93#include "pns_linked_item.h"
94#include "pns_logger.h"
95#include "pns_node.h"
96#include "pns_optimizer.h"
97#include "pns_placement_algo.h"
98#include "pns_segment.h"
99#include "pns_drag_algo.h"
100#include "pns_kicad_iface.h"
101
103
105
106using namespace KIGFX;
107
108
109const VIA_STACK_PRESET* MatchPendingStackExpansion( PCB_VIA* aVia, const std::set<KIID>& aPreRoute,
110 const std::vector<PENDING_STACK_EXPANSION>& aPending )
111{
112 if( aPreRoute.count( aVia->m_Uuid ) )
113 return nullptr;
114
115 for( const PENDING_STACK_EXPANSION& exp : aPending )
116 {
117 // Net and span alone cannot tell the drop's via from any other the route left behind.
118 if( !exp.m_Pos || *exp.m_Pos != aVia->GetPosition() )
119 continue;
120
121 if( aVia->GetNetCode() != exp.m_Net )
122 continue;
123
124 if( ( aVia->TopLayer() == exp.m_Start && aVia->BottomLayer() == exp.m_End )
125 || ( aVia->TopLayer() == exp.m_End && aVia->BottomLayer() == exp.m_Start ) )
126 {
127 return &exp.m_Preset;
128 }
129 }
130
131 return nullptr;
132}
133
134
136{
137 if( aStart == aEnd )
138 return UNDEFINED_LAYER;
139
140 if( aCurrent == aStart )
141 return aEnd;
142
143 // From the end layer the stack goes back the way it came.
144 if( aCurrent == aEnd )
145 return aStart;
146
147 return UNDEFINED_LAYER;
148}
149
150
151namespace
152{
153
154// Saves and restores the global wxUpdateUIEvent interval so it cannot leak on
155// early returns or exceptions.
156class UI_UPDATE_INTERVAL_GUARD
157{
158public:
159 UI_UPDATE_INTERVAL_GUARD( long aNewInterval ) :
160 m_saved( wxUpdateUIEvent::GetUpdateInterval() )
161 {
162 wxUpdateUIEvent::SetUpdateInterval( aNewInterval );
163 }
164
165 ~UI_UPDATE_INTERVAL_GUARD()
166 {
167 wxUpdateUIEvent::SetUpdateInterval( m_saved );
168 }
169
170private:
171 long m_saved;
172};
173
174} // anonymous namespace
175
180{
181 // Via type
182 VIA_MASK = 0x07,
183 VIA = 0x00,
184 BLIND_VIA = 0x01,
185 BURIED_VIA = 0x02,
186 MICROVIA = 0x04,
187
188 // Select layer
190};
191
192
193// Actions, being statically-defined, require specialized I18N handling. We continue to
194// use the _() macro so that string harvesting by the I18N framework doesn't have to be
195// specialized, but we don't translate on initialization and instead do it in the getters.
196
197#undef _
198#define _(s) s
199
200// Pass all the parameters as int to allow combining flags
202 .Name( "pcbnew.InteractiveRouter.PlaceVia" )
203 .Scope( AS_CONTEXT )
204 .DefaultHotkey( 'V' )
205 .LegacyHotkeyName( "Add Through Via" )
206 .FriendlyName( _( "Place Through Via" ) )
207 .Tooltip( _( "Adds a through-hole via at the end of currently routed track." ) )
208 .Icon( BITMAPS::via )
209 .Flags( AF_NONE )
210 .Parameter<int>( VIA_ACTION_FLAGS::VIA ) );
211
213 .Name( "pcbnew.InteractiveRouter.PlaceBlindVia" )
214 .Scope( AS_CONTEXT )
215 .DefaultHotkey( MD_ALT + MD_SHIFT + 'V' )
216 .LegacyHotkeyName( "Add Blind/Buried Via" )
217 .FriendlyName( _( "Place Blind/Buried Via" ) )
218 .Tooltip( _( "Adds a blind or buried via at the end of currently routed track.") )
219 .Icon( BITMAPS::via_buried )
220 .Flags( AF_NONE )
221 .Parameter<int>( VIA_ACTION_FLAGS::BLIND_VIA ) );
222
224 .Name( "pcbnew.InteractiveRouter.PlaceMicroVia" )
225 .Scope( AS_CONTEXT )
226 .DefaultHotkey( MD_CTRL + 'V' )
227 .LegacyHotkeyName( "Add MicroVia" )
228 .FriendlyName( _( "Place Microvia" ) )
229 .Tooltip( _( "Adds a microvia at the end of currently routed track." ) )
230 .Icon( BITMAPS::via_microvia )
231 .Flags( AF_NONE )
232 .Parameter<int>( VIA_ACTION_FLAGS::MICROVIA ) );
233
236 .Name( "pcbnew.InteractiveRouter.PlaceViaStack" )
237 .Scope( AS_CONTEXT )
238 .DefaultHotkey( MD_CTRL + MD_SHIFT + 'V' )
239 .FriendlyName( _( "Place Microvia Stack at Track End" ) )
240 .Tooltip( _( "Drops the active microvia stack preset at the end of the currently routed track "
241 "and continues on the target layer." ) )
243 .Flags( AF_NONE ) );
244
246 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceVia" )
247 .Scope( AS_CONTEXT )
248 .DefaultHotkey( '<' )
249 .LegacyHotkeyName( "Select Layer and Add Through Via" )
250 .FriendlyName( _( "Select Layer and Place Through Via..." ) )
251 .Tooltip( _( "Select a layer, then add a through-hole via at the end of currently routed track." ) )
253 .Flags( AF_NONE )
255
257 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceBlindVia" )
258 .Scope( AS_CONTEXT )
259 .DefaultHotkey( MD_ALT + '<' )
260 .LegacyHotkeyName( "Select Layer and Add Blind/Buried Via" )
261 .FriendlyName( _( "Select Layer and Place Blind/Buried Via..." ) )
262 .Tooltip( _( "Select a layer, then add a blind or buried via at the end of currently routed track." ) )
264 .Flags( AF_NONE )
266
268 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceMicroVia" )
269 .Scope( AS_CONTEXT )
270 .FriendlyName( _( "Select Layer and Place Micro Via..." ) )
271 .Tooltip( _( "Select a layer, then add a micro via at the end of currently routed track." ) )
273 .Flags( AF_NONE )
275
277 .Name( "pcbnew.InteractiveRouter.CustomTrackViaSize" )
278 .Scope( AS_CONTEXT )
279 .DefaultHotkey( 'Q' )
280 .LegacyHotkeyName( "Custom Track/Via Size" )
281 .FriendlyName( _( "Custom Track/Via Size..." ) )
282 .Tooltip( _( "Shows a dialog for changing the track width and via size." ) )
283 .Icon( BITMAPS::width_track ) );
284
286 .Name( "pcbnew.InteractiveRouter.SwitchPosture" )
287 .Scope( AS_CONTEXT )
288 .DefaultHotkey( '/' )
289 .LegacyHotkeyName( "Switch Track Posture" )
290 .FriendlyName( _( "Switch Track Posture" ) )
291 .Tooltip( _( "Switches posture of the currently routed track." ) )
293
294 // This old command ( track corner switch mode) is now moved to a submenu with other corner mode options
296 .Name( "pcbnew.InteractiveRouter.SwitchRoundingToNext" )
297 .Scope( AS_CONTEXT )
298 .DefaultHotkey( MD_CTRL + '/' )
299 .FriendlyName( _( "Track Corner Mode Switch" ) )
300 .Tooltip( _( "Switches between sharp/rounded and 45°/90° corners when routing tracks." ) )
302
303// hotkeys W and Shift+W are used to switch to track width changes
305 .Name( "pcbnew.InteractiveRouter.SwitchRounding45" )
306 .Scope( AS_CONTEXT )
307 .DefaultHotkey( MD_CTRL + 'W' )
308 .FriendlyName( _( "Track Corner Mode 45" ) )
309 .Tooltip( _( "Switch to 45° corner when routing tracks." ) ) );
310
312 .Name( "pcbnew.InteractiveRouter.SwitchRounding90" )
313 .Scope( AS_CONTEXT )
314 .DefaultHotkey( MD_CTRL + MD_ALT + 'W' )
315 .FriendlyName( _( "Track Corner Mode 90" ) )
316 .Tooltip( _( "Switch to 90° corner when routing tracks." ) ) );
317
319 .Name( "pcbnew.InteractiveRouter.SwitchRoundingArc45" )
320 .Scope( AS_CONTEXT )
321 .DefaultHotkey( MD_CTRL + MD_SHIFT + 'W' )
322 .FriendlyName( _( "Track Corner Mode Arc 45" ) )
323 .Tooltip( _( "Switch to arc 45° corner when routing tracks." ) ) );
324
326 .Name( "pcbnew.InteractiveRouter.SwitchRoundingArc90" )
327 .Scope( AS_CONTEXT )
328 .DefaultHotkey( MD_ALT + 'W' )
329 .FriendlyName( _( "Track Corner Mode Arc 90" ) )
330 .Tooltip( _( "Switch to arc 90° corner when routing tracks." ) ) );
331
332#undef _
333#define _(s) wxGetTranslation((s))
334
335
337 TOOL_BASE( "pcbnew.InteractiveRouter" ),
341 m_inRouterTool( false ),
342 m_inRouteSelected( false ),
343 m_startWithVia( false )
344{
345}
346
347
349{
350public:
352 ACTION_MENU( true ),
353 m_frame( aFrame )
354 {
356 SetTitle( _( "Select Track/Via Width" ) );
357 }
358
359protected:
360 ACTION_MENU* create() const override
361 {
362 return new TRACK_WIDTH_MENU( m_frame );
363 }
364
365 void update() override
366 {
367 BOARD_DESIGN_SETTINGS& bds = m_frame.GetBoard()->GetDesignSettings();
368 bool useIndex = !bds.m_UseConnectedTrackWidth && !bds.UseCustomTrackViaSize();
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, targetLayer );
1075
1076 if( !constraint.IsNull() )
1077 {
1078 int width = sizes.TrackWidth();
1079
1080 // Only change the size if we're explicitly using the net class, or we're out of range
1081 // for our new constraints. Otherwise, just leave the track width alone so we don't
1082 // change for no reason.
1083 if( bds.UseNetClassTrack()
1084 || ( width < bds.m_TrackMinWidth )
1085 || ( width < constraint.m_Value.Min() )
1086 || ( width > constraint.m_Value.Max() ) )
1087 {
1088 sizes.SetTrackWidth( std::max( bds.m_TrackMinWidth, constraint.m_Value.PinnedOpt() ) );
1089 }
1090
1091 if( sizes.TrackWidth() == constraint.m_Value.PinnedOpt() )
1092 sizes.SetWidthSource( constraint.GetName() );
1093 else if( sizes.TrackWidth() == bds.m_TrackMinWidth )
1094 sizes.SetWidthSource( _( "board minimum track width" ) );
1095 else
1096 sizes.SetWidthSource( _( "existing track" ) );
1097 }
1098 }
1099
1100 if( nets.size() >= 2 && ( bds.UseNetClassDiffPair() || !sizes.TrackWidthIsExplicit() ) )
1101 {
1102 PCB_TRACK dummyTrackB( board() );
1103 dummyTrackB.SetFlags( ROUTER_TRANSIENT );
1104 dummyTrackB.SetLayer( targetLayer );
1105 dummyTrackB.SetNet( static_cast<NETINFO_ITEM*>( nets[1] ) );
1106 dummyTrackB.SetStart( aPos );
1107 dummyTrackB.SetEnd( dummyTrackB.GetStart() );
1108
1109 constraint = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, &dummyTrack, &dummyTrackB, targetLayer );
1110
1111 if( !constraint.IsNull() )
1112 {
1113 if( bds.UseNetClassDiffPair()
1114 || ( sizes.DiffPairWidth() < bds.m_TrackMinWidth )
1115 || ( sizes.DiffPairWidth() < constraint.m_Value.Min() )
1116 || ( sizes.DiffPairWidth() > constraint.m_Value.Max() ) )
1117 {
1118 sizes.SetDiffPairWidth( std::max( bds.m_TrackMinWidth, constraint.m_Value.PinnedOpt() ) );
1119 }
1120
1121 if( sizes.DiffPairWidth() == constraint.m_Value.PinnedOpt() )
1122 sizes.SetDiffPairWidthSource( constraint.GetName() );
1123 else
1124 sizes.SetDiffPairWidthSource( _( "board minimum track width" ) );
1125 }
1126
1127 constraint = drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, &dummyTrack, &dummyTrackB, targetLayer );
1128
1129 if( !constraint.IsNull() )
1130 {
1131 if( bds.UseNetClassDiffPair()
1132 || ( sizes.DiffPairGap() < bds.m_MinClearance )
1133 || ( sizes.DiffPairGap() < constraint.m_Value.Min() )
1134 || ( sizes.DiffPairGap() > constraint.m_Value.Max() ) )
1135 {
1136 sizes.SetDiffPairGap( std::max( bds.m_MinClearance, constraint.m_Value.PinnedOpt() ) );
1137 }
1138
1139 if( sizes.DiffPairGap() == constraint.m_Value.PinnedOpt() )
1140 sizes.SetDiffPairGapSource( constraint.GetName() );
1141 else
1142 sizes.SetDiffPairGapSource( _( "board minimum clearance" ) );
1143 }
1144 }
1145
1146 m_router->UpdateSizes( sizes );
1147}
1148
1149
1150static VIATYPE getViaTypeFromFlags( int aFlags )
1151{
1152 switch( aFlags & VIA_ACTION_FLAGS::VIA_MASK )
1153 {
1155 return VIATYPE::THROUGH;
1157 return VIATYPE::BLIND;
1159 return VIATYPE::BURIED;
1161 return VIATYPE::MICROVIA;
1162 default:
1163 wxASSERT_MSG( false, wxT( "Unhandled via type" ) );
1164 return VIATYPE::THROUGH;
1165 }
1166}
1167
1168
1170{
1171 handleLayerSwitch( aEvent, false );
1173
1174 return 0;
1175}
1176
1177
1179{
1180 if( !m_router->IsPlacingVia() )
1181 {
1182 return handleLayerSwitch( aEvent, true );
1183 }
1184 else
1185 {
1186 m_router->ToggleViaPlacement();
1187 frame()->SetActiveLayer( m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() ) );
1188 updateEndItem( aEvent );
1190 }
1191
1193 return 0;
1194}
1195
1196
1198{
1199 if( !IsToolActive() )
1200 return 0;
1201
1202 if( !m_router->RoutingInProgress() || !m_router->Placer() )
1203 return 0;
1204
1205 m_iface->SetBoard( board() );
1206
1208 const std::vector<VIA_STACK_PRESET>& presets = bds.m_ViaStackPresets;
1209
1210 if( presets.empty() )
1211 {
1212 frame()->GetInfoBar()->ShowMessageFor( _( "No microvia stack presets defined. Add one in Board Setup, "
1213 "Microvia Stacks." ),
1214 3000, wxICON_INFORMATION );
1215 return 0;
1216 }
1217
1218 int idx = std::clamp( bds.GetViaStackIndex(), 0, (int) presets.size() - 1 );
1219 const VIA_STACK_PRESET& preset = presets[idx];
1220
1221 PCB_LAYER_ID currentLayer = m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() );
1222
1223 if( currentLayer == UNDEFINED_LAYER )
1224 return 0;
1225
1226 const LSET enabled = board()->GetEnabledLayers();
1227
1228 if( !enabled.Contains( preset.m_StartLayer ) || !enabled.Contains( preset.m_EndLayer ) )
1229 {
1230 frame()->GetInfoBar()->ShowMessageFor( _( "The microvia stack preset layers are not present on this board." ),
1231 3000, wxICON_ERROR );
1232 return 0;
1233 }
1234
1235 PCB_LAYER_ID targetLayer = ViaStackTargetLayer( preset.m_StartLayer, preset.m_EndLayer, currentLayer );
1236
1237 if( targetLayer == UNDEFINED_LAYER )
1238 {
1239 frame()->GetInfoBar()->ShowMessageFor( wxString::Format( _( "The microvia stack runs between %s and %s. "
1240 "Route on one of those layers to place it." ),
1241 board()->GetLayerName( preset.m_StartLayer ),
1242 board()->GetLayerName( preset.m_EndLayer ) ),
1243 3000, wxICON_ERROR );
1244 return 0;
1245 }
1246
1247 if( preset.m_Staggered )
1248 {
1249 if( m_router->IsPlacingVia() )
1250 m_router->ToggleViaPlacement();
1251
1252 // The router cannot route through a staggered stack (lateral walk + connecting traces).
1253 // Fix the track here and REMEMBER the stack, but build it only after routing tears down.
1254 // Committing to the board while the PNS world is live invalidates its nodes (crash).
1255 VECTOR2I head = m_endSnapPoint;
1256 const PNS::ITEM_SET traces = m_router->Placer()->Traces();
1257
1258 if( traces.Size() > 0 )
1259 {
1260 if( PNS::LINE* line = dynamic_cast<PNS::LINE*>( traces[0] ) )
1261 {
1262 if( line->PointCount() > 0 )
1263 head = line->CLine().CLastPoint();
1264 }
1265 }
1266
1267 if( !m_router->FixRoute( head, m_endItem, true, false ) )
1268 {
1269 frame()->GetInfoBar()->ShowMessageFor( _( "Could not end the track here for a microvia stack." ), 3000,
1270 wxICON_ERROR );
1272 return 0;
1273 }
1274
1275 m_pendingViaStack = true;
1276 m_pendingStackHead = head;
1277 m_pendingStackStart = currentLayer;
1278 m_pendingStackEnd = targetLayer;
1279
1281 return 0;
1282 }
1283
1284 // A via the route places with no net reports UNCONNECTED, not ORPHANED.
1285 int net = NETINFO_LIST::UNCONNECTED;
1286
1287 if( !m_router->GetCurrentNets().empty() )
1288 {
1289 if( NETINFO_ITEM* ni = static_cast<NETINFO_ITEM*>( m_router->GetCurrentNets()[0] ) )
1290 net = ni->GetNetCode();
1291 }
1292
1293 int viaSize;
1294 int viaDrill;
1295
1296 if( preset.m_UseNetclass )
1297 {
1298 NETINFO_ITEM* ni = board()->FindNet( net );
1299 NETCLASS* nc = ni ? ni->GetNetClass() : nullptr;
1300
1301 viaSize = ( nc && nc->HasuViaDiameter() ) ? nc->GetuViaDiameter() : bds.GetCurrentViaSize();
1302 viaDrill = ( nc && nc->HasuViaDrill() ) ? nc->GetuViaDrill() : bds.GetCurrentViaDrill();
1303
1304 // Route with the size the stack's hops will resolve to from the via rules
1305 PCB_VIA dummyVia( board() );
1306 dummyVia.SetFlags( ROUTER_TRANSIENT );
1307 dummyVia.SetViaType( VIATYPE::MICROVIA );
1308 dummyVia.SetLayerPair( currentLayer, targetLayer );
1309 dummyVia.SetNetCode( net );
1310 dummyVia.SetPosition( m_endSnapPoint );
1311 dummyVia.SetSizeFromRules( viaSize, viaDrill );
1312
1313 viaSize = dummyVia.GetWidth( PADSTACK::ALL_LAYERS );
1314 viaDrill = dummyVia.GetDrillValue();
1315 }
1316 else
1317 {
1318 viaSize = preset.m_ViaSize > 0 ? preset.m_ViaSize : bds.GetCurrentViaSize();
1319 viaDrill = preset.m_ViaDrill > 0 ? preset.m_ViaDrill : bds.GetCurrentViaDrill();
1320 }
1321
1322 // Routing to a non-adjacent layer leaves one multi-hop microvia that finishInteractive()
1323 // expands into a stack.
1324 if( m_pendingStackedExpansions.empty() )
1325 {
1327 }
1328
1329 m_pendingStackedExpansions.push_back( { currentLayer, targetLayer, net, preset, std::nullopt } );
1331
1332 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1333 sizes.ClearLayerPairs();
1334
1335 sizes.SetViaDiameter( viaSize );
1336 sizes.SetViaDrill( viaDrill );
1338 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( currentLayer ),
1339 m_iface->GetPNSLayerFromBoardLayer( targetLayer ) );
1340
1341 m_router->UpdateSizes( sizes );
1342
1343 if( !m_router->IsPlacingVia() )
1344 m_router->ToggleViaPlacement();
1345
1346 if( m_router->RoutingInProgress() )
1347 {
1348 updateEndItem( aEvent );
1350 }
1351
1353 return 0;
1354}
1355
1356
1358{
1359 if( !m_stackDropAwaitingVia || !m_router->IsPlacingVia() )
1360 return;
1361
1362 const PNS::ITEM_SET traces = m_router->Placer()->Traces();
1363
1364 if( traces.Size() == 0 )
1365 return;
1366
1367 // The via lands where the placer's trace ends, which is not where the cursor is once
1368 // the router has walked around or shoved.
1369 if( PNS::LINE* line = dynamic_cast<PNS::LINE*>( traces[0] ) )
1370 {
1371 if( line->EndsWithVia() )
1372 m_pendingStackedExpansions.back().m_Pos = line->Via().Pos();
1373 }
1374}
1375
1376
1378{
1379 m_pendingViaStack = false;
1380
1381 // Hand off to the interactive microvia stack placement, pre-anchored at the routed head, so
1382 // the user steers each staggered hop exactly like free-standing placement. Running this only
1383 // after the route has fully torn down keeps the board edit clear of the PNS world.
1384 if( DRAWING_TOOL* drawingTool = m_toolMgr->GetTool<DRAWING_TOOL>() )
1385 {
1386 drawingTool->SeedViaStackStart( m_pendingStackHead, m_pendingStackStart, m_pendingStackEnd );
1388 }
1389}
1390
1391
1392int ROUTER_TOOL::handleLayerSwitch( const TOOL_EVENT& aEvent, bool aForceVia )
1393{
1394 wxCHECK( m_router, 0 );
1395
1396 if( !IsToolActive() )
1397 return 0;
1398
1399 // Ensure PNS_KICAD_IFACE (m_iface) m_board member is up to date
1400 // For some reason, this is not always the case
1401 m_iface->SetBoard( board() );
1402
1403 // First see if this is one of the switch layer commands
1404 BOARD* brd = board();
1405 LSET enabledLayers = LSET::AllCuMask( brd->GetDesignSettings().GetCopperLayerCount() );
1406 LSEQ layers = enabledLayers.UIOrder();
1407
1408 // These layers are in Board Layer UI order not PNS layer order
1409 PCB_LAYER_ID currentLayer = m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() );
1410 PCB_LAYER_ID targetLayer = UNDEFINED_LAYER;
1411
1412 if( aEvent.IsAction( &PCB_ACTIONS::layerNext ) )
1413 {
1414 size_t idx = 0;
1415 size_t target_idx = 0;
1416
1417 for( size_t i = 0; i < layers.size(); i++ )
1418 {
1419 if( layers[i] == currentLayer )
1420 {
1421 idx = i;
1422 break;
1423 }
1424 }
1425
1426 target_idx = ( idx + 1 ) % layers.size();
1427 // issue: #14480
1428 // idx + 1 layer may be invisible, switches to next visible layer
1429 for( size_t i = 0; i < layers.size() - 1; i++ )
1430 {
1431 if( brd->IsLayerVisible( layers[target_idx] ) )
1432 {
1433 targetLayer = layers[target_idx];
1434 break;
1435 }
1436 target_idx += 1;
1437
1438 if( target_idx >= layers.size() )
1439 {
1440 target_idx = 0;
1441 }
1442 }
1443
1444 if( targetLayer == UNDEFINED_LAYER )
1445 {
1446 // if there is no visible layers
1447 return 0;
1448 }
1449 }
1450 else if( aEvent.IsAction( &PCB_ACTIONS::layerPrev ) )
1451 {
1452 size_t idx = 0;
1453 size_t target_idx = 0;
1454
1455 for( size_t i = 0; i < layers.size(); i++ )
1456 {
1457 if( layers[i] == currentLayer )
1458 {
1459 idx = i;
1460 break;
1461 }
1462 }
1463
1464 target_idx = ( idx > 0 ) ? ( idx - 1 ) : ( layers.size() - 1 );
1465
1466 for( size_t i = 0; i < layers.size() - 1; i++ )
1467 {
1468 if( brd->IsLayerVisible( layers[target_idx] ) )
1469 {
1470 targetLayer = layers[target_idx];
1471 break;
1472 }
1473
1474 if( target_idx > 0 )
1475 target_idx -= 1;
1476 else
1477 target_idx = layers.size() - 1;
1478 }
1479
1480 if( targetLayer == UNDEFINED_LAYER )
1481 {
1482 // if there is no visible layers
1483 return 0;
1484 }
1485 }
1486 else if( aEvent.IsAction( &PCB_ACTIONS::layerToggle ) )
1487 {
1488 PCB_SCREEN* screen = frame()->GetScreen();
1489
1490 if( currentLayer == screen->m_Route_Layer_TOP )
1491 targetLayer = screen->m_Route_Layer_BOTTOM;
1492 else
1493 targetLayer = screen->m_Route_Layer_TOP;
1494 }
1496 {
1497 targetLayer = aEvent.Parameter<PCB_LAYER_ID>();
1498
1499 if( !enabledLayers.test( targetLayer ) )
1500 return 0;
1501 }
1502
1503 if( targetLayer != UNDEFINED_LAYER )
1504 {
1505 if( targetLayer == currentLayer )
1506 return 0;
1507
1508 if( !aForceVia && m_router && m_router->SwitchLayer( m_iface->GetPNSLayerFromBoardLayer( targetLayer ) ) )
1509 {
1510 updateEndItem( aEvent );
1511 updateSizesAfterRouterEvent( m_iface->GetPNSLayerFromBoardLayer( targetLayer ), m_endSnapPoint );
1512 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
1513 return 0;
1514 }
1515 }
1516
1518
1519 PCB_LAYER_ID pairTop = frame()->GetScreen()->m_Route_Layer_TOP;
1520 PCB_LAYER_ID pairBottom = frame()->GetScreen()->m_Route_Layer_BOTTOM;
1521
1522 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1523
1524 VIATYPE viaType = VIATYPE::THROUGH;
1525 bool selectLayer = false;
1526
1527 // Otherwise it is one of the router-specific via commands
1528 if( targetLayer == UNDEFINED_LAYER )
1529 {
1530 const int actViaFlags = aEvent.Parameter<int>();
1531 selectLayer = actViaFlags & VIA_ACTION_FLAGS::SELECT_LAYER;
1532
1533 viaType = getViaTypeFromFlags( actViaFlags );
1534
1535 // ask the user for a target layer
1536 if( selectLayer )
1537 {
1538 // When the currentLayer is undefined, trying to place a via does not work
1539 // because it means there is no track in progress, and some other variables
1540 // values are not defined like m_endSnapPoint. So do not continue.
1541 if( currentLayer == UNDEFINED_LAYER )
1542 return 0;
1543
1544 wxPoint endPoint = ToWxPoint( view()->ToScreen( m_endSnapPoint ) );
1545 endPoint = frame()->GetCanvas()->ClientToScreen( endPoint );
1546
1547 // Build the list of not allowed layer for the target layer
1548 LSET not_allowed_ly = LSET::AllNonCuMask();
1549
1550 if( viaType != VIATYPE::THROUGH )
1551 not_allowed_ly.set( currentLayer );
1552
1553 targetLayer = frame()->SelectOneLayer( static_cast<PCB_LAYER_ID>( currentLayer ),
1554 not_allowed_ly, endPoint );
1555
1556 // Reset the cursor to the end of the track
1558
1559 if( targetLayer == UNDEFINED_LAYER ) // canceled by user
1560 return 0;
1561
1562 // One cannot place a blind/buried via on only one layer:
1563 if( viaType != VIATYPE::THROUGH )
1564 {
1565 if( currentLayer == targetLayer )
1566 return 0;
1567 }
1568 }
1569 }
1570
1571 // fixme: P&S supports more than one fixed layer pair. Update the dialog?
1572 sizes.ClearLayerPairs();
1573
1574 // Convert blind/buried via to a through hole one, if it goes through all layers
1575 if( viaType != VIATYPE::THROUGH
1576 && ( ( targetLayer == B_Cu && currentLayer == F_Cu )
1577 || ( targetLayer == F_Cu && currentLayer == B_Cu ) ) )
1578 {
1579 viaType = VIATYPE::THROUGH;
1580 }
1581
1582 if( targetLayer == UNDEFINED_LAYER )
1583 {
1584 // Implicit layer selection
1585 if( viaType == VIATYPE::THROUGH )
1586 {
1587 // Try to switch to the nearest ratnest item's layer if we have one
1588 VECTOR2I otherEnd;
1589 PNS_LAYER_RANGE otherEndLayers;
1590 PNS::ITEM* otherEndItem = nullptr;
1591
1592 if( !m_router->GetNearestRatnestAnchor( otherEnd, otherEndLayers, otherEndItem ) )
1593 {
1594 // use the default layer pair
1595 currentLayer = pairTop;
1596 targetLayer = pairBottom;
1597 }
1598 else
1599 {
1600 // use the layer of the other end, unless it is the same layer as the currently active layer, in which
1601 // case use the layer pair (if applicable)
1602 PCB_LAYER_ID otherEndLayerPcbId = m_iface->GetBoardLayerFromPNSLayer( otherEndLayers.Start() );
1603 const std::optional<int> pairedLayerPns = m_router->Sizes().PairedLayer( m_router->GetCurrentLayer() );
1604
1605 const PNS_LAYER_RANGE allCopperLayers( m_iface->GetPNSLayerFromBoardLayer( F_Cu ),
1606 m_iface->GetPNSLayerFromBoardLayer( B_Cu ) );
1607
1608 // A through anchor connects on every copper layer, so it names no single target.
1609 // Test the hole rather than the copper range, which segmented padstacks
1610 // (FRONT_INNER_BACK, custom) can report as a single layer.
1611 const bool otherEndIsThrough =
1612 otherEndLayers == allCopperLayers
1613 || ( otherEndItem && otherEndItem->HasHole()
1614 && otherEndItem->Hole()->Layers() == allCopperLayers );
1615
1616 if( otherEndIsThrough )
1617 {
1618 // Honour the user's layer pair; the anchor span start is always the top
1619 // copper layer and would ignore it. Constrained anchors fall through below.
1620 if( currentLayer == pairBottom )
1621 targetLayer = pairTop;
1622 else if( currentLayer == pairTop )
1623 targetLayer = pairBottom;
1624 else
1625 targetLayer = pairTop;
1626 }
1627 else if( currentLayer == otherEndLayerPcbId && pairedLayerPns.has_value() )
1628 {
1629 // Closest ratsnest layer is the same as the active layer - assume the via is being placed for
1630 // other routing reasons and switch the layer
1631 targetLayer = m_iface->GetBoardLayerFromPNSLayer( *pairedLayerPns );
1632 }
1633 else
1634 {
1635 targetLayer = m_iface->GetBoardLayerFromPNSLayer( otherEndLayers.Start() );
1636 }
1637 }
1638 }
1639 else
1640 {
1641 if( currentLayer == pairTop || currentLayer == pairBottom )
1642 {
1643 // the current layer is on the defined layer pair,
1644 // swap to the other side
1645 currentLayer = pairTop;
1646 targetLayer = pairBottom;
1647 }
1648 else
1649 {
1650 // the current layer is not part of the current layer pair,
1651 // so fallback and swap to the top layer of the pair by default
1652 targetLayer = pairTop;
1653 }
1654
1655 // Do not create a broken via (i.e. a via on only one copper layer)
1656 if( currentLayer == targetLayer )
1657 {
1658 WX_INFOBAR* infobar = frame()->GetInfoBar();
1659 infobar->ShowMessageFor( _( "Via needs 2 different layers." ), 5000, wxICON_ERROR,
1660 WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION );
1661 return 0;
1662 }
1663 }
1664 }
1665
1666 sizes.SetViaDiameter( bds.m_ViasMinSize );
1667 sizes.SetViaDrill( bds.m_MinThroughDrill );
1668
1669 if( bds.UseNetClassVia() || viaType == VIATYPE::MICROVIA )
1670 {
1671 PCB_VIA dummyVia( board() );
1672 dummyVia.SetViaType( viaType );
1673 dummyVia.SetLayerPair( currentLayer, targetLayer );
1674
1675 if( !m_router->GetCurrentNets().empty() )
1676 dummyVia.SetNet( static_cast<NETINFO_ITEM*>( m_router->GetCurrentNets()[0] ) );
1677
1678 DRC_CONSTRAINT constraint;
1679
1680 constraint = bds.m_DRCEngine->EvalRules( VIA_DIAMETER_CONSTRAINT, &dummyVia, nullptr, currentLayer );
1681
1682 if( !constraint.IsNull() )
1683 sizes.SetViaDiameter( constraint.m_Value.PinnedOpt() );
1684
1685 constraint = bds.m_DRCEngine->EvalRules( HOLE_SIZE_CONSTRAINT, &dummyVia, nullptr, currentLayer );
1686
1687 if( !constraint.IsNull() )
1688 sizes.SetViaDrill( constraint.m_Value.PinnedOpt() );
1689 }
1690 else
1691 {
1692 sizes.SetViaDiameter( bds.GetCurrentViaSize() );
1693 sizes.SetViaDrill( bds.GetCurrentViaDrill() );
1694 }
1695
1696 sizes.SetViaType( viaType );
1697 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( currentLayer ),
1698 m_iface->GetPNSLayerFromBoardLayer( targetLayer ) );
1699
1700 m_router->UpdateSizes( sizes );
1701
1702 if( !m_router->IsPlacingVia() )
1703 m_router->ToggleViaPlacement();
1704
1705 if( m_router->RoutingInProgress() )
1706 {
1707 updateEndItem( aEvent );
1709 }
1710 else
1711 {
1712 updateStartItem( aEvent );
1713 }
1714
1715 return 0;
1716}
1717
1718
1720{
1723
1724 // The stack handoff names the resume layer so snapping does not guess it from nearby copper.
1726 {
1727 pcbLayer = m_viaStackResumeLayer;
1729 }
1730
1731 int pnsLayer = m_iface->GetPNSLayerFromBoardLayer( pcbLayer );
1732
1733 if( !::IsCopperLayer( pcbLayer ) )
1734 {
1735 editFrame->ShowInfoBarError( _( "Tracks on Copper layers only." ) );
1736 return false;
1737 }
1738
1740 editFrame->SetActiveLayer( pcbLayer );
1741
1742 if( !getView()->IsLayerVisible( pcbLayer ) )
1743 {
1744 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1745 editFrame->GetCanvas()->Refresh();
1746 }
1747
1748 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
1749
1750 m_iface->SetStartLayerFromPCBNew( pcbLayer );
1751
1752 frame()->GetBoard()->GetDesignSettings().m_TempOverrideTrackWidth = false;
1753 m_iface->ImportSizes( sizes, m_startItem, nullptr, aStartPosition );
1754 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_TOP ),
1755 m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_BOTTOM ) );
1756
1757 m_router->UpdateSizes( sizes );
1758
1759 if( m_startItem && m_startItem->Net() )
1760 {
1762 {
1763 if( PNS::NET_HANDLE coupledNet = m_router->GetRuleResolver()->DpCoupledNet( m_startItem->Net() ) )
1764 highlightNets( true, { m_startItem->Net(), coupledNet } );
1765 }
1766 else
1767 {
1768 highlightNets( true, { m_startItem->Net() } );
1769 }
1770 }
1771
1772 controls()->SetAutoPan( true );
1773
1774 if( !m_router->StartRouting( m_startSnapPoint, m_startItem, pnsLayer ) )
1775 {
1776 // It would make more sense to leave the net highlighted as the higher-contrast mode
1777 // makes the router clearances more visible. However, since we just started routing
1778 // the conversion of the screen from low contrast to high contrast is a bit jarring and
1779 // makes the infobar coming up less noticeable.
1780 highlightNets( false );
1781
1782 frame()->ShowInfoBarError( m_router->FailureReason(), true,
1783 [&]()
1784 {
1785 m_router->ClearViewDecorations();
1786 } );
1787
1788 controls()->SetAutoPan( false );
1789 return false;
1790 }
1791
1792 m_endItem = nullptr;
1794
1796 frame()->UndoRedoBlock( true );
1797
1798 return true;
1799}
1800
1801
1803{
1804 m_router->StopRouting();
1805
1806 m_stackDropAwaitingVia = false;
1807
1808 if( !m_pendingStackedExpansions.empty() )
1809 {
1810 BOARD_COMMIT commit( frame() );
1811
1812 auto matcher = [&]( PCB_VIA* aVia ) -> const VIA_STACK_PRESET*
1813 {
1815 };
1816
1817 if( PCB_VIA_STACK::ExpandMultiHopMicrovias( board(), &commit, matcher ) > 0 )
1818 commit.Push( _( "Expand Microvia Stacks" ), APPEND_UNDO );
1819
1822 }
1823
1824 m_startItem = nullptr;
1825 m_endItem = nullptr;
1826
1827 frame()->SetActiveLayer( m_originalActiveLayer );
1829 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1830 controls()->SetAutoPan( false );
1831 controls()->ForceCursorPosition( false );
1832 frame()->UndoRedoBlock( false );
1833 highlightNets( false );
1834
1835 return true;
1836}
1837
1838
1840{
1841 m_router->ClearViewDecorations();
1842
1843 bool startWithVia = std::exchange( m_startWithVia, false );
1844
1845 if( !prepareInteractive( aStartPosition ) )
1846 return;
1847
1848 auto setCursor =
1849 [&]()
1850 {
1851 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1852 };
1853
1854 auto syncRouterAndFrameLayer =
1855 [&]()
1856 {
1857 int pnsLayer = m_router->GetCurrentLayer();
1858 PCB_LAYER_ID pcbLayer = m_iface->GetBoardLayerFromPNSLayer( pnsLayer );
1860
1861 editFrame->SetActiveLayer( pcbLayer );
1862
1863 if( !getView()->IsLayerVisible( pcbLayer ) )
1864 {
1865 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1866 editFrame->GetCanvas()->Refresh();
1867 }
1868 };
1869
1870 // Set initial cursor
1871 setCursor();
1872
1873 // A via or through pad already reaching the layer 'V' switched to must not gain a second via
1874 int viaTargetLayer = m_iface->GetPNSLayerFromBoardLayer( m_originalActiveLayer );
1875 bool startReachesViaTarget = m_startItem && m_startItem->Layers().Overlaps( viaTargetLayer );
1876
1877 // If the user pressed 'V' before starting to route, enable via placement now
1878 if( startWithVia && !startReachesViaTarget )
1879 {
1880 handleLayerSwitch( ACT_PlaceThroughVia.MakeEvent(), true );
1881 }
1882
1883 // Throttle wxEVT_UPDATE_UI during routing. The idle sweep fires between every Wait()
1884 // iteration and its cost dominates at interactive frame rates.
1885 UI_UPDATE_INTERVAL_GUARD uiGuard( 200 );
1886
1887 while( TOOL_EVENT* evt = Wait() )
1888 {
1889 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Catch any visibility
1890 // changes while routing.
1891 m_toolMgr->GetView()->SyncLayerVisibilityCache();
1892
1893 setCursor();
1894
1895 // Don't crash if we missed an operation that canceled routing.
1896 if( !m_router->RoutingInProgress() )
1897 {
1898 if( evt->IsCancelInteractive() )
1899 m_cancelled = true;
1900
1901 break;
1902 }
1903
1904 handleCommonEvents( *evt );
1905
1906 if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
1907 {
1908 updateEndItem( *evt );
1910 }
1911 else if( evt->IsAction( &PCB_ACTIONS::routerUndoLastSegment )
1912 || evt->IsAction( &ACTIONS::doDelete )
1913 || evt->IsAction( &ACTIONS::undo ) )
1914 {
1915 if( std::optional<VECTOR2I> last = m_router->UndoLastSegment() )
1916 {
1917 getViewControls()->WarpMouseCursor( last.value(), true );
1918 evt->SetMousePosition( last.value() );
1919 }
1920
1921 updateEndItem( *evt );
1923 }
1924 else if( evt->IsAction( &PCB_ACTIONS::routerAttemptFinish ) )
1925 {
1926 if( m_toolMgr->IsContextMenuActive() )
1927 m_toolMgr->WarpAfterContextMenu();
1928
1929 bool* autoRouted = evt->Parameter<bool*>();
1930
1931 if( m_router->Finish() )
1932 {
1933 // When we're routing a group of signals automatically we want
1934 // to break up the undo stack every time we have to manually route
1935 // so the user gets nice checkpoints. Remove the APPEND_UNDO flag.
1936 if( autoRouted != nullptr )
1937 *autoRouted = true;
1938
1939 break;
1940 }
1941 else
1942 {
1943 // This acts as check if we were called by the autorouter; we don't want
1944 // to reset APPEND_UNDO if we're auto finishing after route-other-end
1945 if( autoRouted != nullptr )
1946 {
1947 *autoRouted = false;
1948 m_iface->SetCommitFlags( 0 );
1949 }
1950
1951 // Warp the mouse so the user is at the point we managed to route to
1952 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1953 }
1954 }
1955 else if( evt->IsAction( &PCB_ACTIONS::routerContinueFromEnd ) )
1956 {
1957 bool needsAppend = m_router->Placer()->HasPlacedAnything();
1958
1959 if( m_router->ContinueFromEnd( &m_startItem ) )
1960 {
1961 syncRouterAndFrameLayer();
1962 m_startSnapPoint = m_router->Placer()->CurrentStart();
1963 updateEndItem( *evt );
1964
1965 // Warp the mouse to wherever we actually ended up routing to
1966 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1967
1968 // We want the next router commit to be one undo at the UI layer
1969 m_iface->SetCommitFlags( needsAppend ? APPEND_UNDO : 0 );
1970 }
1971 else
1972 {
1973 frame()->ShowInfoBarError( m_router->FailureReason(), true );
1974 }
1975 }
1976 else if( evt->IsClick( BUT_LEFT )
1977 || evt->IsAction( &ACTIONS::cursorClick )
1978 || evt->IsDrag( BUT_LEFT )
1979 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack ) )
1980 {
1981 updateEndItem( *evt );
1982 bool needLayerSwitch = m_router->IsPlacingVia();
1983 bool forceCommit = false;
1984
1986
1987 if( m_router->FixRoute( m_endSnapPoint, m_endItem, false, forceCommit ) )
1988 break;
1989
1990 if( !m_router->IsPlacingVia() )
1991 m_stackDropAwaitingVia = false;
1992
1993 if( needLayerSwitch )
1994 {
1996 }
1997 else
1998 {
2000 }
2001
2002 // Synchronize the indicated layer
2003 syncRouterAndFrameLayer();
2004
2005 updateEndItem( *evt );
2007 m_startItem = nullptr;
2008 }
2009 else if( evt->IsAction( &ACT_PlaceViaStack ) )
2010 {
2011 onViaStackCommand( *evt );
2012
2013 // A staggered drop fixes the track and queues the stack, break so the normal
2014 // teardown runs (restores the cursor) and the generator is committed after the
2015 // PNS world is gone.
2016 if( m_pendingViaStack || !m_router->RoutingInProgress() )
2017 break;
2018
2019 updateEndItem( *evt );
2021 }
2022 else if( evt->IsAction( &ACT_SwitchPosture ) )
2023 {
2024 m_router->FlipPosture();
2025 updateEndItem( *evt );
2026 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
2027 }
2028 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
2029 {
2030 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2031 controls()->SetAutoPan( false );
2032 {
2033 m_toolMgr->RunAction( ACT_CustomTrackWidth );
2034 }
2035 controls()->SetAutoPan( true );
2036 setCursor();
2038 }
2039 else if( evt->IsAction( &ACTIONS::finishInteractive )
2040 || evt->IsDblClick( BUT_LEFT )
2041 || evt->IsAction( &ACTIONS::cursorDblClick ) )
2042 {
2043 // Stop current routing:
2044 bool forceFinish = true;
2045 bool forceCommit = false;
2046
2047 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
2048 break;
2049 }
2050 else if( evt->IsCancelInteractive() || evt->IsAction( &PCB_ACTIONS::cancelCurrentItem )
2051 || evt->IsActivate()
2052 || evt->IsAction( &PCB_ACTIONS::routerInlineDrag ) )
2053 {
2054 if( evt->IsCancelInteractive() && ( m_inRouteSelected || !m_router->RoutingInProgress() ) )
2055 m_cancelled = true;
2056
2057 if( evt->IsActivate() && !evt->IsMoveTool() )
2058 m_cancelled = true;
2059
2060 m_router->AbortPlacement();
2061
2062 break;
2063 }
2064 else if( evt->IsUndoRedo() )
2065 {
2066 // We're in an UndoRedoBlock. If we get here, something's broken.
2067 wxFAIL;
2068 break;
2069 }
2070 else if( evt->IsClick( BUT_RIGHT ) )
2071 {
2072 m_menu->ShowContextMenu( selection() );
2073 }
2074 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2075 // but we don't at present have that, so we just knock out some of the egregious ones.
2076 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2077 {
2078 wxBell();
2079 }
2080 else
2081 {
2082 evt->SetPassEvent();
2083 }
2084 }
2085
2086 m_router->CommitRouting();
2087 // Reset to normal for next route
2088 m_iface->SetCommitFlags( 0 );
2089
2091
2092 // The PNS world is now torn down, so it is safe to add a queued staggered stack to the board.
2093 if( m_pendingViaStack )
2095}
2096
2097
2099{
2100 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
2101 DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( frame(), sizes );
2102
2103 if( settingsDlg.ShowModal() == wxID_OK )
2104 {
2105 m_router->UpdateSizes( sizes );
2106 m_savedSizes = sizes;
2107
2108 BOARD_DESIGN_SETTINGS& bds = frame()->GetBoard()->GetDesignSettings();
2110 bds.SetCustomDiffPairGap( sizes.DiffPairGap() );
2112 }
2113
2114 return 0;
2115}
2116
2117
2119{
2120 DIALOG_PNS_SETTINGS settingsDlg( frame(), m_router->Settings() );
2121
2122 settingsDlg.ShowModal();
2123
2125
2126 return 0;
2127}
2128
2129
2131{
2132 PNS::PNS_MODE mode = aEvent.Parameter<PNS::PNS_MODE>();
2133 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
2134
2135 settings.SetMode( mode );
2137
2138 return 0;
2139}
2140
2141
2143{
2144 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
2145 PNS::PNS_MODE mode = settings.Mode();
2146
2147 switch( mode )
2148 {
2149 case PNS::RM_MarkObstacles: mode = PNS::RM_Shove; break;
2150 case PNS::RM_Shove: mode = PNS::RM_Walkaround; break;
2151 case PNS::RM_Walkaround: mode = PNS::RM_MarkObstacles; break;
2152 }
2153
2154 settings.SetMode( mode );
2156
2157 return 0;
2158}
2159
2160
2162{
2163 return m_router->Settings().Mode();
2164}
2165
2166
2168{
2169 return m_router->RoutingInProgress();
2170}
2171
2172
2174{
2175 if( !m_startItem )
2176 return;
2177
2178 // Never split a via stack's connecting trace. The stack manages it as a unit, and a
2179 // split fragment would drop out of the group. Other generators still allow splitting.
2180 if( BOARD_ITEM* parent = m_startItem->Parent() )
2181 {
2182 if( PCB_GENERATOR* generator = dynamic_cast<PCB_GENERATOR*>( parent->GetParentGroup() ) )
2183 {
2184 if( generator->GetGeneratorType() == PCB_VIA_STACK::GENERATOR_TYPE )
2185 return;
2186 }
2187 }
2188
2190 m_router->BreakSegmentOrArc( m_startItem, m_startSnapPoint );
2191}
2192
2193
2195{
2199 PCB_LAYER_ID originalLayer = frame->GetActiveLayer();
2200 bool autoRoute = aEvent.Matches( PCB_ACTIONS::routerAutorouteSelected.MakeEvent() );
2201 bool otherEnd = aEvent.Matches( PCB_ACTIONS::routerRouteSelectedFromEnd.MakeEvent() );
2202
2203 if( m_router->RoutingInProgress() )
2204 return 0;
2205
2206 // Save selection then clear it for interactive routing
2207 PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2208
2209 if( selection.Size() == 0 )
2210 return 0;
2211
2212 m_toolMgr->RunAction( ACTIONS::selectionClear );
2213
2214 SCOPED_TOOL_PUSHER raii( frame, aEvent );
2215
2216 auto setCursor =
2217 [&]()
2218 {
2219 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2220 };
2221
2222 Activate();
2223 m_inRouteSelected = true;
2224
2225 // Must be done after Activate() so that it gets set into the correct context
2226 controls->ShowCursor( true );
2227 controls->ForceCursorPosition( false );
2228 // Set initial cursor
2229 setCursor();
2230
2231 // Get all connected board items, adding pads for any footprints selected
2232 std::vector<BOARD_CONNECTED_ITEM*> itemList;
2233
2234 for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() )
2235 {
2236 if( item->Type() == PCB_FOOTPRINT_T )
2237 {
2238 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2239 itemList.push_back( pad );
2240 }
2241 else if( dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) != nullptr )
2242 {
2243 itemList.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
2244 }
2245 }
2246
2247 std::shared_ptr<CONNECTIVITY_DATA> connectivity = frame->GetBoard()->GetConnectivity();
2248
2249 // For putting sequential tracks that successfully autoroute into one undo commit
2250 bool groupStart = true;
2251 m_cancelled = false;
2252
2253 for( BOARD_CONNECTED_ITEM* item : itemList )
2254 {
2255 // This code is similar to GetRatsnestForPad() but it only adds the anchor for
2256 // the side of the connectivity on this pad. It also checks for ratsnest points
2257 // inside the pad (like a trace end) and counts them.
2258 RN_NET* net = connectivity->GetRatsnestForNet( item->GetNetCode() );
2259
2260 if( !net )
2261 continue;
2262
2263 std::vector<std::shared_ptr<const CN_ANCHOR>> anchors;
2264
2265 for( const CN_EDGE& edge : net->GetEdges() )
2266 {
2267 std::shared_ptr<const CN_ANCHOR> target = edge.GetTargetNode();
2268 std::shared_ptr<const CN_ANCHOR> source = edge.GetSourceNode();
2269
2270 if( !source || source->Dirty() || !target || target->Dirty() )
2271 continue;
2272
2273 if( source->Parent() == item )
2274 anchors.push_back( source );
2275 else if( target->Parent() == item )
2276 anchors.push_back( target );
2277 }
2278
2279 // Route them
2280 for( std::shared_ptr<const CN_ANCHOR> anchor : anchors )
2281 {
2282 if( !anchor->Valid() )
2283 continue;
2284
2285 // Try to return to the original layer as indicating the user's preferred
2286 // layer for autorouting tracks. The layer can be changed by the user to
2287 // finish tracks that can't complete automatically, but should be changed
2288 // back after.
2289 if( frame->GetActiveLayer() != originalLayer )
2290 frame->SetActiveLayer( originalLayer );
2291
2292 m_startItem = m_router->GetWorld()->FindItemByParent( anchor->Parent() );
2293 m_startSnapPoint = anchor->Pos();
2294 m_router->SetMode( mode );
2295
2296 // Prime the interactive routing to attempt finish if we are autorouting
2297 bool autoRouted = false;
2298
2299 if( autoRoute )
2300 m_toolMgr->PostAction( PCB_ACTIONS::routerAttemptFinish, &autoRouted );
2301 else if( otherEnd )
2303
2304 // We want autorouted tracks to all be in one undo group except for
2305 // any tracks that need to be manually finished.
2306 // The undo appending for manually finished tracks is handled in peformRouting()
2307 if( groupStart )
2308 groupStart = false;
2309 else
2310 m_iface->SetCommitFlags( APPEND_UNDO );
2311
2312 // Start interactive routing. Will automatically finish if possible.
2314
2315 if( m_cancelled )
2316 break;
2317
2318 // Route didn't complete automatically, need to a new undo commit
2319 // for the next line so those can group as far as they autoroute
2320 if( !autoRouted )
2321 groupStart = true;
2322 }
2323
2324 if( m_cancelled )
2325 break;
2326 }
2327
2328 m_iface->SetCommitFlags( 0 );
2329 m_inRouteSelected = false;
2330 return 0;
2331}
2332
2333
2335{
2337 const PCB_SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2338
2339 if( selection.Size() == 0 )
2340 return 0;
2341
2342 std::vector<BOARD_CONNECTED_ITEM*> trackItems;
2343
2344 for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() )
2345 {
2346 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
2347 trackItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
2348 }
2349
2350 if( trackItems.empty() )
2351 return 0;
2352
2353 m_toolMgr->RunAction( ACTIONS::selectionClear );
2354 Activate();
2355
2356 PNS::NODE* world = m_router->GetWorld();
2357
2358 // Differential pairs can't be optimized as individual lines
2359 // TODO once we have a differential pair line primitive, we could handle them...
2360 int dpSkipped = std::erase_if( trackItems,
2361 [&]( BOARD_CONNECTED_ITEM* aItem )
2362 {
2363 PNS::RULE_RESOLVER* rr = world->GetRuleResolver();
2364 return rr && rr->DpCoupledNet( aItem->GetNet() );
2365 } );
2366
2367 if( dpSkipped > 0 )
2368 frame->ShowInfoBarMsg( _( "Differential pair members cannot be optimized." ) );
2369
2370 bool groupStart = true;
2371
2372 for( BOARD_CONNECTED_ITEM* trackItem : trackItems )
2373 {
2374 PNS::ITEM* pnsItem = world->FindItemByParent( trackItem );
2375
2376 if( !pnsItem || !pnsItem->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
2377 continue;
2378
2379 PNS::LINKED_ITEM* linkedItem = static_cast<PNS::LINKED_ITEM*>( pnsItem );
2380
2381 PNS::LINE originalLine = world->AssembleLine( linkedItem );
2382
2383 // TODO: could allow these once we have arc-aware drag/optimize
2384 if( originalLine.ArcCount() > 0 )
2385 continue;
2386
2387 PNS::NODE* branch = world->Branch();
2388 branch->Remove( originalLine );
2389
2390 PNS::LINE optimizedLine( originalLine );
2391 optimizedLine.ClearLinks();
2392
2398
2399 if( m_router->Settings().GetRestrictAngles() )
2401
2402 bool optimized = PNS::OPTIMIZER::Optimize( &optimizedLine, effort, branch );
2403
2404 if( !optimized || optimizedLine.CompareGeometry( originalLine ) )
2405 {
2406 delete branch;
2407 continue;
2408 }
2409
2410 if( branch->CheckColliding( &optimizedLine ) )
2411 {
2412 delete branch;
2413 continue;
2414 }
2415
2416 if( groupStart )
2417 groupStart = false;
2418 else
2419 m_iface->SetCommitFlags( APPEND_UNDO );
2420
2421 branch->Add( optimizedLine );
2422 m_router->CommitRouting( branch );
2423 }
2424
2425 m_iface->SetCommitFlags( 0 );
2426
2427 return 0;
2428}
2429
2430
2432{
2433 if( m_inRouterTool )
2434 return 0;
2435
2437
2441
2442 if( m_router->RoutingInProgress() )
2443 {
2444 if( m_router->Mode() == mode )
2445 return 0;
2446 else
2447 m_router->StopRouting();
2448 }
2449
2450 // Deselect all items
2451 m_toolMgr->RunAction( ACTIONS::selectionClear );
2452
2453 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
2454 SCOPED_TOOL_PUSHER raii( frame, originalEvent );
2455
2456 auto setCursor =
2457 [&]()
2458 {
2459 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2460 };
2461
2462 Activate();
2463 // Must be done after Activate() so that it gets set into the correct context
2464 controls->ShowCursor( true );
2465 controls->ForceCursorPosition( false );
2466 // Set initial cursor
2467 setCursor();
2468
2469 m_router->SetMode( mode );
2470 m_cancelled = false;
2471 m_startWithVia = false;
2472
2473 if( aEvent.HasPosition() )
2474 m_toolMgr->PrimeTool( aEvent.Position() );
2475
2476 // Main loop: keep receiving events
2477 while( TOOL_EVENT* evt = Wait() )
2478 {
2479 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Catch any visibility
2480 // changes while routing.
2481 m_toolMgr->GetView()->SyncLayerVisibilityCache();
2482
2483 if( !evt->IsDrag() )
2484 setCursor();
2485
2486 if( evt->IsCancelInteractive() )
2487 {
2488 break;
2489 }
2490 else if( evt->IsActivate() )
2491 {
2492 if( evt->IsMoveTool() || evt->IsEditorTool() )
2493 {
2494 // Make sure we come back after the move tool runs
2495 frame->PushTool( originalEvent );
2496 }
2497
2498 break;
2499 }
2500 else if( evt->Action() == TA_UNDO_REDO_PRE )
2501 {
2502 m_router->ClearWorld();
2503 }
2504 else if( evt->Action() == TA_UNDO_REDO_POST || evt->Action() == TA_MODEL_CHANGE )
2505 {
2506 m_router->SyncWorld();
2507 }
2508 else if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
2509 {
2510 updateStartItem( *evt );
2511 }
2512 else if( evt->IsAction( &PCB_ACTIONS::dragFreeAngle ) )
2513 {
2514 updateStartItem( *evt, true );
2516 }
2517 else if( evt->IsAction( &PCB_ACTIONS::drag45Degree ) )
2518 {
2519 updateStartItem( *evt, true );
2521 }
2522 else if( evt->IsAction( &PCB_ACTIONS::breakTrack ) )
2523 {
2524 updateStartItem( *evt, true );
2525 breakTrack( );
2526 evt->SetPassEvent( false );
2527 }
2528 else if( evt->IsClick( BUT_LEFT )
2529 || evt->IsAction( &ACTIONS::cursorClick )
2530 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack )
2531 || evt->IsAction( &PCB_ACTIONS::routeDiffPair ) )
2532 {
2533 updateStartItem( *evt );
2534
2535 if( evt->HasPosition() )
2536 performRouting( evt->Position() );
2537 }
2538 else if( evt->IsAction( &ACT_PlaceThroughVia ) )
2539 {
2540 m_startWithVia = true;
2541 m_toolMgr->RunAction( PCB_ACTIONS::layerToggle );
2542 }
2543 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2544 {
2545 m_router->SwitchLayer( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ) );
2546 updateStartItem( *evt );
2547 updateSizesAfterRouterEvent( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ), m_startSnapPoint );
2548 }
2549 else if( evt->IsKeyPressed() )
2550 {
2551 // wxWidgets fails to correctly translate shifted keycodes on the wxEVT_CHAR_HOOK
2552 // event so we need to process the wxEVT_CHAR event that will follow as long as we
2553 // pass the event.
2554 evt->SetPassEvent();
2555 }
2556 else if( evt->IsClick( BUT_RIGHT ) )
2557 {
2558 m_menu->ShowContextMenu( selection() );
2559 }
2560 else
2561 {
2562 evt->SetPassEvent();
2563 }
2564
2565 if( m_cancelled )
2566 break;
2567 }
2568
2569 // Store routing settings till the next invocation
2570 m_savedSizes = m_router->Sizes();
2571 m_router->ClearViewDecorations();
2572
2573 return 0;
2574}
2575
2576
2578{
2579 m_router->ClearViewDecorations();
2580
2581 view()->ClearPreview();
2582 view()->InitPreview();
2583
2585
2586 if( m_startItem && m_startItem->IsLocked() )
2587 {
2588 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
2589 wxOK | wxCANCEL | wxICON_WARNING );
2590 dlg.SetOKLabel( _( "Drag Anyway" ) );
2591 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
2592
2593 if( dlg.ShowModal() == wxID_CANCEL )
2594 return;
2595 }
2596
2597 bool dragStarted = m_router->StartDragging( m_startSnapPoint, m_startItem, aMode );
2598
2599 if( !dragStarted )
2600 {
2601 if( !m_router->FailureReason().IsEmpty() )
2602 frame()->ShowInfoBarError( m_router->FailureReason(), true );
2603
2604 return;
2605 }
2606
2607 if( m_startItem && m_startItem->Net() )
2608 highlightNets( true, { m_startItem->Net() } );
2609
2610 ctls->SetAutoPan( true );
2611 m_gridHelper->SetAuxAxes( true, m_startSnapPoint );
2612 frame()->UndoRedoBlock( true );
2613
2614 UI_UPDATE_INTERVAL_GUARD uiGuard( 200 );
2615
2616 while( TOOL_EVENT* evt = Wait() )
2617 {
2618 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Catch any visibility
2619 // changes while dragging.
2620 m_toolMgr->GetView()->SyncLayerVisibilityCache();
2621
2622 ctls->ForceCursorPosition( false );
2623
2624 if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
2625 {
2626 updateEndItem( *evt );
2628
2629 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
2630 {
2631 bool dragStatus;
2632
2633 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
2634 {
2635 view()->ClearPreview();
2636
2637 if( !dragStatus )
2638 {
2639 wxString hint;
2640 hint.Printf( _( "(%s to commit anyway.)" ),
2642
2644 statusItem->SetMessage( _( "Track violates DRC." ) );
2645 statusItem->SetHint( hint );
2646 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
2647 view()->AddToPreview( statusItem );
2648 }
2649 }
2650 }
2651 }
2652 else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorClick ) )
2653 {
2654 bool forceFinish = false;
2655 bool forceCommit = evt->Modifier( MD_CTRL );
2656
2657 if( m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit ) )
2658 break;
2659 }
2660 else if( evt->IsClick( BUT_RIGHT ) )
2661 {
2662 m_menu->ShowContextMenu( selection() );
2663 }
2664 else if( evt->IsCancelInteractive() || evt->IsAction( &PCB_ACTIONS::cancelCurrentItem )
2665 || evt->IsActivate() )
2666 {
2667 if( evt->IsCancelInteractive() && !m_startItem )
2668 m_cancelled = true;
2669
2670 if( evt->IsActivate() && !evt->IsMoveTool() )
2671 m_cancelled = true;
2672
2673 break;
2674 }
2675 else if( evt->IsUndoRedo() )
2676 {
2677 // We're in an UndoRedoBlock. If we get here, something's broken.
2678 wxFAIL;
2679 break;
2680 }
2681 else if( evt->Category() == TC_COMMAND )
2682 {
2683 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2684 // but we don't at present have that, so we just knock out some of the egregious ones.
2685 if( evt->IsAction( &ACTIONS::cut )
2686 || evt->IsAction( &ACTIONS::copy )
2687 || evt->IsAction( &ACTIONS::paste )
2688 || evt->IsAction( &ACTIONS::pasteSpecial )
2690 {
2691 wxBell();
2692 }
2693 // treat an undo as an escape
2694 else if( evt->IsAction( &ACTIONS::undo ) )
2695 {
2696 if( m_startItem )
2697 break;
2698 else
2699 wxBell();
2700 }
2701 else
2702 {
2703 evt->SetPassEvent();
2704 }
2705 }
2706 else
2707 {
2708 evt->SetPassEvent();
2709 }
2710
2711 handleCommonEvents( *evt );
2712 }
2713
2714 view()->ClearPreview();
2715 view()->ShowPreview( false );
2716
2717 if( m_router->RoutingInProgress() )
2718 m_router->StopRouting();
2719
2720 m_startItem = nullptr;
2721
2722 m_gridHelper->SetAuxAxes( false );
2723 frame()->UndoRedoBlock( false );
2724 ctls->SetAutoPan( false );
2725 ctls->ForceCursorPosition( false );
2726 highlightNets( false );
2727}
2728
2729
2731 PCB_SELECTION_TOOL* aSelTool )
2732{
2733 /*
2734 * If the collection contains a trivial line corner (two connected segments)
2735 * or a non-fanout-via (a via with no more than two connected segments), then
2736 * trim the collection down to a single item (which one won't matter since
2737 * they're all connected).
2738 */
2739
2740 // First make sure we've got something that *might* match.
2741 int vias = aCollector.CountType( PCB_VIA_T );
2742 int traces = aCollector.CountType( PCB_TRACE_T );
2743 int arcs = aCollector.CountType( PCB_ARC_T );
2744
2745 // We eliminate arcs because they are not supported in the inline drag code.
2746 if( arcs > 0 )
2747 return;
2748
2749 // We need to have at least 1 via or track
2750 if( vias + traces == 0 )
2751 return;
2752
2753 // We cannot drag more than one via at a time
2754 if( vias > 1 )
2755 return;
2756
2757 // We cannot drag more than two track segments at a time
2758 if( traces > 2 )
2759 return;
2760
2761 // Fetch first PCB_TRACK (via or trace) as our reference
2762 PCB_TRACK* reference = nullptr;
2763
2764 for( int i = 0; !reference && i < aCollector.GetCount(); i++ )
2765 reference = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2766
2767 // This should never happen, but just in case...
2768 if( !reference )
2769 return;
2770
2771 int refNet = reference->GetNetCode();
2772
2773 VECTOR2I refPoint( aPt.x, aPt.y );
2774 EDA_ITEM_FLAGS flags = reference->IsPointOnEnds( refPoint, -1 );
2775
2776 if( flags & STARTPOINT )
2777 refPoint = reference->GetStart();
2778 else if( flags & ENDPOINT )
2779 refPoint = reference->GetEnd();
2780
2781 // Check all items to ensure that any TRACKs are co-terminus with the reference and on
2782 // the same net.
2783 for( int i = 0; i < aCollector.GetCount(); i++ )
2784 {
2785 PCB_TRACK* neighbor = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2786
2787 if( neighbor && neighbor != reference )
2788 {
2789 if( neighbor->GetNetCode() != refNet )
2790 return;
2791
2792 if( neighbor->GetStart() != refPoint && neighbor->GetEnd() != refPoint )
2793 return;
2794 }
2795 }
2796
2797 // Selection meets criteria; trim it to the reference item.
2798 aCollector.Empty();
2799 aCollector.Append( reference );
2800}
2801
2802
2803// Which drag a multi-item selection gets. CanInlineDrag() and InlineDrag() both use this.
2804enum class DRAG_KIND
2805{
2806 INVALID, // something in it the router cannot drag
2807 FOOTPRINTS_KIND, // footprints only
2808 TRACKS_KIND, // tracks only
2809 BLOCK // copper, pads and footprints mixed
2810};
2811
2812
2813static DRAG_KIND dragKindOf( const PCB_SELECTION& aSelection )
2814{
2815 bool allFootprints = true;
2816 bool allTracks = true;
2817
2818 for( EDA_ITEM* item : aSelection )
2819 {
2820 switch( item->Type() )
2821 {
2822 case PCB_FOOTPRINT_T: allTracks = false; break;
2823
2824 case PCB_TRACE_T: allFootprints = false; break;
2825
2826 case PCB_ARC_T:
2827 case PCB_VIA_T:
2828 case PCB_PAD_T:
2829 allFootprints = false;
2830 allTracks = false;
2831 break;
2832
2833 default: return DRAG_KIND::INVALID;
2834 }
2835 }
2836
2837 if( allFootprints )
2839
2840 if( allTracks )
2842
2843 return DRAG_KIND::BLOCK;
2844}
2845
2846
2847bool ROUTER_TOOL::CanInlineDrag( int aDragMode )
2848{
2850 const PCB_SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2851
2852 if( selection.Size() == 1 )
2853 {
2854 return selection.Front()->IsType( GENERAL_COLLECTOR::DraggableItems ) || selection.Front()->Type() == PCB_PAD_T;
2855 }
2856
2857 switch( dragKindOf( selection ) )
2858 {
2859 case DRAG_KIND::TRACKS_KIND: return true;
2860
2861 // Neither footprints nor a block drag free angle
2863 case DRAG_KIND::BLOCK: return !( aDragMode & PNS::DM_FREE_ANGLE );
2864
2865 default: return false;
2866 }
2867}
2868
2869
2870void ROUTER_TOOL::restoreSelection( const PCB_SELECTION& aOriginalSelection )
2871{
2872 EDA_ITEMS selItems;
2873 std::copy( aOriginalSelection.Items().begin(), aOriginalSelection.Items().end(), std::back_inserter( selItems ) );
2874 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &selItems );
2875}
2876
2877
2879{
2880 const PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2881
2882 if( selection.Empty() )
2884
2885 if( selection.Empty() || !selection.Front()->IsBOARD_ITEM() )
2886 return 0;
2887
2888 // selection gets cleared in the next action, we need a copy of the selected items.
2889 std::deque<EDA_ITEM*> selectedItems = selection.GetItems();
2890
2891 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2892
2893 if( item->Type() != PCB_TRACE_T && item->Type() != PCB_VIA_T && item->Type() != PCB_ARC_T
2894 && item->Type() != PCB_FOOTPRINT_T && item->Type() != PCB_PAD_T )
2895 {
2896 return 0;
2897 }
2898
2899 std::set<FOOTPRINT*> footprints;
2900 std::set<PAD*> freePads;
2901 bool blockDrag = false;
2902
2903 if( item->Type() == PCB_FOOTPRINT_T )
2904 footprints.insert( static_cast<FOOTPRINT*>( item ) );
2905
2906 if( selection.Size() > 1 )
2907 {
2908 for( int idx = 0; idx < selection.Size(); ++idx )
2909 {
2910 if( !selection.GetItem( idx )->IsBOARD_ITEM() )
2911 return 0;
2912
2913 BOARD_ITEM* selItem = static_cast<BOARD_ITEM*>( selection.GetItem( idx ) );
2914
2915 if( selItem->Type() == PCB_FOOTPRINT_T )
2916 footprints.insert( static_cast<FOOTPRINT*>( selItem ) );
2917 }
2918
2919 DRAG_KIND kind = dragKindOf( selection );
2920
2921 if( kind == DRAG_KIND::INVALID )
2922 {
2923 // Refuse if a footprint led the selection, otherwise drop the footprints
2924 if( item->Type() == PCB_FOOTPRINT_T )
2925 {
2926 frame()->ShowInfoBarMsg( _( "The selection contains items that cannot be dragged." ) );
2927 return 0;
2928 }
2929
2930 footprints.clear();
2931 }
2932
2933 blockDrag = kind == DRAG_KIND::BLOCK;
2934 }
2935
2936 // If we overrode locks, we want to clear the flag from the source item before SyncWorld is
2937 // called so that virtual vias are not generated for the (now unlocked) track segment. Note in
2938 // this case the lock can't be reliably re-applied, because there is no guarantee that the end
2939 // state of the drag results in the same number of segments so it's not clear which segment to
2940 // apply the lock state to.
2941 //
2942 // A block drag leaves locks alone. The dragger exempts selected copper itself.
2943 bool wasLocked = false;
2944
2945 if( !blockDrag && item->Type() != PCB_PAD_T && item->IsLocked() )
2946 {
2947 wasLocked = true;
2948 item->SetLocked( false );
2949 }
2950
2951 m_toolMgr->RunAction( ACTIONS::selectionClear );
2952
2953 SCOPED_TOOL_PUSHER raii( frame(), aEvent );
2954 Activate();
2955
2956 m_startItem = nullptr;
2957
2958 PNS::ITEM_SET itemsToDrag;
2959
2960 bool showCourtyardConflicts = frame()->GetPcbNewSettings()->m_ShowCourtyardCollisions;
2961
2962 std::shared_ptr<DRC_ENGINE> drcEngine = m_toolMgr->GetTool<DRC_TOOL>()->GetDRCEngine();
2963 DRC_INTERACTIVE_COURTYARD_CLEARANCE courtyardClearanceDRC( drcEngine );
2964
2965 std::shared_ptr<CONNECTIVITY_DATA> connectivityData = board()->GetConnectivity();
2966 std::vector<BOARD_ITEM*> dynamicItems;
2967 std::unique_ptr<CONNECTIVITY_DATA> dynamicData = nullptr;
2968 VECTOR2I lastOffset;
2969 std::vector<PNS::ITEM*> leaderSegments;
2970 bool singleFootprintDrag = false;
2971
2972 // The PNS world may be stale if the board has been modified since the last sync (e.g. by
2973 // a Move operation). Sync it now so that FindItemByParent and joint lookups work correctly.
2974 m_router->SyncWorld();
2975
2976 // A footprint with no copper the router can see cannot be dragged, and showing it in the
2977 // preview would promise a move the commit will not make
2978 for( auto it = footprints.begin(); it != footprints.end(); )
2979 {
2980 bool hasSolid = false;
2981
2982 for( PAD* pad : ( *it )->Pads() )
2983 hasSolid |= !m_router->GetWorld()->FindItemsByParent( pad ).empty();
2984
2985 it = hasSolid ? std::next( it ) : footprints.erase( it );
2986 }
2987
2988 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Make sure it's up-to-date.
2989 m_toolMgr->GetView()->SyncLayerVisibilityCache();
2990
2991 if( !footprints.empty() )
2992 {
2993 // A block drag has its own algorithm, and the leader logic below assumes a lone footprint
2994 if( footprints.size() == 1 && !blockDrag )
2995 singleFootprintDrag = true;
2996
2997 if( showCourtyardConflicts )
2998 courtyardClearanceDRC.Init( board() );
2999
3000 for( FOOTPRINT* footprint : footprints )
3001 {
3002 for( PAD* pad : footprint->Pads() )
3003 {
3004 // A padstack with per-layer geometry is more than one router solid
3005 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( pad ) )
3006 itemsToDrag.Add( solid );
3007
3008 if( pad->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
3009 {
3010 if( connectivityData->GetRatsnestForPad( pad ).size() > 0 )
3011 dynamicItems.push_back( pad );
3012 }
3013 }
3014
3015 for( ZONE* zone : footprint->Zones() )
3016 {
3017 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( zone ) )
3018 itemsToDrag.Add( solid );
3019 }
3020
3021 for( BOARD_ITEM* shape : footprint->GraphicalItems() )
3022 {
3023 if( shape->GetLayer() == Edge_Cuts
3024 || shape->GetLayer() == Margin
3025 || IsCopperLayer( shape->GetLayer() ) )
3026 {
3027 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( shape ) )
3028 itemsToDrag.Add( solid );
3029 }
3030 }
3031
3032 if( showCourtyardConflicts )
3033 courtyardClearanceDRC.m_FpInMove.push_back( footprint );
3034 }
3035
3036 dynamicData = std::make_unique<CONNECTIVITY_DATA>( board()->GetConnectivity(), dynamicItems, true );
3037 connectivityData->BlockRatsnestItems( dynamicItems );
3038 }
3039
3040 if( footprints.empty() || blockDrag )
3041 {
3042 for( EDA_ITEM* selItem : selectedItems )
3043 {
3044 if( !selItem->IsBOARD_ITEM() )
3045 continue;
3046
3047 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( selItem );
3048 PNS::ITEM* pnsItem = m_router->GetWorld()->FindItemByParent( boardItem );
3049
3050 if( !pnsItem )
3051 continue;
3052
3053 if( pnsItem->OfKind( PNS::ITEM::SEGMENT_T )
3054 || pnsItem->OfKind( PNS::ITEM::VIA_T )
3055 || pnsItem->OfKind( PNS::ITEM::ARC_T ) )
3056 {
3057 itemsToDrag.Add( pnsItem );
3058 }
3059 else if( boardItem->Type() == PCB_PAD_T )
3060 {
3061 PAD* pad = static_cast<PAD*>( boardItem );
3062
3063 // A pad selected together with its footprint already travels with it
3064 if( footprints.count( pad->GetParentFootprint() ) )
3065 continue;
3066
3067 freePads.insert( pad );
3068
3069 // A padstack with per-layer geometry is more than one router solid
3070 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( pad ) )
3071 itemsToDrag.Add( solid );
3072 }
3073 }
3074 }
3075
3076 GAL* gal = m_toolMgr->GetView()->GetGAL();
3077 VECTOR2I p0 = GetClampedCoords( controls()->GetCursorPosition( false ), COORDS_PADDING );
3078 VECTOR2I p = p0;
3079
3080 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
3081 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
3082
3083 if( itemsToDrag.Count() >= 1 )
3084 {
3085 // Snap to closest item. Use the frame's active layer rather than m_originalActiveLayer,
3086 // which is only set during prepareInteractive() and remains UNDEFINED_LAYER for inline
3087 // drag operations.
3088 PCB_LAYER_ID activeLayer = frame()->GetActiveLayer();
3089 int layer = m_iface->GetPNSLayerFromBoardLayer( activeLayer );
3090 PNS::ITEM* closestItem = nullptr;
3091 SEG::ecoord closestDistSq = std::numeric_limits<SEG::ecoord>::max();
3092
3093 for( PNS::ITEM* pitem : itemsToDrag.Items() )
3094 {
3095 const SHAPE* shape = pitem->Shape( layer );
3096
3097 if( !shape )
3098 continue;
3099
3100 SEG::ecoord distSq = shape->SquaredDistance( p0, 0 );
3101
3102 if( distSq < closestDistSq )
3103 {
3104 closestDistSq = distSq;
3105 closestItem = pitem;
3106 }
3107 }
3108
3109 if( closestItem )
3110 {
3111 p = snapToItem( closestItem, p0 );
3112
3113 m_startItem = closestItem;
3114
3115 if( closestItem->Net() )
3116 highlightNets( true, { closestItem->Net() } );
3117 }
3118 }
3119
3120 if( !footprints.empty() && singleFootprintDrag )
3121 {
3122 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
3123
3124 // The mouse is going to be moved on grid before dragging begins.
3125 VECTOR2I tweakedMousePos;
3127
3128 // Check if user wants to warp the mouse to origin of moved object
3129
3130 if( editFrame->GetMoveWarpsCursor() )
3131 tweakedMousePos = footprint->GetPosition(); // Use footprint anchor to warp mouse
3132 else
3133 tweakedMousePos = GetClampedCoords( controls()->GetCursorPosition(),
3134 COORDS_PADDING ); // Just use current mouse pos
3135
3136 // We tweak the mouse position using the value from above, and then use that as the
3137 // start position to prevent the footprint from jumping when we start dragging.
3138 // First we move the visual cross hair cursor...
3139 controls()->ForceCursorPosition( true, tweakedMousePos );
3140 controls()->SetCursorPosition( tweakedMousePos ); // ...then the mouse pointer
3141
3142 // Now that the mouse is in the right position, get a copy of the position to use later
3143 p = controls()->GetCursorPosition();
3144 }
3145
3146 int dragMode = aEvent.Parameter<int> ();
3147
3148 if( !footprints.empty() || !freePads.empty() || blockDrag )
3149 dragMode &= ~PNS::DM_FREE_ANGLE;
3150
3151 if( blockDrag )
3152 dragMode |= PNS::DM_BLOCK;
3153
3154 m_iface->SetIndependentPads( freePads );
3155
3156 bool dragStarted = m_router->StartDragging( p, itemsToDrag, dragMode );
3157
3158 if( !dragStarted )
3159 {
3160 m_iface->SetIndependentPads( {} );
3161
3162 if( blockDrag )
3163 frame()->ShowInfoBarMsg( _( "Nothing in the selection can be dragged." ) );
3164
3165 if( wasLocked )
3166 item->SetLocked( true );
3167
3168 if( !footprints.empty() )
3169 connectivityData->ClearLocalRatsnest();
3170
3171 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
3172 courtyardClearanceDRC.ClearConflicts( getView() );
3173
3175 controls()->ForceCursorPosition( false );
3176 highlightNets( false );
3177 return 0;
3178 }
3179
3180 m_gridHelper->SetAuxAxes( true, p );
3181 controls()->ShowCursor( true );
3182 controls()->SetAutoPan( true );
3183 frame()->UndoRedoBlock( true );
3184
3185 if( auto* blockDragger = dynamic_cast<PNS::BLOCK_DRAGGER*>( m_router->GetDragger() ) )
3186 {
3187 size_t held = blockDragger->AnchoredLines().size();
3188
3189 if( held > 0 )
3190 {
3191 frame()->ShowInfoBarWarning(
3192 wxString::Format( wxPLURAL( "%zu selected arc cannot move because both ends are fixed.",
3193 "%zu selected arcs cannot move because both ends are fixed.", held ),
3194 held ) );
3195 }
3196 }
3197
3198 view()->ClearPreview();
3199 view()->InitPreview();
3200
3201 auto setCursor =
3202 [&]()
3203 {
3204 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3205 };
3206
3207 // Set initial cursor
3208 setCursor();
3209
3210 // Set the initial visible area
3211 BOX2D viewAreaD = getView()->GetGAL()->GetVisibleWorldExtents();
3212 m_router->SetVisibleViewArea( BOX2ISafe( viewAreaD ) );
3213
3214 // Send an initial movement to prime the collision detection
3215 m_router->Move( p, nullptr );
3216
3217 UI_UPDATE_INTERVAL_GUARD uiGuard( 200 );
3218
3219 bool hasMouseMoved = false;
3220 bool hasMultidragCancelled = false;
3221
3222 while( TOOL_EVENT* evt = Wait() )
3223 {
3224 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Catch any visibility
3225 // changes while dragging.
3226 m_toolMgr->GetView()->SyncLayerVisibilityCache();
3227
3228 setCursor();
3229
3230 if( evt->IsCancelInteractive()
3231 || evt->IsAction( &PCB_ACTIONS::cancelCurrentItem )
3232 || evt->IsActivate() )
3233 {
3234 if( wasLocked )
3235 item->SetLocked( true );
3236
3237 hasMultidragCancelled = true;
3238
3239 break;
3240 }
3241 else if( evt->IsMotion()
3242 || evt->IsAction( &ACTIONS::refreshPreview )
3243 || evt->IsDrag( BUT_LEFT ) )
3244 {
3245 hasMouseMoved = true;
3246 updateEndItem( *evt );
3248
3249 view()->ClearPreview();
3250
3251 if( !footprints.empty() )
3252 {
3253 VECTOR2I offset = m_endSnapPoint - p;
3254 BOARD_ITEM* previewItem;
3255
3256 for( FOOTPRINT* footprint : footprints )
3257 {
3258 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
3259 {
3260 previewItem = static_cast<BOARD_ITEM*>( drawing->Clone() );
3261 previewItem->Move( offset );
3262
3263 view()->AddToPreview( previewItem );
3264 view()->Hide( drawing, true );
3265 }
3266
3267 for( PAD* pad : footprint->Pads() )
3268 {
3269 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none()
3270 && pad->GetDrillSize().x == 0 )
3271 {
3272 previewItem = static_cast<BOARD_ITEM*>( pad->Clone() );
3273 previewItem->Move( offset );
3274
3275 view()->AddToPreview( previewItem );
3276 }
3277 else
3278 {
3279 // Pads with copper or holes are handled by the router
3280 }
3281
3282 view()->Hide( pad, true );
3283 }
3284
3285 previewItem = static_cast<BOARD_ITEM*>( footprint->Reference().Clone() );
3286 previewItem->Move( offset );
3287 view()->AddToPreview( previewItem );
3288 view()->Hide( &footprint->Reference() );
3289
3290 previewItem = static_cast<BOARD_ITEM*>( footprint->Value().Clone() );
3291 previewItem->Move( offset );
3292 view()->AddToPreview( previewItem );
3293 view()->Hide( &footprint->Value() );
3294
3295 if( showCourtyardConflicts )
3296 footprint->Move( offset );
3297 }
3298
3299 if( showCourtyardConflicts )
3300 {
3301 courtyardClearanceDRC.Run();
3302 courtyardClearanceDRC.UpdateConflicts( getView(), false );
3303
3304 for( FOOTPRINT* footprint : footprints )
3305 footprint->Move( -offset );
3306 }
3307
3308 // Update ratsnest
3309 dynamicData->Move( offset - lastOffset );
3310 lastOffset = offset;
3311 connectivityData->ComputeLocalRatsnest( dynamicItems, dynamicData.get(), offset );
3312 }
3313
3314 for( PAD* pad : freePads )
3315 view()->Hide( pad, true );
3316
3317 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
3318 {
3319 bool dragStatus;
3320
3321 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
3322 {
3323 if( !dragStatus )
3324 {
3325 wxString hint;
3326 hint.Printf( _( "(%s to commit anyway.)" ), KeyNameFromKeyCode( MD_CTRL + PSEUDO_WXK_CLICK ) );
3327
3329 statusItem->SetMessage( _( "Track violates DRC." ) );
3330 statusItem->SetHint( hint );
3331 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
3332 view()->AddToPreview( statusItem );
3333 }
3334 }
3335 }
3336 }
3337 else if( hasMouseMoved && ( evt->IsMouseUp( BUT_LEFT )
3338 || evt->IsClick( BUT_LEFT )
3339 || evt->IsAction( &ACTIONS::cursorClick ) ) )
3340 {
3341 bool forceFinish = false;
3342 bool forceCommit = evt->Modifier( MD_CTRL );
3343
3344 updateEndItem( *evt );
3345 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
3346 leaderSegments = m_router->GetLastCommittedLeaderSegments();
3347
3348 break;
3349 }
3350 else if( evt->IsUndoRedo() )
3351 {
3352 // We're in an UndoRedoBlock. If we get here, something's broken.
3353 wxFAIL;
3354 break;
3355 }
3356 else if( evt->Category() == TC_COMMAND )
3357 {
3358 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
3359 // but we don't at present have that, so we just knock out some of the egregious ones.
3360 if( evt->IsAction( &ACTIONS::cut )
3361 || evt->IsAction( &ACTIONS::copy )
3362 || evt->IsAction( &ACTIONS::paste )
3363 || evt->IsAction( &ACTIONS::pasteSpecial )
3365 {
3366 wxBell();
3367 }
3368 // treat an undo as an escape
3369 else if( evt->IsAction( &ACTIONS::undo ) )
3370 {
3371 if( wasLocked )
3372 item->SetLocked( true );
3373
3374 break;
3375 }
3376 else
3377 {
3378 evt->SetPassEvent();
3379 }
3380 }
3381 else
3382 {
3383 evt->SetPassEvent();
3384 }
3385
3386 handleCommonEvents( *evt );
3387 }
3388
3389 if( !footprints.empty() )
3390 {
3391 for( FOOTPRINT* footprint : footprints )
3392 {
3393 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
3394 view()->Hide( drawing, false );
3395
3396 view()->Hide( &footprint->Reference(), false );
3397 view()->Hide( &footprint->Value(), false );
3398
3399 for( PAD* pad : footprint->Pads() )
3400 view()->Hide( pad, false );
3401 }
3402
3403 view()->ClearPreview();
3404 view()->ShowPreview( false );
3405
3406 connectivityData->ClearLocalRatsnest();
3407 }
3408
3409 for( PAD* pad : freePads )
3410 view()->Hide( pad, false );
3411
3412 m_iface->SetIndependentPads( {} );
3413
3414 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
3415 courtyardClearanceDRC.ClearConflicts( getView() );
3416
3417 if( m_router->RoutingInProgress() )
3418 m_router->StopRouting();
3419
3420
3421 if( itemsToDrag.Size() && hasMultidragCancelled )
3422 {
3424 }
3425 else if( leaderSegments.size() )
3426 {
3427 std::vector<EDA_ITEM*> newItems;
3428
3429 for( PNS::ITEM* lseg : leaderSegments )
3430 newItems.push_back( lseg->Parent() );
3431
3432 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &newItems );
3433 }
3434
3435 m_gridHelper->SetAuxAxes( false );
3436 controls()->SetAutoPan( false );
3437 controls()->ForceCursorPosition( false );
3438 frame()->UndoRedoBlock( false );
3439 highlightNets( false );
3440 view()->ClearPreview();
3441 view()->ShowPreview( false );
3442
3443 return 0;
3444}
3445
3446
3448{
3449 const SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
3450
3451 if( selection.Size() != 1 )
3452 return 0;
3453
3454 const BOARD_CONNECTED_ITEM* item = static_cast<const BOARD_CONNECTED_ITEM*>( selection.Front() );
3455
3456 if( item->Type() != PCB_TRACE_T && item->Type() != PCB_ARC_T )
3457 return 0;
3458
3459 Activate();
3460
3461 // Snapping uses ViewGetLOD(), which uses the layerVisibilityCache. Make sure it's up-to-date.
3462 m_toolMgr->GetView()->SyncLayerVisibilityCache();
3463
3464 m_startItem = m_router->GetWorld()->FindItemByParent( item );
3465
3466 TOOL_MANAGER* toolManager = frame()->GetToolManager();
3467 GAL* gal = toolManager->GetView()->GetGAL();
3468
3469 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
3470 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
3471
3472 controls()->ForceCursorPosition( false );
3473
3474 if( toolManager->IsContextMenuActive() )
3475 {
3476 // If we're here from a context menu then we need to get the position of the
3477 // cursor when the context menu was invoked. This is used to figure out the
3478 // break point on the track.
3480 }
3481 else
3482 {
3483 // If we're here from a hotkey, then get the current mouse position so we know
3484 // where to break the track.
3485 m_startSnapPoint = snapToItem( m_startItem, controls()->GetCursorPosition() );
3486 }
3487
3488 if( m_startItem && m_startItem->IsLocked() )
3489 {
3490 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
3491 wxOK | wxCANCEL | wxICON_WARNING );
3492 dlg.SetOKLabel( _( "Break Track" ) );
3493 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
3494
3495 if( dlg.ShowModal() == wxID_CANCEL )
3496 return 0;
3497 }
3498
3499 m_toolMgr->RunAction( ACTIONS::selectionClear );
3500
3501 frame()->UndoRedoBlock( true );
3502 breakTrack();
3503
3504 if( m_router->RoutingInProgress() )
3505 m_router->StopRouting();
3506
3507 frame()->UndoRedoBlock( false );
3508
3509 return 0;
3510}
3511
3512
3514{
3516 DIALOG_TRACK_VIA_SIZE sizeDlg( frame(), bds );
3517
3518 if( sizeDlg.ShowModal() == wxID_OK )
3519 {
3520 bds.m_TempOverrideTrackWidth = true;
3521 bds.UseCustomTrackViaSize( true );
3522
3525 }
3526
3527 return 0;
3528}
3529
3530
3532{
3533 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
3534
3535 if( !m_router->GetCurrentNets().empty() )
3536 m_iface->ImportSizes( sizes, m_startItem, m_router->GetCurrentNets()[0], VECTOR2D() );
3537
3538 m_router->UpdateSizes( sizes );
3539
3540 // Changing the track width can affect the placement, so call the
3541 // move routine without changing the destination
3542 // Update end item first to avoid moving to an invalid/missing item
3543 updateEndItem( aEvent );
3545
3547
3548 return 0;
3549}
3550
3551
3553{
3554 std::vector<MSG_PANEL_ITEM> items;
3555
3556 if( m_router->GetState() == PNS::ROUTER::ROUTE_TRACK )
3557 {
3558 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
3559 PNS::RULE_RESOLVER* resolver = m_iface->GetRuleResolver();
3560 PNS::CONSTRAINT constraint;
3561 std::vector<PNS::NET_HANDLE> nets = m_router->GetCurrentNets();
3562 wxString description;
3563 wxString secondary;
3564 wxString mode;
3565
3567 {
3568 wxASSERT( nets.size() >= 2 );
3569
3570 NETINFO_ITEM* netA = static_cast<NETINFO_ITEM*>( nets[0] );
3571 NETINFO_ITEM* netB = static_cast<NETINFO_ITEM*>( nets[1] );
3572 wxASSERT( netA );
3573 wxASSERT( netB );
3574
3575 description = wxString::Format( _( "Routing Diff Pair: %s" ),
3576 netA->GetNetname() + wxT( ", " ) + netB->GetNetname() );
3577
3578 wxString netclass;
3579 NETCLASS* netclassA = netA->GetNetClass();
3580 NETCLASS* netclassB = netB->GetNetClass();
3581
3582 if( *netclassA == *netclassB )
3583 netclass = netclassA->GetHumanReadableName();
3584 else
3585 netclass = netclassA->GetHumanReadableName() + wxT( ", " )
3586 + netclassB->GetHumanReadableName();
3587
3588 secondary = wxString::Format( _( "Resolved Netclass: %s" ),
3589 UnescapeString( netclass ) );
3590 }
3591 else if( !nets.empty() && nets[0] )
3592 {
3593 NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( nets[0] );
3594
3595 description = wxString::Format( _( "Routing Track: %s" ),
3596 net->GetNetname() );
3597
3598 secondary = wxString::Format(
3599 _( "Resolved Netclass: %s" ),
3601 }
3602 else
3603 {
3604 description = _( "Routing Track" );
3605 secondary = _( "(no net)" );
3606 }
3607
3608 items.emplace_back( description, secondary );
3609
3610 wxString cornerMode;
3611
3612 if( m_router->Settings().GetFreeAngleMode() )
3613 {
3614 cornerMode = _( "Free-angle" );
3615 }
3616 else
3617 {
3618 switch( m_router->Settings().GetCornerMode() )
3619 {
3620 case DIRECTION_45::CORNER_MODE::MITERED_45: cornerMode = _( "45-degree" ); break;
3621 case DIRECTION_45::CORNER_MODE::ROUNDED_45: cornerMode = _( "45-degree rounded" ); break;
3622 case DIRECTION_45::CORNER_MODE::MITERED_90: cornerMode = _( "90-degree" ); break;
3623 case DIRECTION_45::CORNER_MODE::ROUNDED_90: cornerMode = _( "90-degree rounded" ); break;
3624 default: break;
3625 }
3626 }
3627
3628 items.emplace_back( _( "Corner Style" ), cornerMode );
3629
3630 switch( m_router->Settings().Mode() )
3631 {
3632 case PNS::PNS_MODE::RM_MarkObstacles: mode = _( "Highlight collisions" ); break;
3633 case PNS::PNS_MODE::RM_Walkaround: mode = _( "Walk around" ); break;
3634 case PNS::PNS_MODE::RM_Shove: mode = _( "Shove" ); break;
3635 default: break;
3636 }
3637
3638 items.emplace_back( _( "Mode" ), mode );
3639
3640#define FORMAT_VALUE( x ) frame()->MessageTextFromValue( x )
3641
3643 {
3644 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
3645 FORMAT_VALUE( sizes.DiffPairWidth() ) ),
3646 wxString::Format( _( "(from %s)" ),
3647 sizes.GetDiffPairWidthSource() ) );
3648
3649 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
3650 FORMAT_VALUE( sizes.Clearance() ) ),
3651 wxString::Format( _( "(from %s)" ),
3652 sizes.GetClearanceSource() ) );
3653
3654 items.emplace_back( wxString::Format( _( "Diff Pair Gap: %s" ),
3655 FORMAT_VALUE( sizes.DiffPairGap() ) ),
3656 wxString::Format( _( "(from %s)" ),
3657 sizes.GetDiffPairGapSource() ) );
3658
3659 const PNS::ITEM_SET& traces = m_router->Placer()->Traces();
3660 wxASSERT( traces.Count() == 2 );
3661
3663 traces[1], m_router->GetCurrentLayer(), &constraint ) )
3664 {
3665 items.emplace_back( wxString::Format( _( "DP Max Uncoupled-length: %s" ),
3666 FORMAT_VALUE( constraint.m_Value.Max() ) ),
3667 wxString::Format( _( "(from %s)" ),
3668 constraint.m_RuleName ) );
3669 }
3670 }
3671 else
3672 {
3673 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
3674 FORMAT_VALUE( sizes.TrackWidth() ) ),
3675 wxString::Format( _( "(from %s)" ),
3676 sizes.GetWidthSource() ) );
3677
3678 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
3679 FORMAT_VALUE( sizes.Clearance() ) ),
3680 wxString::Format( _( "(from %s)" ),
3681 sizes.GetClearanceSource() ) );
3682 }
3683
3684#undef FORMAT_VALUE
3685
3686 frame()->SetMsgPanel( items );
3687 }
3688 else
3689 {
3690 frame()->SetMsgPanel( board() );
3691 return;
3692 }
3693}
3694
3695
3697{
3699
3714
3722
3758
3761
3767}
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:85
void SetLocked(bool aLocked) override
Definition board_item.h:427
bool IsLocked() const override
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:445
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2797
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:1180
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1174
BOARD_DESIGN_SETTINGS & GetDesignSettings()
Definition board.cpp:1290
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:758
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:162
MINOPTMAX< int > m_Value
Definition drc_rule.h:204
bool IsNull() const
Definition drc_rule.h:122
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:2755
virtual int GetTopLayer() const
Definition view.cpp:1239
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:224
void InitPreview()
Definition view.cpp:2734
void ClearPreview()
Definition view.cpp:2719
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:2576
void AddToPreview(VIEW_ITEM *aItem, bool aTakeOwnership=true)
Definition view.cpp:2741
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
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:44
bool HasuViaDrill() const
Definition netclass.h:181
const wxString GetHumanReadableName() const
Gets the consolidated name of this netclass (which may be an aggregate).
Definition netclass.cpp:346
int GetuViaDrill() const
Definition netclass.h:182
bool HasuViaDiameter() const
Definition netclass.h:173
int GetuViaDiameter() const
Definition netclass.h:174
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:603
void SetPosition(const VECTOR2I &aPoint) override
Definition pcb_track.h:604
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:423
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:153
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:163
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:498
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:753
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:997
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
@ BLOCK
Cannot be represented at all, export to this target is not possible.
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:43
@ PENCIL
Definition cursors.h:49
#define CHECK(x)
@ VIA_DIAMETER_CONSTRAINT
@ DIFF_PAIR_GAP_CONSTRAINT
@ TRACK_WIDTH_CONSTRAINT
@ CLEARANCE_CONSTRAINT
@ HOLE_SIZE_CONSTRAINT
#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:741
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:640
Push and Shove diff pair dimensions (gap) settings dialog.
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
@ DM_BLOCK
Definition pns_router.h:85
#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))
DRAG_KIND
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.
static DRAG_KIND dragKindOf(const PCB_SELECTION &aSelection)
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_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ 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.