diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp index aa4c58aa88..1768bb8f0e 100644 --- a/pcbnew/router/router_tool.cpp +++ b/pcbnew/router/router_tool.cpp @@ -1309,6 +1309,10 @@ void ROUTER_TOOL::performRouting() else { VECTOR2I moveResultPoint; + bool* autoRouted = evt->Parameter(); + + if( autoRouted != nullptr ) + *autoRouted = false; // Keep moving until we don't change position do @@ -1322,11 +1326,28 @@ void ROUTER_TOOL::performRouting() && otherEndLayers.Overlaps( m_router->GetCurrentLayer() ) ) { if( m_router->FixRoute( otherEnd, ¤t, false ) ) + { + // When we're routing a group of signals automatically we want + // to break up the undo stack every time we have to manually route + // so the user gets nice checkpoints. Remove the APPEND_UNDO flag. + if( autoRouted != nullptr ) + { + *autoRouted = true; + } + break; + } + // This acts as check if we were called by the autorouter; we don't want + // to reset APPEND_UNDO if we're auto finishing after route-other-end + else if( autoRouted != nullptr ) + m_iface->SetCommitFlags( 0 ); } // Otherwise warp the mouse so the user is at the point we managed to route to else + { + m_iface->SetCommitFlags( 0 ); controls()->WarpMouseCursor( moveResultPoint, true, true ); + } } } else if( evt->IsClick( BUT_LEFT ) || evt->IsDrag( BUT_LEFT ) || evt->IsAction( &PCB_ACTIONS::routeSingleTrack ) ) @@ -1490,6 +1511,129 @@ void ROUTER_TOOL::breakTrack() } +int ROUTER_TOOL::RouteSelected( const TOOL_EVENT& aEvent ) +{ + PNS::ROUTER_MODE mode = aEvent.Parameter(); + PCB_EDIT_FRAME* frame = getEditFrame(); + VIEW_CONTROLS* controls = getViewControls(); + PCB_LAYER_ID originalLayer = frame->GetActiveLayer(); + bool autoRoute = aEvent.Matches( PCB_ACTIONS::routerAutorouteSelected.MakeEvent() ); + bool otherEnd = aEvent.Matches( PCB_ACTIONS::routerRouteSelectedFromEnd.MakeEvent() ); + + if( m_router->RoutingInProgress() ) + return 0; + + // Save selection then clear it for interactive routing + PCB_SELECTION selection = m_toolMgr->GetTool()->GetSelection(); + + if( selection.Size() == 0 ) + return 0; + + m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); + + std::string tool = aEvent.GetCommandStr().value(); + frame->PushTool( tool ); + + auto setCursor = + [&]() + { + frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL ); + }; + + Activate(); + // Must be done after Activate() so that it gets set into the correct context + controls->ShowCursor( true ); + controls->ForceCursorPosition( false ); + // Set initial cursor + setCursor(); + + // Get all connected board items, adding pads for any footprints selected + std::vector itemList; + + for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() ) + { + if( item->Type() == PCB_FOOTPRINT_T ) + { + const PADS& fpPads = ( static_cast( item ) )->Pads(); + + for( PAD* pad : fpPads ) + itemList.push_back( pad ); + } + else if( dynamic_cast( item ) != nullptr ) + itemList.push_back( static_cast( item ) ); + } + + std::shared_ptr connectivity = frame->GetBoard()->GetConnectivity(); + + // For putting sequential tracks that successfully autoroute into one undo commit + bool groupStart = true; + + for( BOARD_CONNECTED_ITEM* item : itemList ) + { + // This code is similar to GetRatsnestForPad() but it only adds the anchor for + // the side of the connectivity on this pad. It also checks for ratsnest points + // inside the pad (like a trace end) and counts them. + RN_NET* net = connectivity->GetRatsnestForNet( item->GetNetCode() ); + std::vector> anchors; + + for( const CN_EDGE& edge : net->GetEdges() ) + { + std::shared_ptr target = edge.GetTargetNode(); + std::shared_ptr source = edge.GetSourceNode(); + + if( source->Parent() == item ) + anchors.push_back( edge.GetSourceNode() ); + else if( target->Parent() == item ) + anchors.push_back( edge.GetTargetNode() ); + } + + // Route them + for( std::shared_ptr anchor : anchors ) + { + // Try to return to the original layer as indicating the user's preferred + // layer for autorouting tracks. The layer can be changed by the user to + // finish tracks that can't complete automatically, but should be changed + // back after. + if( frame->GetActiveLayer() != originalLayer ) + frame->SetActiveLayer( originalLayer ); + + VECTOR2I ignore; + m_startItem = m_router->GetWorld()->FindItemByParent( anchor->Parent() ); + m_startSnapPoint = anchor->Pos(); + m_router->SetMode( mode ); + + // Prime the interactive routing to attempt finish if we are autorouting + bool autoRouted = false; + + if( autoRoute ) + m_toolMgr->RunAction( PCB_ACTIONS::routerAttemptFinish, false, &autoRouted ); + else if( otherEnd ) + m_toolMgr->RunAction( PCB_ACTIONS::routerContinueFromEnd, false ); + + // We want autorouted tracks to all be in one undo group except for + // any tracks that need to be manually finished. + // The undo appending for manually finished tracks is handled in peformRouting() + if( groupStart ) + groupStart = false; + else + m_iface->SetCommitFlags( APPEND_UNDO ); + + // Start interactive routing. Will automatically finish if possible. + performRouting(); + + // Route didn't complete automatically, need to a new undo commit + // for the next line so those can group as far as they autoroute + if( !autoRouted ) + groupStart = true; + } + } + + m_iface->SetCommitFlags( 0 ); + frame->PopTool( tool ); + return 0; +} + + int ROUTER_TOOL::MainLoop( const TOOL_EVENT& aEvent ) { PNS::ROUTER_MODE mode = aEvent.Parameter(); @@ -2305,6 +2449,9 @@ void ROUTER_TOOL::setTransitions() Go( &ROUTER_TOOL::MainLoop, PCB_ACTIONS::routeSingleTrack.MakeEvent() ); Go( &ROUTER_TOOL::MainLoop, PCB_ACTIONS::routeDiffPair.MakeEvent() ); + Go( &ROUTER_TOOL::RouteSelected, PCB_ACTIONS::routerRouteSelected.MakeEvent() ); + Go( &ROUTER_TOOL::RouteSelected, PCB_ACTIONS::routerRouteSelectedFromEnd.MakeEvent() ); + Go( &ROUTER_TOOL::RouteSelected, PCB_ACTIONS::routerAutorouteSelected.MakeEvent() ); Go( &ROUTER_TOOL::DpDimensionsDialog, PCB_ACTIONS::routerDiffPairDialog.MakeEvent() ); Go( &ROUTER_TOOL::SettingsDialog, PCB_ACTIONS::routerSettingsDialog.MakeEvent() ); Go( &ROUTER_TOOL::ChangeRouterMode, PCB_ACTIONS::routerHighlightMode.MakeEvent() ); diff --git a/pcbnew/router/router_tool.h b/pcbnew/router/router_tool.h index 2b487b3103..683b59e198 100644 --- a/pcbnew/router/router_tool.h +++ b/pcbnew/router/router_tool.h @@ -35,6 +35,7 @@ public: void Reset( RESET_REASON aReason ) override; int MainLoop( const TOOL_EVENT& aEvent ); + int RouteSelected( const TOOL_EVENT& aEvent ); int InlineBreakTrack( const TOOL_EVENT& aEvent ); bool CanInlineDrag( int aDragMode ); diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp index 12f81ffb4d..f8f56ba1b6 100644 --- a/pcbnew/tools/pcb_actions.cpp +++ b/pcbnew/tools/pcb_actions.cpp @@ -1454,6 +1454,27 @@ TOOL_ACTION PCB_ACTIONS::routerAttemptFinish( "pcbnew.InteractiveRouter.AttemptF _( "Attempt Finish" ), _( "Attempts to complete current route to nearest ratsnest end." ) ); +TOOL_ACTION PCB_ACTIONS::routerRouteSelected( "pcbnew.InteractiveRouter.RouteSelected", + AS_GLOBAL, + MD_SHIFT + 'X', "", + _( "Route Selected" ), + _( "Sequentially route selected items from ratsnest anchor." ), + BITMAPS::INVALID_BITMAP, AF_ACTIVATE, (void *) PNS::PNS_MODE_ROUTE_SINGLE); + +TOOL_ACTION PCB_ACTIONS::routerRouteSelectedFromEnd( "pcbnew.InteractiveRouter.RouteSelectedFromEnd", + AS_GLOBAL, + MD_SHIFT + 'E', "", + _( "Route Selected From Other End" ), + _( "Sequentially route selected items from other end of ratsnest anchor." ), + BITMAPS::INVALID_BITMAP, AF_ACTIVATE, (void *) PNS::PNS_MODE_ROUTE_SINGLE); + +TOOL_ACTION PCB_ACTIONS::routerAutorouteSelected( "pcbnew.InteractiveRouter.Autoroute", + AS_GLOBAL, + MD_SHIFT + 'F', "", + _( "Attempt Finish Selected (Autoroute)" ), + _( "Sequentially attempt to automatically route all selected pads." ), + BITMAPS::INVALID_BITMAP, AF_ACTIVATE, (void *) PNS::PNS_MODE_ROUTE_SINGLE); + TOOL_ACTION PCB_ACTIONS::breakTrack( "pcbnew.InteractiveRouter.BreakTrack", AS_GLOBAL, 0, "", _( "Break Track" ), diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h index dbb479e656..7cde0a32af 100644 --- a/pcbnew/tools/pcb_actions.h +++ b/pcbnew/tools/pcb_actions.h @@ -203,6 +203,9 @@ public: static TOOL_ACTION routerContinueFromEnd; static TOOL_ACTION routerAttemptFinish; + static TOOL_ACTION routerRouteSelected; + static TOOL_ACTION routerRouteSelectedFromEnd; + static TOOL_ACTION routerAutorouteSelected; /// Activation of the Push and Shove settings dialogs static TOOL_ACTION routerSettingsDialog;