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