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