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 <collectors.h>
40#include <footprint.h>
42#include <pad.h>
43#include <zone.h>
44#include <pcb_edit_frame.h>
45#include <pcb_track.h>
46#include <pcbnew_id.h>
51#include <math/vector2wx.h>
52#include <paths.h>
54#include <confirm.h>
55#include <kidialog.h>
56#include <widgets/wx_infobar.h>
61#include <view/view_controls.h>
62#include <bitmaps.h>
63#include <string_utils.h>
64#include <gal/painter.h>
65#include <tool/tool_action.h>
66#include <tool/action_menu.h>
67#include <tool/tool_manager.h>
68#include <tool/tool_menu.h>
69#include <tools/pcb_actions.h>
72#include <tools/drc_tool.h>
75
76#include <project.h>
79
80#include <io/io_utils.h>
81
82#include "router_tool.h"
84#include "pns_router.h"
85#include "pns_itemset.h"
86#include "pns_logger.h"
87#include "pns_placement_algo.h"
88#include "pns_drag_algo.h"
89
90#include "pns_kicad_iface.h"
91
93
95
96using namespace KIGFX;
97
98namespace
99{
100
101// Saves and restores the global wxUpdateUIEvent interval so it cannot leak on
102// early returns or exceptions.
103class UI_UPDATE_INTERVAL_GUARD
104{
105public:
106 UI_UPDATE_INTERVAL_GUARD( long aNewInterval ) :
107 m_saved( wxUpdateUIEvent::GetUpdateInterval() )
108 {
109 wxUpdateUIEvent::SetUpdateInterval( aNewInterval );
110 }
111
112 ~UI_UPDATE_INTERVAL_GUARD()
113 {
114 wxUpdateUIEvent::SetUpdateInterval( m_saved );
115 }
116
117private:
118 long m_saved;
119};
120
121} // anonymous namespace
122
127{
128 // Via type
129 VIA_MASK = 0x07,
130 VIA = 0x00,
131 BLIND_VIA = 0x01,
132 BURIED_VIA = 0x02,
133 MICROVIA = 0x04,
134
135 // Select layer
137};
138
139
140// Actions, being statically-defined, require specialized I18N handling. We continue to
141// use the _() macro so that string harvesting by the I18N framework doesn't have to be
142// specialized, but we don't translate on initialization and instead do it in the getters.
143
144#undef _
145#define _(s) s
146
147// Pass all the parameters as int to allow combining flags
149 .Name( "pcbnew.InteractiveRouter.PlaceVia" )
150 .Scope( AS_CONTEXT )
151 .DefaultHotkey( 'V' )
152 .LegacyHotkeyName( "Add Through Via" )
153 .FriendlyName( _( "Place Through Via" ) )
154 .Tooltip( _( "Adds a through-hole via at the end of currently routed track." ) )
155 .Icon( BITMAPS::via )
156 .Flags( AF_NONE )
157 .Parameter<int>( VIA_ACTION_FLAGS::VIA ) );
158
160 .Name( "pcbnew.InteractiveRouter.PlaceBlindVia" )
161 .Scope( AS_CONTEXT )
162 .DefaultHotkey( MD_ALT + MD_SHIFT + 'V' )
163 .LegacyHotkeyName( "Add Blind/Buried Via" )
164 .FriendlyName( _( "Place Blind/Buried Via" ) )
165 .Tooltip( _( "Adds a blind or buried via at the end of currently routed track.") )
166 .Icon( BITMAPS::via_buried )
167 .Flags( AF_NONE )
168 .Parameter<int>( VIA_ACTION_FLAGS::BLIND_VIA ) );
169
171 .Name( "pcbnew.InteractiveRouter.PlaceMicroVia" )
172 .Scope( AS_CONTEXT )
173 .DefaultHotkey( MD_CTRL + 'V' )
174 .LegacyHotkeyName( "Add MicroVia" )
175 .FriendlyName( _( "Place Microvia" ) )
176 .Tooltip( _( "Adds a microvia at the end of currently routed track." ) )
177 .Icon( BITMAPS::via_microvia )
178 .Flags( AF_NONE )
179 .Parameter<int>( VIA_ACTION_FLAGS::MICROVIA ) );
180
182 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceVia" )
183 .Scope( AS_CONTEXT )
184 .DefaultHotkey( '<' )
185 .LegacyHotkeyName( "Select Layer and Add Through Via" )
186 .FriendlyName( _( "Select Layer and Place Through Via..." ) )
187 .Tooltip( _( "Select a layer, then add a through-hole via at the end of currently routed track." ) )
189 .Flags( AF_NONE )
191
193 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceBlindVia" )
194 .Scope( AS_CONTEXT )
195 .DefaultHotkey( MD_ALT + '<' )
196 .LegacyHotkeyName( "Select Layer and Add Blind/Buried Via" )
197 .FriendlyName( _( "Select Layer and Place Blind/Buried Via..." ) )
198 .Tooltip( _( "Select a layer, then add a blind or buried via at the end of currently routed track." ) )
200 .Flags( AF_NONE )
202
204 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceMicroVia" )
205 .Scope( AS_CONTEXT )
206 .FriendlyName( _( "Select Layer and Place Micro Via..." ) )
207 .Tooltip( _( "Select a layer, then add a micro via at the end of currently routed track." ) )
209 .Flags( AF_NONE )
211
213 .Name( "pcbnew.InteractiveRouter.CustomTrackViaSize" )
214 .Scope( AS_CONTEXT )
215 .DefaultHotkey( 'Q' )
216 .LegacyHotkeyName( "Custom Track/Via Size" )
217 .FriendlyName( _( "Custom Track/Via Size..." ) )
218 .Tooltip( _( "Shows a dialog for changing the track width and via size." ) )
219 .Icon( BITMAPS::width_track ) );
220
222 .Name( "pcbnew.InteractiveRouter.SwitchPosture" )
223 .Scope( AS_CONTEXT )
224 .DefaultHotkey( '/' )
225 .LegacyHotkeyName( "Switch Track Posture" )
226 .FriendlyName( _( "Switch Track Posture" ) )
227 .Tooltip( _( "Switches posture of the currently routed track." ) )
229
230 // This old command ( track corner switch mode) is now moved to a submenu with other corner mode options
232 .Name( "pcbnew.InteractiveRouter.SwitchRoundingToNext" )
233 .Scope( AS_CONTEXT )
234 .DefaultHotkey( MD_CTRL + '/' )
235 .FriendlyName( _( "Track Corner Mode Switch" ) )
236 .Tooltip( _( "Switches between sharp/rounded and 45°/90° corners when routing tracks." ) )
238
239// hotkeys W and Shift+W are used to switch to track width changes
241 .Name( "pcbnew.InteractiveRouter.SwitchRounding45" )
242 .Scope( AS_CONTEXT )
243 .DefaultHotkey( MD_CTRL + 'W' )
244 .FriendlyName( _( "Track Corner Mode 45" ) )
245 .Tooltip( _( "Switch to 45° corner when routing tracks." ) ) );
246
248 .Name( "pcbnew.InteractiveRouter.SwitchRounding90" )
249 .Scope( AS_CONTEXT )
250 .DefaultHotkey( MD_CTRL + MD_ALT + 'W' )
251 .FriendlyName( _( "Track Corner Mode 90" ) )
252 .Tooltip( _( "Switch to 90° corner when routing tracks." ) ) );
253
255 .Name( "pcbnew.InteractiveRouter.SwitchRoundingArc45" )
256 .Scope( AS_CONTEXT )
257 .DefaultHotkey( MD_CTRL + MD_SHIFT + 'W' )
258 .FriendlyName( _( "Track Corner Mode Arc 45" ) )
259 .Tooltip( _( "Switch to arc 45° corner when routing tracks." ) ) );
260
262 .Name( "pcbnew.InteractiveRouter.SwitchRoundingArc90" )
263 .Scope( AS_CONTEXT )
264 .DefaultHotkey( MD_ALT + 'W' )
265 .FriendlyName( _( "Track Corner Mode Arc 90" ) )
266 .Tooltip( _( "Switch to arc 90° corner when routing tracks." ) ) );
267
268#undef _
269#define _(s) wxGetTranslation((s))
270
271
273 TOOL_BASE( "pcbnew.InteractiveRouter" ),
276 m_inRouterTool( false ),
277 m_inRouteSelected( false ),
278 m_startWithVia( false )
279{
280}
281
282
284{
285public:
287 ACTION_MENU( true ),
288 m_frame( aFrame )
289 {
291 SetTitle( _( "Select Track/Via Width" ) );
292 }
293
294protected:
295 ACTION_MENU* create() const override
296 {
297 return new TRACK_WIDTH_MENU( m_frame );
298 }
299
300 void update() override
301 {
302 BOARD_DESIGN_SETTINGS& bds = m_frame.GetBoard()->GetDesignSettings();
303 bool useIndex = !bds.m_UseConnectedTrackWidth &&
305 wxString msg;
306
307 Clear();
308
309 Append( ID_POPUP_PCB_SELECT_AUTO_WIDTH, _( "Use Starting Track Width" ),
310 _( "Route using the width of the starting track." ), wxITEM_CHECK );
313
314 Append( ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES, _( "Use Net Class Values" ),
315 _( "Use track and via sizes from the net class" ), wxITEM_CHECK );
317 useIndex && bds.GetTrackWidthIndex() == 0 && bds.GetViaSizeIndex() == 0 );
318
319 Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
320 _( "Specify custom track and via sizes" ), wxITEM_CHECK );
322
323 AppendSeparator();
324
325 // Append the list of tracks & via sizes
326 for( unsigned i = 0; i < bds.m_TrackWidthList.size(); i++ )
327 {
328 int width = bds.m_TrackWidthList[i];
329
330 if( i == 0 )
331 msg = _( "Track netclass width" );
332 else
333 msg.Printf( _( "Track %s" ), m_frame.MessageTextFromValue( width ) );
334
335 int menuIdx = ID_POPUP_PCB_SELECT_WIDTH1 + i;
336 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
337 Check( menuIdx, useIndex && bds.GetTrackWidthIndex() == (int) i );
338 }
339
340 AppendSeparator();
341
342 for( unsigned i = 0; i < bds.m_ViasDimensionsList.size(); i++ )
343 {
345
346 if( i == 0 )
347 msg = _( "Via netclass values" );
348 else
349 {
350 if( via.m_Drill > 0 )
351 {
352 msg.Printf( _("Via %s, hole %s" ),
353 m_frame.MessageTextFromValue( via.m_Diameter ),
354 m_frame.MessageTextFromValue( via.m_Drill ) );
355 }
356 else
357 {
358 msg.Printf( _( "Via %s" ),
359 m_frame.MessageTextFromValue( via.m_Diameter ) );
360 }
361 }
362
363 int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
364 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
365 Check( menuIdx, useIndex && bds.GetViaSizeIndex() == (int) i );
366 }
367 }
368
369 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
370 {
371 BOARD_DESIGN_SETTINGS &bds = m_frame.GetBoard()->GetDesignSettings();
372 int id = aEvent.GetId();
373
374 // On Windows, this handler can be called with an event ID not existing in any
375 // menuitem, so only set flags when we have an ID match.
376
378 {
379 bds.UseCustomTrackViaSize( true );
380 bds.m_TempOverrideTrackWidth = true;
381 m_frame.GetToolManager()->RunAction( ACT_CustomTrackWidth );
382 }
383 else if( id == ID_POPUP_PCB_SELECT_AUTO_WIDTH )
384 {
385 bds.UseCustomTrackViaSize( false );
386 bds.m_UseConnectedTrackWidth = true;
387 bds.m_TempOverrideTrackWidth = false;
388 }
390 {
391 bds.UseCustomTrackViaSize( false );
392 bds.m_UseConnectedTrackWidth = false;
393 bds.SetViaSizeIndex( 0 );
394 bds.SetTrackWidthIndex( 0 );
395 }
397 {
398 bds.UseCustomTrackViaSize( false );
400 }
402 {
403 bds.UseCustomTrackViaSize( false );
404 bds.m_TempOverrideTrackWidth = true;
406 }
407
409 }
410
411private:
413};
414
415
417{
418public:
420 ACTION_MENU( true ),
421 m_frame( aFrame )
422 {
424 SetTitle( _( "Select Differential Pair Dimensions" ) );
425 }
426
427protected:
428 ACTION_MENU* create() const override
429 {
430 return new DIFF_PAIR_MENU( m_frame );
431 }
432
433 void update() override
434 {
435 const BOARD_DESIGN_SETTINGS& bds = m_frame.GetBoard()->GetDesignSettings();
436
437 Clear();
438
439 Append( ID_POPUP_PCB_SELECT_USE_NETCLASS_DIFFPAIR, _( "Use Net Class Values" ),
440 _( "Use differential pair dimensions from the net class" ), wxITEM_CHECK );
442 !bds.UseCustomDiffPairDimensions() && bds.GetDiffPairIndex() == 0 );
443
444 Append( ID_POPUP_PCB_SELECT_CUSTOM_DIFFPAIR, _( "Use Custom Values..." ),
445 _( "Specify custom differential pair dimensions" ), wxITEM_CHECK );
447
448 AppendSeparator();
449
450 // Append the list of differential pair dimensions
451
452 // Drop index 0 which is the current netclass dimensions (which are handled above)
453 for( unsigned i = 1; i < bds.m_DiffPairDimensionsList.size(); ++i )
454 {
456 wxString msg;
457
458 if( diffPair.m_Gap <= 0 )
459 {
460 if( diffPair.m_ViaGap <= 0 )
461 {
462 msg.Printf( _( "Width %s" ),
463 m_frame.MessageTextFromValue( diffPair.m_Width ) );
464 }
465 else
466 {
467 msg.Printf( _( "Width %s, via gap %s" ),
468 m_frame.MessageTextFromValue( diffPair.m_Width ),
469 m_frame.MessageTextFromValue( diffPair.m_ViaGap ) );
470 }
471 }
472 else
473 {
474 if( diffPair.m_ViaGap <= 0 )
475 {
476 msg.Printf( _( "Width %s, gap %s" ),
477 m_frame.MessageTextFromValue( diffPair.m_Width ),
478 m_frame.MessageTextFromValue( diffPair.m_Gap ) );
479 }
480 else
481 {
482 msg.Printf( _( "Width %s, gap %s, via gap %s" ),
483 m_frame.MessageTextFromValue( diffPair.m_Width ),
484 m_frame.MessageTextFromValue( diffPair.m_Gap ),
485 m_frame.MessageTextFromValue( diffPair.m_ViaGap ) );
486 }
487 }
488
489 int menuIdx = ID_POPUP_PCB_SELECT_DIFFPAIR1 + i - 1;
490 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
491 Check( menuIdx, !bds.UseCustomDiffPairDimensions() && bds.GetDiffPairIndex() == (int) i );
492 }
493 }
494
495 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
496 {
497 BOARD_DESIGN_SETTINGS &bds = m_frame.GetBoard()->GetDesignSettings();
498 int id = aEvent.GetId();
499
500 // On Windows, this handler can be called with an event ID not existing in any
501 // menuitem, so only set flags when we have an ID match.
502
504 {
505 bds.UseCustomDiffPairDimensions( true );
506 TOOL_MANAGER* toolManager = m_frame.GetToolManager();
508 }
510 {
511 bds.UseCustomDiffPairDimensions( false );
512 bds.SetDiffPairIndex( 0 );
513 }
515 {
516 bds.UseCustomDiffPairDimensions( false );
517 // remember that the menu doesn't contain index 0 (which is the netclass values)
519 }
520
522 }
523
524private:
526};
527
528
532
533
535{
537
539
540 wxASSERT( frame );
541
542 auto& menu = m_menu->GetMenu();
543 menu.SetUntranslatedTitle( _HKI( "Interactive Router" ) );
544
545 m_trackViaMenu = std::make_shared<TRACK_WIDTH_MENU>( *frame );
546 m_trackViaMenu->SetTool( this );
547 m_menu->RegisterSubMenu( m_trackViaMenu );
548
549 m_diffPairMenu = std::make_shared<DIFF_PAIR_MENU>( *frame );
550 m_diffPairMenu->SetTool( this );
551 m_menu->RegisterSubMenu( m_diffPairMenu );
552
553 ACTION_MANAGER* mgr = frame->GetToolManager()->GetActionManager();
554
555 auto haveHighlight =
556 [this]( const SELECTION& sel )
557 {
558 KIGFX::RENDER_SETTINGS* cfg = m_toolMgr->GetView()->GetPainter()->GetSettings();
559
560 return !cfg->GetHighlightNetCodes().empty();
561 };
562
563 auto notRoutingCond =
564 [this]( const SELECTION& )
565 {
566 return !m_router->RoutingInProgress();
567 };
568
569 auto inRouteSelected =
570 [this]( const SELECTION& )
571 {
572 return m_inRouteSelected;
573 };
574
575 auto hasOtherEnd =
576 [this]( const SELECTION& )
577 {
578 std::vector<PNS::NET_HANDLE> currentNets = m_router->GetCurrentNets();
579
580 if( currentNets.empty() || currentNets[0] == nullptr )
581 return false;
582
583 // Need to have something unconnected to finish to
584 NETINFO_ITEM* netInfo = static_cast<NETINFO_ITEM*>( currentNets[0] );
585 int currentNet = netInfo->GetNetCode();
587 RN_NET* ratsnest = board->GetConnectivity()->GetRatsnestForNet( currentNet );
588
589 return ratsnest && !ratsnest->GetEdges().empty();
590 };
591
593 menu.AddItem( PCB_ACTIONS::cancelCurrentItem, inRouteSelected, 1 );
594 menu.AddSeparator( 1 );
595
596 menu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 2 );
597 menu.AddSeparator( haveHighlight, 2 );
598
599 menu.AddItem( PCB_ACTIONS::routeSingleTrack, notRoutingCond );
600 menu.AddItem( PCB_ACTIONS::routeDiffPair, notRoutingCond );
603 menu.AddItem( PCB_ACTIONS::routerContinueFromEnd, hasOtherEnd );
604 menu.AddItem( PCB_ACTIONS::routerAttemptFinish, hasOtherEnd );
605 menu.AddItem( PCB_ACTIONS::routerAutorouteSelected, notRoutingCond
607 menu.AddItem( PCB_ACTIONS::breakTrack, notRoutingCond );
608
609 menu.AddItem( PCB_ACTIONS::drag45Degree, notRoutingCond );
610 menu.AddItem( PCB_ACTIONS::dragFreeAngle, notRoutingCond );
611
619
620 // Add submenu for track corner mode handling
621 CONDITIONAL_MENU* submenuCornerMode = new CONDITIONAL_MENU( this );
622 submenuCornerMode->SetTitle( _( "Track Corner Mode" ) );
624
626 submenuCornerMode->AddSeparator( 1 );
631
632 menu.AddMenu( submenuCornerMode );
633
634 // Manage check/uncheck marks in this submenu items
635 auto cornerMode45Cond =
636 [this]( const SELECTION& )
637 {
638 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::MITERED_45;
639 };
640
641 auto cornerMode90Cond =
642 [this]( const SELECTION& )
643 {
644 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::MITERED_90;
645 };
646
647 auto cornerModeArc45Cond =
648 [this]( const SELECTION& )
649 {
650 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::ROUNDED_45;
651 };
652
653 auto cornerModeArc90Cond =
654 [this]( const SELECTION& )
655 {
656 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::ROUNDED_90;
657 };
658
659#define CHECK( x ) ACTION_CONDITIONS().Check( x )
660 mgr->SetConditions( ACT_SwitchCornerMode45, CHECK( cornerMode45Cond ) );
661 mgr->SetConditions( ACT_SwitchCornerMode90, CHECK( cornerMode90Cond ) );
662 mgr->SetConditions( ACT_SwitchCornerModeArc45, CHECK( cornerModeArc45Cond ) );
663 mgr->SetConditions( ACT_SwitchCornerModeArc90, CHECK( cornerModeArc90Cond ) );
664
665 auto diffPairCond =
666 [this]( const SELECTION& )
667 {
668 return m_router->Mode() == PNS::PNS_MODE_ROUTE_DIFF_PAIR;
669 };
670
671 menu.AddSeparator();
672
674 menu.AddMenu( m_diffPairMenu.get(), diffPairCond );
675
677
678 menu.AddSeparator();
679
680 frame->AddStandardSubMenus( *m_menu.get() );
681
682 return true;
683}
684
685
687{
688 if( aReason == RUN )
689 TOOL_BASE::Reset( aReason );
690}
691
692// Saves the complete event log and the dump of the PCB, allowing us to
693// recreate hard-to-find P&S quirks and bugs.
694
696{
697 wxString testCaseDir = ADVANCED_CFG::GetCfg().m_RouterTestCaseDirectory;
698 wxString logPath;
699 static size_t lastLoggerSize = 0;
700 static wxString mruPath;
701 PNS::LOGGER::LOG_DATA logData;
702
703 auto logger = m_router->Logger();
704
705 if( !logger || logger->GetEvents().size() == 0
706 || logger->GetEvents().size() == lastLoggerSize )
707 {
708 return;
709 }
710
711 if( !testCaseDir.IsEmpty() )
712 {
713 DIALOG_ROUTER_SAVE_TEST_CASE saveDlg( frame(), testCaseDir );
714 bool doExit = false;
715
716 if( saveDlg.ShowModal() == wxID_OK )
717 {
718 wxFileName path = wxFileName::DirName( testCaseDir );
719 path.AppendDir( saveDlg.getTestCaseName() );
720 logData.m_TestCaseType = saveDlg.getTestCaseType();
721
722 if( path.DirExists() )
723 {
724 doExit = !IsOK( frame(), wxString::Format( _("Test case in directory %s already exists. Overwrite?"), path.GetFullPath() ) );
725 }
726 else
727 {
728 path.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
729 }
730
731 path.SetName( wxT("pns") );
732 logPath = path.GetFullPath();
733 }
734 else
735 {
736 doExit = true;
737 }
738
739 if( doExit )
740 {
741 lastLoggerSize = logger->GetEvents().size(); // prevent re-entry
742 return;
743 }
744 }
745 else
746 {
747 if ( mruPath.IsEmpty() )
748 {
750 }
751
752 wxFileDialog dlg( frame(), _( "Save router log" ), mruPath, "pns.log",
753 "PNS log files" + AddFileExtListToFilter( { "log" } ),
754 wxFD_OVERWRITE_PROMPT | wxFD_SAVE );
755
757
758 if( dlg.ShowModal() != wxID_OK )
759 {
760 lastLoggerSize = logger->GetEvents().size(); // prevent re-entry
761 return;
762 }
763
764 logPath = dlg.GetPath();
765 }
766
767
768 wxFileName fname_log( logPath );
769 mruPath = fname_log.GetPath();
770 fname_log.SetExt( "log" );
771 wxLogTrace( wxT( "PNS" ), wxT( "save log to: %s" ), fname_log.GetFullPath() );
772
773 wxFileName fname_dump( fname_log );
774 fname_dump.SetExt( "dump" );
775
776 wxFileName fname_settings( fname_log );
777 fname_settings.SetExt( "settings" );
778
779 FILE* settings_f = wxFopen( fname_settings.GetAbsolutePath(), "wb" );
780
781 if( !settings_f )
782 {
783 DisplayError( frame(), wxString::Format( _( "Unable to write '%s'." ), fname_settings.GetAbsolutePath() ) );
784 return;
785 }
786
787 std::string settingsStr = m_router->Settings().FormatAsString();
788 fprintf( settings_f, "%s\n", settingsStr.c_str() );
789 fclose( settings_f );
790
791 // Export as *.kicad_pcb format, using a strategy which is specifically chosen
792 // as an example on how it could also be used to send it to the system clipboard.
793
794 PCB_IO_KICAD_SEXPR pcb_io;
795
796 pcb_io.SaveBoard( fname_dump.GetAbsolutePath(), m_iface->GetBoard(), nullptr );
797
798 PROJECT* prj = m_iface->GetBoard()->GetProject();
799 prj->GetProjectFile().SaveAs( fname_dump.GetPath(), fname_dump.GetName() );
800 prj->GetLocalSettings().SaveAs( fname_dump.GetPath(), fname_dump.GetName() );
801
802 // Copy the project's custom DRC rules
804 {
805 wxString srcRules = editFrame->GetBoard()->GetDesignRulesPath();
806
807 if( !srcRules.IsEmpty() && wxFileName::FileExists( srcRules ) )
808 {
809 wxFileName fname_rules( fname_dump );
810 fname_rules.SetExt( FILEEXT::DesignRulesFileExtension );
811 wxCopyFile( srcRules, fname_rules.GetAbsolutePath() );
812 }
813 }
814
815 // Build log file:
816 std::vector<PNS::ITEM*> removed;
817 m_router->GetUpdatedItems( removed, logData.m_AddedItems, logData.m_Heads );
818
819
820 for( auto item : removed )
821 {
822 if( item->OfKind( PNS::ITEM::HOLE_T ) )
823 continue;
824
825 wxASSERT_MSG( item->Parent() != nullptr, "removed an item with no parent uuid?" );
826
827 if( item->Parent() )
828 logData.m_RemovedItems.insert( item->Parent()->m_Uuid );
829 }
830
831 logData.m_BoardHash = IO_UTILS::fileHashMMH3( fname_dump.GetAbsolutePath() );
832
833 if( !logData.m_BoardHash ) // should never happen...
834 return;
835
836 logData.m_Mode = m_router->Mode();
837 logData.m_Events = logger->GetEvents();
838
839 FILE* log_f = wxFopen( fname_log.GetAbsolutePath(), "wb" );
840 wxString logString = PNS::LOGGER::FormatLogFileAsJSON( logData );
841
842 if( !log_f )
843 {
844 DisplayError( frame(), wxString::Format( _( "Unable to write '%s'." ),
845 fname_log.GetAbsolutePath() ) );
846 return;
847 }
848
849 fprintf( log_f, "%s\n", logString.c_str().AsChar() );
850 fclose( log_f );
851
852 logger->Clear(); // prevent re-entry
853 lastLoggerSize = 0;
854}
855
856
858{
859 if( aEvent.Category() == TC_VIEW || aEvent.Category() == TC_MOUSE )
860 {
861 BOX2D viewAreaD = getView()->GetGAL()->GetVisibleWorldExtents();
862 m_router->SetVisibleViewArea( BOX2ISafe( viewAreaD ) );
863 }
864
865 if( !ADVANCED_CFG::GetCfg().m_EnableRouterDump )
866 return;
867
868 if( !aEvent.IsKeyPressed() )
869 return;
870
871 switch( aEvent.KeyCode() )
872 {
873 case '0':
875 aEvent.SetPassEvent( false );
876 break;
877
878 default:
879 break;
880 }
881}
882
884{
885 bool asChanged = false;
886
887 if( aEvent.IsAction( &ACT_SwitchCornerModeToNext ) )
888 {
889 DIRECTION_45::CORNER_MODE curr_mode = m_router->Settings().GetCornerMode();
890
892 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_45 );
893 else if( curr_mode == DIRECTION_45::CORNER_MODE::ROUNDED_45 )
894 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_90 );
895 else if( curr_mode == DIRECTION_45::CORNER_MODE::MITERED_90 )
896 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_90 );
897 else if( curr_mode == DIRECTION_45::CORNER_MODE::ROUNDED_90 )
898 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_45 );
899
900 asChanged = true;
901 }
902 else if( aEvent.IsAction( &ACT_SwitchCornerMode45 ) )
903 {
904 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_45 );
905 asChanged = true;
906 }
907 else if( aEvent.IsAction( &ACT_SwitchCornerModeArc45 ) )
908 {
909 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_45 );
910 asChanged = true;
911 }
912 else if( aEvent.IsAction( &ACT_SwitchCornerMode90 ) )
913 {
914 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_90 );
915 asChanged = true;
916 }
917 else if( aEvent.IsAction( &ACT_SwitchCornerModeArc90 ) )
918 {
919 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_90 );
920 asChanged = true;
921 }
922
923 if( asChanged )
924 {
926 updateEndItem( aEvent );
927 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
928 }
929
930 return 0;
931}
932
933
935{
936 PCB_LAYER_ID tl = static_cast<PCB_LAYER_ID>( getView()->GetTopLayer() );
937
938 if( m_startItem )
939 {
940 int startLayer = m_iface->GetPNSLayerFromBoardLayer( tl );
941 const PNS_LAYER_RANGE& ls = m_startItem->Layers();
942
943 if( ls.Overlaps( startLayer ) )
944 return tl;
945 else
946 return m_iface->GetBoardLayerFromPNSLayer( ls.Start() );
947 }
948
949 return tl;
950}
951
952
954{
955 int activeLayer = m_iface->GetPNSLayerFromBoardLayer( frame()->GetActiveLayer() );
956 int currentLayer = m_router->GetCurrentLayer();
957
958 if( currentLayer != activeLayer )
959 m_router->SwitchLayer( activeLayer );
960
961 std::optional<int> newLayer = m_router->Sizes().PairedLayer( currentLayer );
962
963 if( !newLayer )
964 newLayer = m_router->Sizes().GetLayerTop();
965
966 m_router->SwitchLayer( *newLayer );
967 m_lastTargetLayer = m_iface->GetBoardLayerFromPNSLayer( *newLayer );
968
971}
972
973
974// N.B. aTargetLayer is a PNS layer, not a PCB_LAYER_ID
975void ROUTER_TOOL::updateSizesAfterRouterEvent( int aTargetLayer, const VECTOR2I& aPos )
976{
977 std::vector<PNS::NET_HANDLE> nets = m_router->GetCurrentNets();
978
979 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
981 std::shared_ptr<DRC_ENGINE>& drcEngine = bds.m_DRCEngine;
982 DRC_CONSTRAINT constraint;
983 PCB_LAYER_ID targetLayer = m_iface->GetBoardLayerFromPNSLayer( aTargetLayer );
984
985 PCB_TRACK dummyTrack( board() );
986 dummyTrack.SetFlags( ROUTER_TRANSIENT );
987 dummyTrack.SetLayer( targetLayer );
988 dummyTrack.SetNet( nets.empty() ? nullptr: static_cast<NETINFO_ITEM*>( nets[0] ) );
989 dummyTrack.SetStart( aPos );
990 dummyTrack.SetEnd( dummyTrack.GetStart() );
991
992 constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, &dummyTrack, nullptr, targetLayer );
993
994 if( constraint.m_Value.Min() >= bds.m_MinClearance )
995 {
996 sizes.SetClearance( constraint.m_Value.Min() );
997 sizes.SetClearanceSource( constraint.GetName() );
998 }
999 else
1000 {
1001 sizes.SetClearance( bds.m_MinClearance );
1002 sizes.SetClearanceSource( _( "board minimum clearance" ) );
1003 }
1004
1005 if( bds.UseNetClassTrack() || !sizes.TrackWidthIsExplicit() )
1006 {
1007 constraint = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, &dummyTrack, nullptr,
1008 targetLayer );
1009
1010 if( !constraint.IsNull() )
1011 {
1012 int width = sizes.TrackWidth();
1013
1014 // Only change the size if we're explicitly using the net class, or we're out of range
1015 // for our new constraints. Otherwise, just leave the track width alone so we don't
1016 // change for no reason.
1017 if( bds.UseNetClassTrack()
1018 || ( width < bds.m_TrackMinWidth )
1019 || ( width < constraint.m_Value.Min() )
1020 || ( width > constraint.m_Value.Max() ) )
1021 {
1022 sizes.SetTrackWidth( std::max( bds.m_TrackMinWidth, constraint.m_Value.Opt() ) );
1023 }
1024
1025 if( sizes.TrackWidth() == constraint.m_Value.Opt() )
1026 sizes.SetWidthSource( constraint.GetName() );
1027 else if( sizes.TrackWidth() == bds.m_TrackMinWidth )
1028 sizes.SetWidthSource( _( "board minimum track width" ) );
1029 else
1030 sizes.SetWidthSource( _( "existing track" ) );
1031 }
1032 }
1033
1034 if( nets.size() >= 2 && ( bds.UseNetClassDiffPair() || !sizes.TrackWidthIsExplicit() ) )
1035 {
1036 PCB_TRACK dummyTrackB( board() );
1037 dummyTrackB.SetFlags( ROUTER_TRANSIENT );
1038 dummyTrackB.SetLayer( targetLayer );
1039 dummyTrackB.SetNet( static_cast<NETINFO_ITEM*>( nets[1] ) );
1040 dummyTrackB.SetStart( aPos );
1041 dummyTrackB.SetEnd( dummyTrackB.GetStart() );
1042
1043 constraint = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, &dummyTrack, &dummyTrackB,
1044 targetLayer );
1045
1046 if( !constraint.IsNull() )
1047 {
1048 if( bds.UseNetClassDiffPair()
1049 || ( sizes.DiffPairWidth() < bds.m_TrackMinWidth )
1050 || ( sizes.DiffPairWidth() < constraint.m_Value.Min() )
1051 || ( sizes.DiffPairWidth() > constraint.m_Value.Max() ) )
1052 {
1053 sizes.SetDiffPairWidth( std::max( bds.m_TrackMinWidth, constraint.m_Value.Opt() ) );
1054 }
1055
1056 if( sizes.DiffPairWidth() == constraint.m_Value.Opt() )
1057 sizes.SetDiffPairWidthSource( constraint.GetName() );
1058 else
1059 sizes.SetDiffPairWidthSource( _( "board minimum track width" ) );
1060 }
1061
1062 constraint = drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, &dummyTrack, &dummyTrackB,
1063 targetLayer );
1064
1065 if( !constraint.IsNull() )
1066 {
1067 if( bds.UseNetClassDiffPair()
1068 || ( sizes.DiffPairGap() < bds.m_MinClearance )
1069 || ( sizes.DiffPairGap() < constraint.m_Value.Min() )
1070 || ( sizes.DiffPairGap() > constraint.m_Value.Max() ) )
1071 {
1072 sizes.SetDiffPairGap( std::max( bds.m_MinClearance, constraint.m_Value.Opt() ) );
1073 }
1074
1075 if( sizes.DiffPairGap() == constraint.m_Value.Opt() )
1076 sizes.SetDiffPairGapSource( constraint.GetName() );
1077 else
1078 sizes.SetDiffPairGapSource( _( "board minimum clearance" ) );
1079 }
1080 }
1081
1082 m_router->UpdateSizes( sizes );
1083}
1084
1085
1086static VIATYPE getViaTypeFromFlags( int aFlags )
1087{
1088 switch( aFlags & VIA_ACTION_FLAGS::VIA_MASK )
1089 {
1091 return VIATYPE::THROUGH;
1093 return VIATYPE::BLIND;
1095 return VIATYPE::BURIED;
1097 return VIATYPE::MICROVIA;
1098 default:
1099 wxASSERT_MSG( false, wxT( "Unhandled via type" ) );
1100 return VIATYPE::THROUGH;
1101 }
1102}
1103
1104
1106{
1107 handleLayerSwitch( aEvent, false );
1109
1110 return 0;
1111}
1112
1113
1115{
1116 if( !m_router->IsPlacingVia() )
1117 {
1118 return handleLayerSwitch( aEvent, true );
1119 }
1120 else
1121 {
1122 m_router->ToggleViaPlacement();
1123 frame()->SetActiveLayer(
1124 m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() ) );
1125 updateEndItem( aEvent );
1127 }
1128
1130 return 0;
1131}
1132
1133
1134int ROUTER_TOOL::handleLayerSwitch( const TOOL_EVENT& aEvent, bool aForceVia )
1135{
1136 wxCHECK( m_router, 0 );
1137
1138 if( !IsToolActive() )
1139 return 0;
1140
1141 // Ensure PNS_KICAD_IFACE (m_iface) m_board member is up to date
1142 // For some reason, this is not always the case
1143 m_iface->SetBoard( board() );
1144
1145 // First see if this is one of the switch layer commands
1146 BOARD* brd = board();
1147 LSET enabledLayers = LSET::AllCuMask( brd->GetDesignSettings().GetCopperLayerCount() );
1148 LSEQ layers = enabledLayers.UIOrder();
1149
1150 // These layers are in Board Layer UI order not PNS layer order
1151 PCB_LAYER_ID currentLayer = m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() );
1152 PCB_LAYER_ID targetLayer = UNDEFINED_LAYER;
1153
1154 if( aEvent.IsAction( &PCB_ACTIONS::layerNext ) )
1155 {
1156 size_t idx = 0;
1157 size_t target_idx = 0;
1158
1159 for( size_t i = 0; i < layers.size(); i++ )
1160 {
1161 if( layers[i] == currentLayer )
1162 {
1163 idx = i;
1164 break;
1165 }
1166 }
1167
1168 target_idx = ( idx + 1 ) % layers.size();
1169 // issue: #14480
1170 // idx + 1 layer may be invisible, switches to next visible layer
1171 for( size_t i = 0; i < layers.size() - 1; i++ )
1172 {
1173 if( brd->IsLayerVisible( layers[target_idx] ) )
1174 {
1175 targetLayer = layers[target_idx];
1176 break;
1177 }
1178 target_idx += 1;
1179
1180 if( target_idx >= layers.size() )
1181 {
1182 target_idx = 0;
1183 }
1184 }
1185
1186 if( targetLayer == UNDEFINED_LAYER )
1187 {
1188 // if there is no visible layers
1189 return 0;
1190 }
1191 }
1192 else if( aEvent.IsAction( &PCB_ACTIONS::layerPrev ) )
1193 {
1194 size_t idx = 0;
1195 size_t target_idx = 0;
1196
1197 for( size_t i = 0; i < layers.size(); i++ )
1198 {
1199 if( layers[i] == currentLayer )
1200 {
1201 idx = i;
1202 break;
1203 }
1204 }
1205
1206 target_idx = ( idx > 0 ) ? ( idx - 1 ) : ( layers.size() - 1 );
1207
1208 for( size_t i = 0; i < layers.size() - 1; i++ )
1209 {
1210 if( brd->IsLayerVisible( layers[target_idx] ) )
1211 {
1212 targetLayer = layers[target_idx];
1213 break;
1214 }
1215
1216 if( target_idx > 0 )
1217 target_idx -= 1;
1218 else
1219 target_idx = layers.size() - 1;
1220 }
1221
1222 if( targetLayer == UNDEFINED_LAYER )
1223 {
1224 // if there is no visible layers
1225 return 0;
1226 }
1227 }
1228 else if( aEvent.IsAction( &PCB_ACTIONS::layerToggle ) )
1229 {
1230 PCB_SCREEN* screen = frame()->GetScreen();
1231
1232 if( currentLayer == screen->m_Route_Layer_TOP )
1233 targetLayer = screen->m_Route_Layer_BOTTOM;
1234 else
1235 targetLayer = screen->m_Route_Layer_TOP;
1236 }
1238 {
1239 targetLayer = aEvent.Parameter<PCB_LAYER_ID>();
1240
1241 if( !enabledLayers.test( targetLayer ) )
1242 return 0;
1243 }
1244
1245 if( targetLayer != UNDEFINED_LAYER )
1246 {
1247 if( targetLayer == currentLayer )
1248 return 0;
1249
1250 if( !aForceVia && m_router && m_router->SwitchLayer( m_iface->GetPNSLayerFromBoardLayer( targetLayer ) ) )
1251 {
1252 updateEndItem( aEvent );
1253 updateSizesAfterRouterEvent( m_iface->GetPNSLayerFromBoardLayer( targetLayer ), m_endSnapPoint );
1254 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
1255 return 0;
1256 }
1257 }
1258
1260
1261 PCB_LAYER_ID pairTop = frame()->GetScreen()->m_Route_Layer_TOP;
1262 PCB_LAYER_ID pairBottom = frame()->GetScreen()->m_Route_Layer_BOTTOM;
1263
1264 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1265
1266 VIATYPE viaType = VIATYPE::THROUGH;
1267 bool selectLayer = false;
1268
1269 // Otherwise it is one of the router-specific via commands
1270 if( targetLayer == UNDEFINED_LAYER )
1271 {
1272 const int actViaFlags = aEvent.Parameter<int>();
1273 selectLayer = actViaFlags & VIA_ACTION_FLAGS::SELECT_LAYER;
1274
1275 viaType = getViaTypeFromFlags( actViaFlags );
1276
1277 // ask the user for a target layer
1278 if( selectLayer )
1279 {
1280 // When the currentLayer is undefined, trying to place a via does not work
1281 // because it means there is no track in progress, and some other variables
1282 // values are not defined like m_endSnapPoint. So do not continue.
1283 if( currentLayer == UNDEFINED_LAYER )
1284 return 0;
1285
1286 wxPoint endPoint = ToWxPoint( view()->ToScreen( m_endSnapPoint ) );
1287 endPoint = frame()->GetCanvas()->ClientToScreen( endPoint );
1288
1289 // Build the list of not allowed layer for the target layer
1290 LSET not_allowed_ly = LSET::AllNonCuMask();
1291
1292 if( viaType != VIATYPE::THROUGH )
1293 not_allowed_ly.set( currentLayer );
1294
1295 targetLayer = frame()->SelectOneLayer( static_cast<PCB_LAYER_ID>( currentLayer ),
1296 not_allowed_ly, endPoint );
1297
1298 // Reset the cursor to the end of the track
1300
1301 if( targetLayer == UNDEFINED_LAYER ) // canceled by user
1302 return 0;
1303
1304 // One cannot place a blind/buried via on only one layer:
1305 if( viaType != VIATYPE::THROUGH )
1306 {
1307 if( currentLayer == targetLayer )
1308 return 0;
1309 }
1310 }
1311 }
1312
1313 // fixme: P&S supports more than one fixed layer pair. Update the dialog?
1314 sizes.ClearLayerPairs();
1315
1316 // Convert blind/buried via to a through hole one, if it goes through all layers
1317 if( viaType != VIATYPE::THROUGH
1318 && ( ( targetLayer == B_Cu && currentLayer == F_Cu )
1319 || ( targetLayer == F_Cu && currentLayer == B_Cu ) ) )
1320 {
1321 viaType = VIATYPE::THROUGH;
1322 }
1323
1324 if( targetLayer == UNDEFINED_LAYER )
1325 {
1326 // Implicit layer selection
1327 if( viaType == VIATYPE::THROUGH )
1328 {
1329 // Try to switch to the nearest ratnest item's layer if we have one
1330 VECTOR2I otherEnd;
1331 PNS_LAYER_RANGE otherEndLayers;
1332 PNS::ITEM* otherEndItem = nullptr;
1333
1334 if( !m_router->GetNearestRatnestAnchor( otherEnd, otherEndLayers, otherEndItem ) )
1335 {
1336 // use the default layer pair
1337 currentLayer = pairTop;
1338 targetLayer = pairBottom;
1339 }
1340 else
1341 {
1342 // use the layer of the other end, unless it is the same layer as the currently active layer, in which
1343 // case use the layer pair (if applicable)
1344 PCB_LAYER_ID otherEndLayerPcbId = m_iface->GetBoardLayerFromPNSLayer( otherEndLayers.Start() );
1345 const std::optional<int> pairedLayerPns = m_router->Sizes().PairedLayer( m_router->GetCurrentLayer() );
1346
1347 if( currentLayer == otherEndLayerPcbId && pairedLayerPns.has_value() )
1348 {
1349 // Closest ratsnest layer is the same as the active layer - assume the via is being placed for
1350 // other routing reasons and switch the layer
1351 targetLayer = m_iface->GetBoardLayerFromPNSLayer( *pairedLayerPns );
1352 }
1353 else
1354 {
1355 targetLayer = m_iface->GetBoardLayerFromPNSLayer( otherEndLayers.Start() );
1356 }
1357 }
1358 }
1359 else
1360 {
1361 if( currentLayer == pairTop || currentLayer == pairBottom )
1362 {
1363 // the current layer is on the defined layer pair,
1364 // swap to the other side
1365 currentLayer = pairTop;
1366 targetLayer = pairBottom;
1367 }
1368 else
1369 {
1370 // the current layer is not part of the current layer pair,
1371 // so fallback and swap to the top layer of the pair by default
1372 targetLayer = pairTop;
1373 }
1374
1375 // Do not create a broken via (i.e. a via on only one copper layer)
1376 if( currentLayer == targetLayer )
1377 {
1378 WX_INFOBAR* infobar = frame()->GetInfoBar();
1379 infobar->ShowMessageFor( _( "Via needs 2 different layers." ),
1380 2000, wxICON_ERROR,
1381 WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION );
1382 return 0;
1383 }
1384 }
1385 }
1386
1387 sizes.SetViaDiameter( bds.m_ViasMinSize );
1388 sizes.SetViaDrill( bds.m_MinThroughDrill );
1389
1390 if( bds.UseNetClassVia() || viaType == VIATYPE::MICROVIA )
1391 {
1392 PCB_VIA dummyVia( board() );
1393 dummyVia.SetViaType( viaType );
1394 dummyVia.SetLayerPair( currentLayer, targetLayer );
1395
1396 if( !m_router->GetCurrentNets().empty() )
1397 dummyVia.SetNet( static_cast<NETINFO_ITEM*>( m_router->GetCurrentNets()[0] ) );
1398
1399 DRC_CONSTRAINT constraint;
1400
1401 constraint = bds.m_DRCEngine->EvalRules( VIA_DIAMETER_CONSTRAINT, &dummyVia, nullptr,
1402 currentLayer );
1403
1404 if( !constraint.IsNull() )
1405 sizes.SetViaDiameter( constraint.m_Value.Opt() );
1406
1407 constraint = bds.m_DRCEngine->EvalRules( HOLE_SIZE_CONSTRAINT, &dummyVia, nullptr,
1408 currentLayer );
1409
1410 if( !constraint.IsNull() )
1411 sizes.SetViaDrill( constraint.m_Value.Opt() );
1412 }
1413 else
1414 {
1415 sizes.SetViaDiameter( bds.GetCurrentViaSize() );
1416 sizes.SetViaDrill( bds.GetCurrentViaDrill() );
1417 }
1418
1419 sizes.SetViaType( viaType );
1420 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( currentLayer ),
1421 m_iface->GetPNSLayerFromBoardLayer( targetLayer ) );
1422
1423 m_router->UpdateSizes( sizes );
1424
1425 if( !m_router->IsPlacingVia() )
1426 m_router->ToggleViaPlacement();
1427
1428 if( m_router->RoutingInProgress() )
1429 {
1430 updateEndItem( aEvent );
1432 }
1433 else
1434 {
1435 updateStartItem( aEvent );
1436 }
1437
1438 return 0;
1439}
1440
1441
1443{
1446 int pnsLayer = m_iface->GetPNSLayerFromBoardLayer( pcbLayer );
1447
1448 if( !::IsCopperLayer( pcbLayer ) )
1449 {
1450 editFrame->ShowInfoBarError( _( "Tracks on Copper layers only." ) );
1451 return false;
1452 }
1453
1455 editFrame->SetActiveLayer( pcbLayer );
1456
1457 if( !getView()->IsLayerVisible( pcbLayer ) )
1458 {
1459 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1460 editFrame->GetCanvas()->Refresh();
1461 }
1462
1463 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
1464
1465 m_iface->SetStartLayerFromPCBNew( pcbLayer );
1466
1467 frame()->GetBoard()->GetDesignSettings().m_TempOverrideTrackWidth = false;
1468 m_iface->ImportSizes( sizes, m_startItem, nullptr, aStartPosition );
1469 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_TOP ),
1470 m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_BOTTOM ) );
1471
1472 m_router->UpdateSizes( sizes );
1473
1474 if( m_startItem && m_startItem->Net() )
1475 {
1477 {
1478 if( PNS::NET_HANDLE coupledNet = m_router->GetRuleResolver()->DpCoupledNet( m_startItem->Net() ) )
1479 highlightNets( true, { m_startItem->Net(), coupledNet } );
1480 }
1481 else
1482 {
1483 highlightNets( true, { m_startItem->Net() } );
1484 }
1485 }
1486
1487 controls()->SetAutoPan( true );
1488
1489 if( !m_router->StartRouting( m_startSnapPoint, m_startItem, pnsLayer ) )
1490 {
1491 // It would make more sense to leave the net highlighted as the higher-contrast mode
1492 // makes the router clearances more visible. However, since we just started routing
1493 // the conversion of the screen from low contrast to high contrast is a bit jarring and
1494 // makes the infobar coming up less noticeable.
1495 highlightNets( false );
1496
1497 frame()->ShowInfoBarError( m_router->FailureReason(), true,
1498 [&]()
1499 {
1500 m_router->ClearViewDecorations();
1501 } );
1502
1503 controls()->SetAutoPan( false );
1504 return false;
1505 }
1506
1507 m_endItem = nullptr;
1509
1511 frame()->UndoRedoBlock( true );
1512
1513 return true;
1514}
1515
1516
1518{
1519 m_router->StopRouting();
1520
1521 m_startItem = nullptr;
1522 m_endItem = nullptr;
1523
1524 frame()->SetActiveLayer( m_originalActiveLayer );
1526 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1527 controls()->SetAutoPan( false );
1528 controls()->ForceCursorPosition( false );
1529 frame()->UndoRedoBlock( false );
1530 highlightNets( false );
1531
1532 return true;
1533}
1534
1535
1537{
1538 m_router->ClearViewDecorations();
1539
1540 if( !prepareInteractive( aStartPosition ) )
1541 return;
1542
1543 auto setCursor =
1544 [&]()
1545 {
1546 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1547 };
1548
1549 auto syncRouterAndFrameLayer =
1550 [&]()
1551 {
1552 int pnsLayer = m_router->GetCurrentLayer();
1553 PCB_LAYER_ID pcbLayer = m_iface->GetBoardLayerFromPNSLayer( pnsLayer );
1555
1556 editFrame->SetActiveLayer( pcbLayer );
1557
1558 if( !getView()->IsLayerVisible( pcbLayer ) )
1559 {
1560 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1561 editFrame->GetCanvas()->Refresh();
1562 }
1563 };
1564
1565 // Set initial cursor
1566 setCursor();
1567
1568 // If the user pressed 'V' before starting to route, enable via placement now
1569 if( m_startWithVia )
1570 {
1571 m_startWithVia = false;
1572 handleLayerSwitch( ACT_PlaceThroughVia.MakeEvent(), true );
1573 }
1574
1575 // Throttle wxEVT_UPDATE_UI during routing. The idle sweep fires between every Wait()
1576 // iteration and its cost dominates at interactive frame rates.
1577 UI_UPDATE_INTERVAL_GUARD uiGuard( 200 );
1578
1579 while( TOOL_EVENT* evt = Wait() )
1580 {
1581 setCursor();
1582
1583 // Don't crash if we missed an operation that canceled routing.
1584 if( !m_router->RoutingInProgress() )
1585 {
1586 if( evt->IsCancelInteractive() )
1587 m_cancelled = true;
1588
1589 break;
1590 }
1591
1592 handleCommonEvents( *evt );
1593
1594 if( evt->IsMotion() )
1595 {
1596 updateEndItem( *evt );
1598 }
1599 else if( evt->IsAction( &PCB_ACTIONS::routerUndoLastSegment )
1600 || evt->IsAction( &ACTIONS::doDelete )
1601 || evt->IsAction( &ACTIONS::undo ) )
1602 {
1603 if( std::optional<VECTOR2I> last = m_router->UndoLastSegment() )
1604 {
1605 getViewControls()->WarpMouseCursor( last.value(), true );
1606 evt->SetMousePosition( last.value() );
1607 }
1608
1609 updateEndItem( *evt );
1611 }
1612 else if( evt->IsAction( &PCB_ACTIONS::routerAttemptFinish ) )
1613 {
1614 if( m_toolMgr->IsContextMenuActive() )
1615 m_toolMgr->WarpAfterContextMenu();
1616
1617 bool* autoRouted = evt->Parameter<bool*>();
1618
1619 if( m_router->Finish() )
1620 {
1621 // When we're routing a group of signals automatically we want
1622 // to break up the undo stack every time we have to manually route
1623 // so the user gets nice checkpoints. Remove the APPEND_UNDO flag.
1624 if( autoRouted != nullptr )
1625 *autoRouted = true;
1626
1627 break;
1628 }
1629 else
1630 {
1631 // This acts as check if we were called by the autorouter; we don't want
1632 // to reset APPEND_UNDO if we're auto finishing after route-other-end
1633 if( autoRouted != nullptr )
1634 {
1635 *autoRouted = false;
1636 m_iface->SetCommitFlags( 0 );
1637 }
1638
1639 // Warp the mouse so the user is at the point we managed to route to
1640 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1641 }
1642 }
1643 else if( evt->IsAction( &PCB_ACTIONS::routerContinueFromEnd ) )
1644 {
1645 bool needsAppend = m_router->Placer()->HasPlacedAnything();
1646
1647 if( m_router->ContinueFromEnd( &m_startItem ) )
1648 {
1649 syncRouterAndFrameLayer();
1650 m_startSnapPoint = m_router->Placer()->CurrentStart();
1651 updateEndItem( *evt );
1652
1653 // Warp the mouse to wherever we actually ended up routing to
1654 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1655
1656 // We want the next router commit to be one undo at the UI layer
1657 m_iface->SetCommitFlags( needsAppend ? APPEND_UNDO : 0 );
1658 }
1659 else
1660 {
1661 frame()->ShowInfoBarError( m_router->FailureReason(), true );
1662 }
1663 }
1664 else if( evt->IsClick( BUT_LEFT )
1665 || evt->IsDrag( BUT_LEFT )
1666 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack ) )
1667 {
1668 updateEndItem( *evt );
1669 bool needLayerSwitch = m_router->IsPlacingVia();
1670 bool forceCommit = false;
1671
1672 if( m_router->FixRoute( m_endSnapPoint, m_endItem, false, forceCommit ) )
1673 break;
1674
1675 if( needLayerSwitch )
1676 {
1678 }
1679 else
1680 {
1682 }
1683
1684 // Synchronize the indicated layer
1685 syncRouterAndFrameLayer();
1686
1687 updateEndItem( *evt );
1689 m_startItem = nullptr;
1690 }
1691 else if( evt->IsAction( &ACT_SwitchPosture ) )
1692 {
1693 m_router->FlipPosture();
1694 updateEndItem( *evt );
1695 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
1696 }
1697 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
1698 {
1699 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1700 controls()->SetAutoPan( false );
1701 {
1702 m_toolMgr->RunAction( ACT_CustomTrackWidth );
1703 }
1704 controls()->SetAutoPan( true );
1705 setCursor();
1707 }
1708 else if( evt->IsAction( &ACTIONS::finishInteractive ) || evt->IsDblClick( BUT_LEFT ) )
1709 {
1710 // Stop current routing:
1711 bool forceFinish = true;
1712 bool forceCommit = false;
1713
1714 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
1715 break;
1716 }
1717 else if( evt->IsCancelInteractive() || evt->IsAction( &PCB_ACTIONS::cancelCurrentItem )
1718 || evt->IsActivate()
1719 || evt->IsAction( &PCB_ACTIONS::routerInlineDrag ) )
1720 {
1721 if( evt->IsCancelInteractive() && ( m_inRouteSelected || !m_router->RoutingInProgress() ) )
1722 m_cancelled = true;
1723
1724 if( evt->IsActivate() && !evt->IsMoveTool() )
1725 m_cancelled = true;
1726
1727 break;
1728 }
1729 else if( evt->IsUndoRedo() )
1730 {
1731 // We're in an UndoRedoBlock. If we get here, something's broken.
1732 wxFAIL;
1733 break;
1734 }
1735 else if( evt->IsClick( BUT_RIGHT ) )
1736 {
1737 m_menu->ShowContextMenu( selection() );
1738 }
1739 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
1740 // but we don't at present have that, so we just knock out some of the egregious ones.
1741 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
1742 {
1743 wxBell();
1744 }
1745 else
1746 {
1747 evt->SetPassEvent();
1748 }
1749 }
1750
1751 m_router->CommitRouting();
1752 // Reset to normal for next route
1753 m_iface->SetCommitFlags( 0 );
1754
1756}
1757
1758
1760{
1761 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1762 DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( frame(), sizes );
1763
1764 if( settingsDlg.ShowModal() == wxID_OK )
1765 {
1766 m_router->UpdateSizes( sizes );
1767 m_savedSizes = sizes;
1768
1769 BOARD_DESIGN_SETTINGS& bds = frame()->GetBoard()->GetDesignSettings();
1771 bds.SetCustomDiffPairGap( sizes.DiffPairGap() );
1773 }
1774
1775 return 0;
1776}
1777
1778
1780{
1781 DIALOG_PNS_SETTINGS settingsDlg( frame(), m_router->Settings() );
1782
1783 settingsDlg.ShowModal();
1784
1786
1787 return 0;
1788}
1789
1790
1792{
1793 PNS::PNS_MODE mode = aEvent.Parameter<PNS::PNS_MODE>();
1794 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
1795
1796 settings.SetMode( mode );
1798
1799 return 0;
1800}
1801
1802
1804{
1805 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
1806 PNS::PNS_MODE mode = settings.Mode();
1807
1808 switch( mode )
1809 {
1810 case PNS::RM_MarkObstacles: mode = PNS::RM_Shove; break;
1811 case PNS::RM_Shove: mode = PNS::RM_Walkaround; break;
1812 case PNS::RM_Walkaround: mode = PNS::RM_MarkObstacles; break;
1813 }
1814
1815 settings.SetMode( mode );
1817
1818 return 0;
1819}
1820
1821
1823{
1824 return m_router->Settings().Mode();
1825}
1826
1827
1829{
1830 return m_router->RoutingInProgress();
1831}
1832
1833
1835{
1836 if( !m_startItem )
1837 return;
1838
1840 m_router->BreakSegmentOrArc( m_startItem, m_startSnapPoint );
1841}
1842
1843
1845{
1849 PCB_LAYER_ID originalLayer = frame->GetActiveLayer();
1850 bool autoRoute = aEvent.Matches( PCB_ACTIONS::routerAutorouteSelected.MakeEvent() );
1851 bool otherEnd = aEvent.Matches( PCB_ACTIONS::routerRouteSelectedFromEnd.MakeEvent() );
1852
1853 if( m_router->RoutingInProgress() )
1854 return 0;
1855
1856 // Save selection then clear it for interactive routing
1857 PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
1858
1859 if( selection.Size() == 0 )
1860 return 0;
1861
1862 m_toolMgr->RunAction( ACTIONS::selectionClear );
1863
1864 TOOL_EVENT pushedEvent = aEvent;
1865 frame->PushTool( aEvent );
1866
1867 auto setCursor =
1868 [&]()
1869 {
1870 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1871 };
1872
1873 Activate();
1874 m_inRouteSelected = true;
1875
1876 // Must be done after Activate() so that it gets set into the correct context
1877 controls->ShowCursor( true );
1878 controls->ForceCursorPosition( false );
1879 // Set initial cursor
1880 setCursor();
1881
1882 // Get all connected board items, adding pads for any footprints selected
1883 std::vector<BOARD_CONNECTED_ITEM*> itemList;
1884
1885 for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() )
1886 {
1887 if( item->Type() == PCB_FOOTPRINT_T )
1888 {
1889 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1890 itemList.push_back( pad );
1891 }
1892 else if( dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) != nullptr )
1893 {
1894 itemList.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1895 }
1896 }
1897
1898 std::shared_ptr<CONNECTIVITY_DATA> connectivity = frame->GetBoard()->GetConnectivity();
1899
1900 // For putting sequential tracks that successfully autoroute into one undo commit
1901 bool groupStart = true;
1902 m_cancelled = false;
1903
1904 for( BOARD_CONNECTED_ITEM* item : itemList )
1905 {
1906 // This code is similar to GetRatsnestForPad() but it only adds the anchor for
1907 // the side of the connectivity on this pad. It also checks for ratsnest points
1908 // inside the pad (like a trace end) and counts them.
1909 RN_NET* net = connectivity->GetRatsnestForNet( item->GetNetCode() );
1910
1911 if( !net )
1912 continue;
1913
1914 std::vector<std::shared_ptr<const CN_ANCHOR>> anchors;
1915
1916 for( const CN_EDGE& edge : net->GetEdges() )
1917 {
1918 std::shared_ptr<const CN_ANCHOR> target = edge.GetTargetNode();
1919 std::shared_ptr<const CN_ANCHOR> source = edge.GetSourceNode();
1920
1921 if( !source || source->Dirty() || !target || target->Dirty() )
1922 continue;
1923
1924 if( source->Parent() == item )
1925 anchors.push_back( source );
1926 else if( target->Parent() == item )
1927 anchors.push_back( target );
1928 }
1929
1930 // Route them
1931 for( std::shared_ptr<const CN_ANCHOR> anchor : anchors )
1932 {
1933 if( !anchor->Valid() )
1934 continue;
1935
1936 // Try to return to the original layer as indicating the user's preferred
1937 // layer for autorouting tracks. The layer can be changed by the user to
1938 // finish tracks that can't complete automatically, but should be changed
1939 // back after.
1940 if( frame->GetActiveLayer() != originalLayer )
1941 frame->SetActiveLayer( originalLayer );
1942
1943 m_startItem = m_router->GetWorld()->FindItemByParent( anchor->Parent() );
1944 m_startSnapPoint = anchor->Pos();
1945 m_router->SetMode( mode );
1946
1947 // Prime the interactive routing to attempt finish if we are autorouting
1948 bool autoRouted = false;
1949
1950 if( autoRoute )
1951 m_toolMgr->PostAction( PCB_ACTIONS::routerAttemptFinish, &autoRouted );
1952 else if( otherEnd )
1954
1955 // We want autorouted tracks to all be in one undo group except for
1956 // any tracks that need to be manually finished.
1957 // The undo appending for manually finished tracks is handled in peformRouting()
1958 if( groupStart )
1959 groupStart = false;
1960 else
1961 m_iface->SetCommitFlags( APPEND_UNDO );
1962
1963 // Start interactive routing. Will automatically finish if possible.
1965
1966 if( m_cancelled )
1967 break;
1968
1969 // Route didn't complete automatically, need to a new undo commit
1970 // for the next line so those can group as far as they autoroute
1971 if( !autoRouted )
1972 groupStart = true;
1973 }
1974
1975 if( m_cancelled )
1976 break;
1977 }
1978
1979 m_iface->SetCommitFlags( 0 );
1980 frame->PopTool( pushedEvent );
1981 m_inRouteSelected = false;
1982 return 0;
1983}
1984
1985
1987{
1988 if( m_inRouterTool )
1989 return 0;
1990
1992
1996
1997 if( m_router->RoutingInProgress() )
1998 {
1999 if( m_router->Mode() == mode )
2000 return 0;
2001 else
2002 m_router->StopRouting();
2003 }
2004
2005 // Deselect all items
2006 m_toolMgr->RunAction( ACTIONS::selectionClear );
2007
2008 TOOL_EVENT pushedEvent = aEvent;
2009 frame->PushTool( aEvent );
2010
2011 auto setCursor =
2012 [&]()
2013 {
2014 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2015 };
2016
2017 Activate();
2018 // Must be done after Activate() so that it gets set into the correct context
2019 controls->ShowCursor( true );
2020 controls->ForceCursorPosition( false );
2021 // Set initial cursor
2022 setCursor();
2023
2024 m_router->SetMode( mode );
2025 m_cancelled = false;
2026 m_startWithVia = false;
2027
2028 if( aEvent.HasPosition() )
2029 m_toolMgr->PrimeTool( aEvent.Position() );
2030
2031 // Main loop: keep receiving events
2032 while( TOOL_EVENT* evt = Wait() )
2033 {
2034 if( !evt->IsDrag() )
2035 setCursor();
2036
2037 if( evt->IsCancelInteractive() )
2038 {
2039 frame->PopTool( pushedEvent );
2040 break;
2041 }
2042 else if( evt->IsActivate() )
2043 {
2044 if( evt->IsMoveTool() || evt->IsEditorTool() )
2045 {
2046 // leave ourselves on the stack so we come back after the move
2047 break;
2048 }
2049 else
2050 {
2051 frame->PopTool( pushedEvent );
2052 break;
2053 }
2054 }
2055 else if( evt->Action() == TA_UNDO_REDO_PRE )
2056 {
2057 m_router->ClearWorld();
2058 }
2059 else if( evt->Action() == TA_UNDO_REDO_POST || evt->Action() == TA_MODEL_CHANGE )
2060 {
2061 m_router->SyncWorld();
2062 }
2063 else if( evt->IsMotion() )
2064 {
2065 updateStartItem( *evt );
2066 }
2067 else if( evt->IsAction( &PCB_ACTIONS::dragFreeAngle ) )
2068 {
2069 updateStartItem( *evt, true );
2071 }
2072 else if( evt->IsAction( &PCB_ACTIONS::drag45Degree ) )
2073 {
2074 updateStartItem( *evt, true );
2076 }
2077 else if( evt->IsAction( &PCB_ACTIONS::breakTrack ) )
2078 {
2079 updateStartItem( *evt, true );
2080 breakTrack( );
2081 evt->SetPassEvent( false );
2082 }
2083 else if( evt->IsClick( BUT_LEFT )
2084 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack )
2085 || evt->IsAction( &PCB_ACTIONS::routeDiffPair ) )
2086 {
2087 updateStartItem( *evt );
2088
2089 if( evt->HasPosition() )
2090 performRouting( evt->Position() );
2091 }
2092 else if( evt->IsAction( &ACT_PlaceThroughVia ) )
2093 {
2094 m_startWithVia = true;
2095 m_toolMgr->RunAction( PCB_ACTIONS::layerToggle );
2096 }
2097 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2098 {
2099 m_router->SwitchLayer( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ) );
2100 updateStartItem( *evt );
2101 updateSizesAfterRouterEvent( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ), m_startSnapPoint );
2102 }
2103 else if( evt->IsKeyPressed() )
2104 {
2105 // wxWidgets fails to correctly translate shifted keycodes on the wxEVT_CHAR_HOOK
2106 // event so we need to process the wxEVT_CHAR event that will follow as long as we
2107 // pass the event.
2108 evt->SetPassEvent();
2109 }
2110 else if( evt->IsClick( BUT_RIGHT ) )
2111 {
2112 m_menu->ShowContextMenu( selection() );
2113 }
2114 else
2115 {
2116 evt->SetPassEvent();
2117 }
2118
2119 if( m_cancelled )
2120 {
2121 frame->PopTool( pushedEvent );
2122 break;
2123 }
2124 }
2125
2126 // Store routing settings till the next invocation
2127 m_savedSizes = m_router->Sizes();
2128 m_router->ClearViewDecorations();
2129
2130 return 0;
2131}
2132
2133
2135{
2136 m_router->ClearViewDecorations();
2137
2138 view()->ClearPreview();
2139 view()->InitPreview();
2140
2142
2143 if( m_startItem && m_startItem->IsLocked() )
2144 {
2145 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
2146 wxOK | wxCANCEL | wxICON_WARNING );
2147 dlg.SetOKLabel( _( "Drag Anyway" ) );
2148 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
2149
2150 if( dlg.ShowModal() == wxID_CANCEL )
2151 return;
2152 }
2153
2154 bool dragStarted = m_router->StartDragging( m_startSnapPoint, m_startItem, aMode );
2155
2156 if( !dragStarted )
2157 {
2158 if( !m_router->FailureReason().IsEmpty() )
2159 frame()->ShowInfoBarError( m_router->FailureReason(), true );
2160
2161 return;
2162 }
2163
2164 if( m_startItem && m_startItem->Net() )
2165 highlightNets( true, { m_startItem->Net() } );
2166
2167 ctls->SetAutoPan( true );
2168 m_gridHelper->SetAuxAxes( true, m_startSnapPoint );
2169 frame()->UndoRedoBlock( true );
2170
2171 UI_UPDATE_INTERVAL_GUARD uiGuard( 200 );
2172
2173 while( TOOL_EVENT* evt = Wait() )
2174 {
2175 ctls->ForceCursorPosition( false );
2176
2177 if( evt->IsMotion() )
2178 {
2179 updateEndItem( *evt );
2181
2182 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
2183 {
2184 bool dragStatus;
2185
2186 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
2187 {
2188 view()->ClearPreview();
2189
2190 if( !dragStatus )
2191 {
2192 wxString hint;
2193 hint.Printf( _( "(%s to commit anyway.)" ),
2195
2197 statusItem->SetMessage( _( "Track violates DRC." ) );
2198 statusItem->SetHint( hint );
2199 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
2200 view()->AddToPreview( statusItem );
2201 }
2202 }
2203 }
2204 }
2205 else if( evt->IsClick( BUT_LEFT ) )
2206 {
2207 bool forceFinish = false;
2208 bool forceCommit = evt->Modifier( MD_CTRL );
2209
2210 if( m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit ) )
2211 break;
2212 }
2213 else if( evt->IsClick( BUT_RIGHT ) )
2214 {
2215 m_menu->ShowContextMenu( selection() );
2216 }
2217 else if( evt->IsCancelInteractive() || evt->IsAction( &PCB_ACTIONS::cancelCurrentItem )
2218 || evt->IsActivate() )
2219 {
2220 if( evt->IsCancelInteractive() && !m_startItem )
2221 m_cancelled = true;
2222
2223 if( evt->IsActivate() && !evt->IsMoveTool() )
2224 m_cancelled = true;
2225
2226 break;
2227 }
2228 else if( evt->IsUndoRedo() )
2229 {
2230 // We're in an UndoRedoBlock. If we get here, something's broken.
2231 wxFAIL;
2232 break;
2233 }
2234 else if( evt->Category() == TC_COMMAND )
2235 {
2236 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2237 // but we don't at present have that, so we just knock out some of the egregious ones.
2238 if( evt->IsAction( &ACTIONS::cut )
2239 || evt->IsAction( &ACTIONS::copy )
2240 || evt->IsAction( &ACTIONS::paste )
2241 || evt->IsAction( &ACTIONS::pasteSpecial )
2243 {
2244 wxBell();
2245 }
2246 // treat an undo as an escape
2247 else if( evt->IsAction( &ACTIONS::undo ) )
2248 {
2249 if( m_startItem )
2250 break;
2251 else
2252 wxBell();
2253 }
2254 else
2255 {
2256 evt->SetPassEvent();
2257 }
2258 }
2259 else
2260 {
2261 evt->SetPassEvent();
2262 }
2263
2264 handleCommonEvents( *evt );
2265 }
2266
2267 view()->ClearPreview();
2268 view()->ShowPreview( false );
2269
2270 if( m_router->RoutingInProgress() )
2271 m_router->StopRouting();
2272
2273 m_startItem = nullptr;
2274
2275 m_gridHelper->SetAuxAxes( false );
2276 frame()->UndoRedoBlock( false );
2277 ctls->SetAutoPan( false );
2278 ctls->ForceCursorPosition( false );
2279 highlightNets( false );
2280}
2281
2282
2284 PCB_SELECTION_TOOL* aSelTool )
2285{
2286 /*
2287 * If the collection contains a trivial line corner (two connected segments)
2288 * or a non-fanout-via (a via with no more than two connected segments), then
2289 * trim the collection down to a single item (which one won't matter since
2290 * they're all connected).
2291 */
2292
2293 // First make sure we've got something that *might* match.
2294 int vias = aCollector.CountType( PCB_VIA_T );
2295 int traces = aCollector.CountType( PCB_TRACE_T );
2296 int arcs = aCollector.CountType( PCB_ARC_T );
2297
2298 // We eliminate arcs because they are not supported in the inline drag code.
2299 if( arcs > 0 )
2300 return;
2301
2302 // We need to have at least 1 via or track
2303 if( vias + traces == 0 )
2304 return;
2305
2306 // We cannot drag more than one via at a time
2307 if( vias > 1 )
2308 return;
2309
2310 // We cannot drag more than two track segments at a time
2311 if( traces > 2 )
2312 return;
2313
2314 // Fetch first PCB_TRACK (via or trace) as our reference
2315 PCB_TRACK* reference = nullptr;
2316
2317 for( int i = 0; !reference && i < aCollector.GetCount(); i++ )
2318 reference = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2319
2320 // This should never happen, but just in case...
2321 if( !reference )
2322 return;
2323
2324 int refNet = reference->GetNetCode();
2325
2326 VECTOR2I refPoint( aPt.x, aPt.y );
2327 EDA_ITEM_FLAGS flags = reference->IsPointOnEnds( refPoint, -1 );
2328
2329 if( flags & STARTPOINT )
2330 refPoint = reference->GetStart();
2331 else if( flags & ENDPOINT )
2332 refPoint = reference->GetEnd();
2333
2334 // Check all items to ensure that any TRACKs are co-terminus with the reference and on
2335 // the same net.
2336 for( int i = 0; i < aCollector.GetCount(); i++ )
2337 {
2338 PCB_TRACK* neighbor = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2339
2340 if( neighbor && neighbor != reference )
2341 {
2342 if( neighbor->GetNetCode() != refNet )
2343 return;
2344
2345 if( neighbor->GetStart() != refPoint && neighbor->GetEnd() != refPoint )
2346 return;
2347 }
2348 }
2349
2350 // Selection meets criteria; trim it to the reference item.
2351 aCollector.Empty();
2352 aCollector.Append( reference );
2353}
2354
2355
2356bool ROUTER_TOOL::CanInlineDrag( int aDragMode )
2357{
2359 const PCB_SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2360
2361 if( selection.Size() == 1 )
2362 {
2363 return selection.Front()->IsType( GENERAL_COLLECTOR::DraggableItems );
2364 }
2365 else if( selection.CountType( PCB_FOOTPRINT_T ) == (size_t) selection.Size() )
2366 {
2367 // Footprints cannot be dragged freely.
2368 return !( aDragMode & PNS::DM_FREE_ANGLE );
2369 }
2370 else if( selection.CountType( PCB_TRACE_T ) == (size_t) selection.Size() )
2371 {
2372 return true;
2373 }
2374
2375 return false;
2376}
2377
2378
2379void ROUTER_TOOL::restoreSelection( const PCB_SELECTION& aOriginalSelection )
2380{
2381 EDA_ITEMS selItems;
2382 std::copy( aOriginalSelection.Items().begin(), aOriginalSelection.Items().end(), std::back_inserter( selItems ) );
2383 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &selItems );
2384}
2385
2386
2388{
2389 const PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2390
2391 if( selection.Empty() )
2393
2394 if( selection.Empty() || !selection.Front()->IsBOARD_ITEM() )
2395 return 0;
2396
2397 // selection gets cleared in the next action, we need a copy of the selected items.
2398 std::deque<EDA_ITEM*> selectedItems = selection.GetItems();
2399
2400 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2401
2402 if( item->Type() != PCB_TRACE_T
2403 && item->Type() != PCB_VIA_T
2404 && item->Type() != PCB_ARC_T
2405 && item->Type() != PCB_FOOTPRINT_T )
2406 {
2407 return 0;
2408 }
2409
2410 std::set<FOOTPRINT*> footprints;
2411
2412 if( item->Type() == PCB_FOOTPRINT_T )
2413 footprints.insert( static_cast<FOOTPRINT*>( item ) );
2414
2415 // We can drag multiple footprints, but not a grab-bag of items
2416 if( selection.Size() > 1 && item->Type() == PCB_FOOTPRINT_T )
2417 {
2418 for( int idx = 1; idx < selection.Size(); ++idx )
2419 {
2420 if( !selection.GetItem( idx )->IsBOARD_ITEM() )
2421 return 0;
2422
2423 if( static_cast<BOARD_ITEM*>( selection.GetItem( idx ) )->Type() != PCB_FOOTPRINT_T )
2424 return 0;
2425
2426 footprints.insert( static_cast<FOOTPRINT*>( selection.GetItem( idx ) ) );
2427 }
2428 }
2429
2430 // If we overrode locks, we want to clear the flag from the source item before SyncWorld is
2431 // called so that virtual vias are not generated for the (now unlocked) track segment. Note in
2432 // this case the lock can't be reliably re-applied, because there is no guarantee that the end
2433 // state of the drag results in the same number of segments so it's not clear which segment to
2434 // apply the lock state to.
2435 bool wasLocked = false;
2436
2437 if( item->IsLocked() )
2438 {
2439 wasLocked = true;
2440 item->SetLocked( false );
2441 }
2442
2443 m_toolMgr->RunAction( ACTIONS::selectionClear );
2444
2445 TOOL_EVENT pushedEvent = aEvent;
2446 frame()->PushTool( aEvent );
2447 Activate();
2448
2449 m_startItem = nullptr;
2450
2451 PNS::ITEM_SET itemsToDrag;
2452
2453 bool showCourtyardConflicts = frame()->GetPcbNewSettings()->m_ShowCourtyardCollisions;
2454
2455 std::shared_ptr<DRC_ENGINE> drcEngine = m_toolMgr->GetTool<DRC_TOOL>()->GetDRCEngine();
2456 DRC_INTERACTIVE_COURTYARD_CLEARANCE courtyardClearanceDRC( drcEngine );
2457
2458 std::shared_ptr<CONNECTIVITY_DATA> connectivityData = board()->GetConnectivity();
2459 std::vector<BOARD_ITEM*> dynamicItems;
2460 std::unique_ptr<CONNECTIVITY_DATA> dynamicData = nullptr;
2461 VECTOR2I lastOffset;
2462 std::vector<PNS::ITEM*> leaderSegments;
2463 bool singleFootprintDrag = false;
2464
2465 // The PNS world may be stale if the board has been modified since the last sync (e.g. by
2466 // a Move operation). Sync it now so that FindItemByParent and joint lookups work correctly.
2467 m_router->SyncWorld();
2468
2469 if( !footprints.empty() )
2470 {
2471 if( footprints.size() == 1 )
2472 singleFootprintDrag = true;
2473
2474 if( showCourtyardConflicts )
2475 courtyardClearanceDRC.Init( board() );
2476
2477 for( FOOTPRINT* footprint : footprints )
2478 {
2479 for( PAD* pad : footprint->Pads() )
2480 {
2481 PNS::ITEM* solid = m_router->GetWorld()->FindItemByParent( pad );
2482
2483 if( solid )
2484 itemsToDrag.Add( solid );
2485
2486 if( pad->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
2487 {
2488 if( connectivityData->GetRatsnestForPad( pad ).size() > 0 )
2489 dynamicItems.push_back( pad );
2490 }
2491 }
2492
2493 for( ZONE* zone : footprint->Zones() )
2494 {
2495 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( zone ) )
2496 itemsToDrag.Add( solid );
2497 }
2498
2499 for( BOARD_ITEM* shape : footprint->GraphicalItems() )
2500 {
2501 if( shape->GetLayer() == Edge_Cuts
2502 || shape->GetLayer() == Margin
2503 || IsCopperLayer( shape->GetLayer() ) )
2504 {
2505 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( shape ) )
2506 itemsToDrag.Add( solid );
2507 }
2508 }
2509
2510 if( showCourtyardConflicts )
2511 courtyardClearanceDRC.m_FpInMove.push_back( footprint );
2512 }
2513
2514 dynamicData = std::make_unique<CONNECTIVITY_DATA>( board()->GetConnectivity(),
2515 dynamicItems, true );
2516 connectivityData->BlockRatsnestItems( dynamicItems );
2517 }
2518 else
2519 {
2520 for( const EDA_ITEM* selItem : selectedItems )
2521 {
2522 if( !selItem->IsBOARD_ITEM() )
2523 continue;
2524
2525 const BOARD_ITEM* boardItem = static_cast<const BOARD_ITEM*>( selItem );
2526 PNS::ITEM* pnsItem = m_router->GetWorld()->FindItemByParent( boardItem );
2527
2528 if( !pnsItem )
2529 continue;
2530
2531 if( pnsItem->OfKind( PNS::ITEM::SEGMENT_T )
2532 || pnsItem->OfKind( PNS::ITEM::VIA_T )
2533 || pnsItem->OfKind( PNS::ITEM::ARC_T ) )
2534 {
2535 itemsToDrag.Add( pnsItem );
2536 }
2537 }
2538 }
2539
2540 GAL* gal = m_toolMgr->GetView()->GetGAL();
2541 VECTOR2I p0 = GetClampedCoords( controls()->GetCursorPosition( false ), COORDS_PADDING );
2542 VECTOR2I p = p0;
2543
2544 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
2545 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
2546
2547 if( itemsToDrag.Count() >= 1 )
2548 {
2549 // Snap to closest item. Use the frame's active layer rather than m_originalActiveLayer,
2550 // which is only set during prepareInteractive() and remains UNDEFINED_LAYER for inline
2551 // drag operations.
2552 PCB_LAYER_ID activeLayer = frame()->GetActiveLayer();
2553 int layer = m_iface->GetPNSLayerFromBoardLayer( activeLayer );
2554 PNS::ITEM* closestItem = nullptr;
2555 SEG::ecoord closestDistSq = std::numeric_limits<SEG::ecoord>::max();
2556
2557 for( PNS::ITEM* pitem : itemsToDrag.Items() )
2558 {
2559 const SHAPE* shape = pitem->Shape( layer );
2560
2561 if( !shape )
2562 continue;
2563
2564 SEG::ecoord distSq = shape->SquaredDistance( p0, 0 );
2565
2566 if( distSq < closestDistSq )
2567 {
2568 closestDistSq = distSq;
2569 closestItem = pitem;
2570 }
2571 }
2572
2573 if( closestItem )
2574 {
2575 p = snapToItem( closestItem, p0 );
2576
2577 m_startItem = closestItem;
2578
2579 if( closestItem->Net() )
2580 highlightNets( true, { closestItem->Net() } );
2581 }
2582 }
2583
2584 if( !footprints.empty() && singleFootprintDrag )
2585 {
2586 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
2587
2588 // The mouse is going to be moved on grid before dragging begins.
2589 VECTOR2I tweakedMousePos;
2591
2592 // Check if user wants to warp the mouse to origin of moved object
2593
2594 if( editFrame->GetMoveWarpsCursor() )
2595 tweakedMousePos = footprint->GetPosition(); // Use footprint anchor to warp mouse
2596 else
2597 tweakedMousePos = GetClampedCoords( controls()->GetCursorPosition(),
2598 COORDS_PADDING ); // Just use current mouse pos
2599
2600 // We tweak the mouse position using the value from above, and then use that as the
2601 // start position to prevent the footprint from jumping when we start dragging.
2602 // First we move the visual cross hair cursor...
2603 controls()->ForceCursorPosition( true, tweakedMousePos );
2604 controls()->SetCursorPosition( tweakedMousePos ); // ...then the mouse pointer
2605
2606 // Now that the mouse is in the right position, get a copy of the position to use later
2607 p = controls()->GetCursorPosition();
2608 }
2609
2610 int dragMode = aEvent.Parameter<int> ();
2611
2612 bool dragStarted = m_router->StartDragging( p, itemsToDrag, dragMode );
2613
2614 if( !dragStarted )
2615 {
2616 if( wasLocked )
2617 item->SetLocked( true );
2618
2619 if( !footprints.empty() )
2620 connectivityData->ClearLocalRatsnest();
2621
2622 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
2623 courtyardClearanceDRC.ClearConflicts( getView() );
2624
2626 controls()->ForceCursorPosition( false );
2627 frame()->PopTool( pushedEvent );
2628 highlightNets( false );
2629 return 0;
2630 }
2631
2632 m_gridHelper->SetAuxAxes( true, p );
2633 controls()->ShowCursor( true );
2634 controls()->SetAutoPan( true );
2635 frame()->UndoRedoBlock( true );
2636
2637 view()->ClearPreview();
2638 view()->InitPreview();
2639
2640 auto setCursor =
2641 [&]()
2642 {
2643 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2644 };
2645
2646 // Set initial cursor
2647 setCursor();
2648
2649 // Set the initial visible area
2650 BOX2D viewAreaD = getView()->GetGAL()->GetVisibleWorldExtents();
2651 m_router->SetVisibleViewArea( BOX2ISafe( viewAreaD ) );
2652
2653 // Send an initial movement to prime the collision detection
2654 m_router->Move( p, nullptr );
2655
2656 UI_UPDATE_INTERVAL_GUARD uiGuard( 200 );
2657
2658 bool hasMouseMoved = false;
2659 bool hasMultidragCancelled = false;
2660
2661 while( TOOL_EVENT* evt = Wait() )
2662 {
2663 setCursor();
2664
2665 if( evt->IsCancelInteractive() || evt->IsAction( &PCB_ACTIONS::cancelCurrentItem )
2666 || evt->IsActivate() )
2667 {
2668 if( wasLocked )
2669 item->SetLocked( true );
2670
2671 hasMultidragCancelled = true;
2672
2673 break;
2674 }
2675 else if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
2676 {
2677 hasMouseMoved = true;
2678 updateEndItem( *evt );
2680
2681 view()->ClearPreview();
2682
2683 if( !footprints.empty() )
2684 {
2685 VECTOR2I offset = m_endSnapPoint - p;
2686 BOARD_ITEM* previewItem;
2687
2688 for( FOOTPRINT* footprint : footprints )
2689 {
2690 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
2691 {
2692 previewItem = static_cast<BOARD_ITEM*>( drawing->Clone() );
2693 previewItem->Move( offset );
2694
2695 view()->AddToPreview( previewItem );
2696 view()->Hide( drawing, true );
2697 }
2698
2699 for( PAD* pad : footprint->Pads() )
2700 {
2701 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none()
2702 && pad->GetDrillSize().x == 0 )
2703 {
2704 previewItem = static_cast<BOARD_ITEM*>( pad->Clone() );
2705 previewItem->Move( offset );
2706
2707 view()->AddToPreview( previewItem );
2708 }
2709 else
2710 {
2711 // Pads with copper or holes are handled by the router
2712 }
2713
2714 view()->Hide( pad, true );
2715 }
2716
2717 previewItem = static_cast<BOARD_ITEM*>( footprint->Reference().Clone() );
2718 previewItem->Move( offset );
2719 view()->AddToPreview( previewItem );
2720 view()->Hide( &footprint->Reference() );
2721
2722 previewItem = static_cast<BOARD_ITEM*>( footprint->Value().Clone() );
2723 previewItem->Move( offset );
2724 view()->AddToPreview( previewItem );
2725 view()->Hide( &footprint->Value() );
2726
2727 if( showCourtyardConflicts )
2728 footprint->Move( offset );
2729 }
2730
2731 if( showCourtyardConflicts )
2732 {
2733 courtyardClearanceDRC.Run();
2734 courtyardClearanceDRC.UpdateConflicts( getView(), false );
2735
2736 for( FOOTPRINT* footprint : footprints )
2737 footprint->Move( -offset );
2738 }
2739
2740 // Update ratsnest
2741 dynamicData->Move( offset - lastOffset );
2742 lastOffset = offset;
2743 connectivityData->ComputeLocalRatsnest( dynamicItems, dynamicData.get(), offset );
2744 }
2745
2746 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
2747 {
2748 bool dragStatus;
2749
2750 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
2751 {
2752 if( !dragStatus )
2753 {
2754 wxString hint;
2755 hint.Printf( _( "(%s to commit anyway.)" ),
2757
2759 statusItem->SetMessage( _( "Track violates DRC." ) );
2760 statusItem->SetHint( hint );
2761 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
2762 view()->AddToPreview( statusItem );
2763 }
2764 }
2765 }
2766 }
2767 else if( hasMouseMoved && ( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
2768 {
2769 bool forceFinish = false;
2770 bool forceCommit = evt->Modifier( MD_CTRL );
2771
2772 updateEndItem( *evt );
2773 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
2774 leaderSegments = m_router->GetLastCommittedLeaderSegments();
2775
2776 break;
2777 }
2778 else if( evt->IsUndoRedo() )
2779 {
2780 // We're in an UndoRedoBlock. If we get here, something's broken.
2781 wxFAIL;
2782 break;
2783 }
2784 else if( evt->Category() == TC_COMMAND )
2785 {
2786 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2787 // but we don't at present have that, so we just knock out some of the egregious ones.
2788 if( evt->IsAction( &ACTIONS::cut )
2789 || evt->IsAction( &ACTIONS::copy )
2790 || evt->IsAction( &ACTIONS::paste )
2791 || evt->IsAction( &ACTIONS::pasteSpecial )
2793 {
2794 wxBell();
2795 }
2796 // treat an undo as an escape
2797 else if( evt->IsAction( &ACTIONS::undo ) )
2798 {
2799 if( wasLocked )
2800 item->SetLocked( true );
2801
2802 break;
2803 }
2804 else
2805 {
2806 evt->SetPassEvent();
2807 }
2808 }
2809 else
2810 {
2811 evt->SetPassEvent();
2812 }
2813
2814 handleCommonEvents( *evt );
2815 }
2816
2817 if( !footprints.empty() )
2818 {
2819 for( FOOTPRINT* footprint : footprints )
2820 {
2821 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
2822 view()->Hide( drawing, false );
2823
2824 view()->Hide( &footprint->Reference(), false );
2825 view()->Hide( &footprint->Value(), false );
2826
2827 for( PAD* pad : footprint->Pads() )
2828 view()->Hide( pad, false );
2829 }
2830
2831 view()->ClearPreview();
2832 view()->ShowPreview( false );
2833
2834 connectivityData->ClearLocalRatsnest();
2835 }
2836
2837 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
2838 courtyardClearanceDRC.ClearConflicts( getView() );
2839
2840 if( m_router->RoutingInProgress() )
2841 m_router->StopRouting();
2842
2843
2844 if( itemsToDrag.Size() && hasMultidragCancelled )
2845 {
2847 }
2848 else if( leaderSegments.size() )
2849 {
2850 std::vector<EDA_ITEM*> newItems;
2851
2852 for( auto lseg : leaderSegments )
2853 newItems.push_back( lseg->Parent() );
2854
2855 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &newItems );
2856 }
2857
2858 m_gridHelper->SetAuxAxes( false );
2859 controls()->SetAutoPan( false );
2860 controls()->ForceCursorPosition( false );
2861 frame()->UndoRedoBlock( false );
2862 frame()->PopTool( pushedEvent );
2863 highlightNets( false );
2864 view()->ClearPreview();
2865 view()->ShowPreview( false );
2866
2867 return 0;
2868}
2869
2870
2872{
2873 const SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2874
2875 if( selection.Size() != 1 )
2876 return 0;
2877
2878 const BOARD_CONNECTED_ITEM* item =
2879 static_cast<const BOARD_CONNECTED_ITEM*>( selection.Front() );
2880
2881 if( item->Type() != PCB_TRACE_T && item->Type() != PCB_ARC_T )
2882 return 0;
2883
2884 m_toolMgr->RunAction( ACTIONS::selectionClear );
2885
2886 Activate();
2887
2888 m_startItem = m_router->GetWorld()->FindItemByParent( item );
2889
2890 TOOL_MANAGER* toolManager = frame()->GetToolManager();
2891 GAL* gal = toolManager->GetView()->GetGAL();
2892
2893 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
2894 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
2895
2896 controls()->ForceCursorPosition( false );
2897
2898 if( toolManager->IsContextMenuActive() )
2899 {
2900 // If we're here from a context menu then we need to get the position of the
2901 // cursor when the context menu was invoked. This is used to figure out the
2902 // break point on the track.
2904 }
2905 else
2906 {
2907 // If we're here from a hotkey, then get the current mouse position so we know
2908 // where to break the track.
2909 m_startSnapPoint = snapToItem( m_startItem, controls()->GetCursorPosition() );
2910 }
2911
2912 if( m_startItem && m_startItem->IsLocked() )
2913 {
2914 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
2915 wxOK | wxCANCEL | wxICON_WARNING );
2916 dlg.SetOKLabel( _( "Break Track" ) );
2917 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
2918
2919 if( dlg.ShowModal() == wxID_CANCEL )
2920 return 0;
2921 }
2922
2923 frame()->UndoRedoBlock( true );
2924 breakTrack();
2925
2926 if( m_router->RoutingInProgress() )
2927 m_router->StopRouting();
2928
2929 frame()->UndoRedoBlock( false );
2930
2931 return 0;
2932}
2933
2934
2936{
2938 DIALOG_TRACK_VIA_SIZE sizeDlg( frame(), bds );
2939
2940 if( sizeDlg.ShowModal() == wxID_OK )
2941 {
2942 bds.m_TempOverrideTrackWidth = true;
2943 bds.UseCustomTrackViaSize( true );
2944
2947 }
2948
2949 return 0;
2950}
2951
2952
2954{
2955 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
2956
2957 if( !m_router->GetCurrentNets().empty() )
2958 m_iface->ImportSizes( sizes, m_startItem, m_router->GetCurrentNets()[0], VECTOR2D() );
2959
2960 m_router->UpdateSizes( sizes );
2961
2962 // Changing the track width can affect the placement, so call the
2963 // move routine without changing the destination
2964 // Update end item first to avoid moving to an invalid/missing item
2965 updateEndItem( aEvent );
2967
2969
2970 return 0;
2971}
2972
2973
2975{
2976 std::vector<MSG_PANEL_ITEM> items;
2977
2978 if( m_router->GetState() == PNS::ROUTER::ROUTE_TRACK )
2979 {
2980 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
2981 PNS::RULE_RESOLVER* resolver = m_iface->GetRuleResolver();
2982 PNS::CONSTRAINT constraint;
2983 std::vector<PNS::NET_HANDLE> nets = m_router->GetCurrentNets();
2984 wxString description;
2985 wxString secondary;
2986 wxString mode;
2987
2989 {
2990 wxASSERT( nets.size() >= 2 );
2991
2992 NETINFO_ITEM* netA = static_cast<NETINFO_ITEM*>( nets[0] );
2993 NETINFO_ITEM* netB = static_cast<NETINFO_ITEM*>( nets[1] );
2994 wxASSERT( netA );
2995 wxASSERT( netB );
2996
2997 description = wxString::Format( _( "Routing Diff Pair: %s" ),
2998 netA->GetNetname() + wxT( ", " ) + netB->GetNetname() );
2999
3000 wxString netclass;
3001 NETCLASS* netclassA = netA->GetNetClass();
3002 NETCLASS* netclassB = netB->GetNetClass();
3003
3004 if( *netclassA == *netclassB )
3005 netclass = netclassA->GetHumanReadableName();
3006 else
3007 netclass = netclassA->GetHumanReadableName() + wxT( ", " )
3008 + netclassB->GetHumanReadableName();
3009
3010 secondary = wxString::Format( _( "Resolved Netclass: %s" ),
3011 UnescapeString( netclass ) );
3012 }
3013 else if( !nets.empty() && nets[0] )
3014 {
3015 NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( nets[0] );
3016
3017 description = wxString::Format( _( "Routing Track: %s" ),
3018 net->GetNetname() );
3019
3020 secondary = wxString::Format(
3021 _( "Resolved Netclass: %s" ),
3023 }
3024 else
3025 {
3026 description = _( "Routing Track" );
3027 secondary = _( "(no net)" );
3028 }
3029
3030 items.emplace_back( description, secondary );
3031
3032 wxString cornerMode;
3033
3034 if( m_router->Settings().GetFreeAngleMode() )
3035 {
3036 cornerMode = _( "Free-angle" );
3037 }
3038 else
3039 {
3040 switch( m_router->Settings().GetCornerMode() )
3041 {
3042 case DIRECTION_45::CORNER_MODE::MITERED_45: cornerMode = _( "45-degree" ); break;
3043 case DIRECTION_45::CORNER_MODE::ROUNDED_45: cornerMode = _( "45-degree rounded" ); break;
3044 case DIRECTION_45::CORNER_MODE::MITERED_90: cornerMode = _( "90-degree" ); break;
3045 case DIRECTION_45::CORNER_MODE::ROUNDED_90: cornerMode = _( "90-degree rounded" ); break;
3046 default: break;
3047 }
3048 }
3049
3050 items.emplace_back( _( "Corner Style" ), cornerMode );
3051
3052 switch( m_router->Settings().Mode() )
3053 {
3054 case PNS::PNS_MODE::RM_MarkObstacles: mode = _( "Highlight collisions" ); break;
3055 case PNS::PNS_MODE::RM_Walkaround: mode = _( "Walk around" ); break;
3056 case PNS::PNS_MODE::RM_Shove: mode = _( "Shove" ); break;
3057 default: break;
3058 }
3059
3060 items.emplace_back( _( "Mode" ), mode );
3061
3062#define FORMAT_VALUE( x ) frame()->MessageTextFromValue( x )
3063
3065 {
3066 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
3067 FORMAT_VALUE( sizes.DiffPairWidth() ) ),
3068 wxString::Format( _( "(from %s)" ),
3069 sizes.GetDiffPairWidthSource() ) );
3070
3071 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
3072 FORMAT_VALUE( sizes.Clearance() ) ),
3073 wxString::Format( _( "(from %s)" ),
3074 sizes.GetClearanceSource() ) );
3075
3076 items.emplace_back( wxString::Format( _( "Diff Pair Gap: %s" ),
3077 FORMAT_VALUE( sizes.DiffPairGap() ) ),
3078 wxString::Format( _( "(from %s)" ),
3079 sizes.GetDiffPairGapSource() ) );
3080
3081 const PNS::ITEM_SET& traces = m_router->Placer()->Traces();
3082 wxASSERT( traces.Count() == 2 );
3083
3084 if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_MAX_UNCOUPLED, traces[0],
3085 traces[1], m_router->GetCurrentLayer(), &constraint ) )
3086 {
3087 items.emplace_back( wxString::Format( _( "DP Max Uncoupled-length: %s" ),
3088 FORMAT_VALUE( constraint.m_Value.Max() ) ),
3089 wxString::Format( _( "(from %s)" ),
3090 constraint.m_RuleName ) );
3091 }
3092 }
3093 else
3094 {
3095 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
3096 FORMAT_VALUE( sizes.TrackWidth() ) ),
3097 wxString::Format( _( "(from %s)" ),
3098 sizes.GetWidthSource() ) );
3099
3100 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
3101 FORMAT_VALUE( sizes.Clearance() ) ),
3102 wxString::Format( _( "(from %s)" ),
3103 sizes.GetClearanceSource() ) );
3104 }
3105
3106#undef FORMAT_VALUE
3107
3108 frame()->SetMsgPanel( items );
3109 }
3110 else
3111 {
3112 frame()->SetMsgPanel( board() );
3113 return;
3114 }
3115}
3116
3117
3119{
3121
3135
3142
3178
3181
3187}
std::function< void(const VECTOR2I &, GENERAL_COLLECTOR &, PCB_SELECTION_TOOL *)> CLIENT_SELECTION_FILTER
Definition actions.h:33
@ width_track_via
@ change_entry_orient
@ switch_corner_rounding_shape
constexpr BOX2I BOX2ISafe(const BOX2D &aInput)
Definition box2.h:925
BOX2< VECTOR2D > BOX2D
Definition box2.h:919
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:116
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.
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_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:81
void SetLocked(bool aLocked) override
Definition board_item.h:356
bool IsLocked() const override
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:372
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
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:1040
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1149
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:634
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:219
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
wxString GetName() const
Definition drc_rule.h:204
MINOPTMAX< int > m_Value
Definition drc_rule.h:240
bool IsNull() const
Definition drc_rule.h:191
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:96
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
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:141
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:1894
virtual int GetTopLayer() const
Definition view.cpp:899
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:207
void InitPreview()
Definition view.cpp:1873
void ClearPreview()
Definition view.cpp:1858
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:1780
void AddToPreview(VIEW_ITEM *aItem, bool aTakeOwnership=true)
Definition view.cpp:1880
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
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:38
const wxString GetHumanReadableName() const
Gets the consolidated name of this netclass (which may be an aggregate).
Definition netclass.cpp:320
Handle the data for a net.
Definition netinfo.h:46
const wxString & GetNetname() const
Definition netinfo.h:100
NETCLASS * GetNetClass()
Definition netinfo.h:91
int GetNetCode() const
Definition netinfo.h:94
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 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 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...
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:395
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
virtual NET_HANDLE Net() const
Definition pns_item.h:210
bool OfKind(int aKindMask) const
Definition pns_item.h:181
static wxString FormatLogFileAsJSON(const LOG_DATA &aLogData)
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.
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:62
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
Definition project.h:206
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:200
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)
int InlineDrag(const TOOL_EVENT &aEvent)
std::shared_ptr< ACTION_MENU > m_trackViaMenu
Definition router_tool.h:98
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()
void saveRouterDebugLog()
void performDragging(int aMode=PNS::DM_ANY)
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
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
Definition router_tool.h:97
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 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 CanInlineDrag(int aDragMode)
int SettingsDialog(const TOOL_EVENT &aEvent)
int DpDimensionsDialog(const TOOL_EVENT &aEvent)
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:178
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:77
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:675
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:86
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:29
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:448
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:24
@ ID_POPUP_PCB_SELECT_DIFFPAIR16
Definition pcbnew_id.h:76
@ ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES
Definition pcbnew_id.h:23
@ ID_POPUP_PCB_SELECT_WIDTH16
Definition pcbnew_id.h:39
@ ID_POPUP_PCB_SELECT_AUTO_WIDTH
Definition pcbnew_id.h:22
@ ID_POPUP_PCB_SELECT_CUSTOM_WIDTH
Definition pcbnew_id.h:21
@ ID_POPUP_PCB_SELECT_DIFFPAIR1
Definition pcbnew_id.h:61
@ ID_POPUP_PCB_SELECT_USE_NETCLASS_DIFFPAIR
Definition pcbnew_id.h:60
@ ID_POPUP_PCB_SELECT_VIASIZE1
Definition pcbnew_id.h:40
@ ID_POPUP_PCB_SELECT_CUSTOM_DIFFPAIR
Definition pcbnew_id.h:59
@ ID_POPUP_PCB_SELECT_VIASIZE16
Definition pcbnew_id.h:55
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))
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_SelLayerAndPlaceBlindVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SelLayerAndPlaceBlindVia") .Scope(AS_CONTEXT) .DefaultHotkey(MD_ALT+'<') .LegacyHotkeyName("Select Layer and Add Blind/Buried Via") .FriendlyName(_("Select Layer and Place Blind/Buried Via...")) .Tooltip(_("Select a layer, then add a blind or buried via at the end of currently routed track.")) .Icon(BITMAPS::select_w_layer) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::BLIND_VIA|VIA_ACTION_FLAGS::SELECT_LAYER))
static const TOOL_ACTION ACT_SwitchPosture(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchPosture") .Scope(AS_CONTEXT) .DefaultHotkey('/') .LegacyHotkeyName("Switch Track Posture") .FriendlyName(_("Switch Track Posture")) .Tooltip(_("Switches posture of the currently routed track.")) .Icon(BITMAPS::change_entry_orient))
VIA_ACTION_FLAGS
Flags used by via tool actions.
@ BLIND_VIA
blind via
@ BURIED_VIA
buried via
@ SELECT_LAYER
Ask user to select layer before adding via.
@ MICROVIA
Microvia.
@ VIA_MASK
@ VIA
Normal via.
static 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))
#define APPEND_UNDO
Definition sch_commit.h:37
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:99
std::optional< TEST_CASE_TYPE > m_TestCaseType
Definition pns_logger.h:104
std::vector< ITEM * > m_AddedItems
Definition pns_logger.h:100
std::vector< EVENT_ENTRY > m_Events
Definition pns_logger.h:103
std::set< KIID > m_RemovedItems
Definition pns_logger.h:101
std::vector< ITEM * > m_Heads
Definition pns_logger.h:102
Container to handle a stock of specific vias each with unique diameter and drill sizes in the BOARD c...
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:90
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
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.