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