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