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