diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp index daaca18555..1e761f60a2 100644 --- a/pcbnew/router/pns_router.cpp +++ b/pcbnew/router/pns_router.cpp @@ -462,6 +462,86 @@ bool ROUTER::Move( const VECTOR2I& aP, ITEM* endItem ) } +bool ROUTER::getNearestRatnestAnchor( VECTOR2I& aOtherEnd, LAYER_RANGE& aOtherEndLayers ) +{ + // Can't finish something with no connections + if( GetCurrentNets().empty() ) + return false; + + PNS::LINE_PLACER* placer = dynamic_cast( Placer() ); + + if( placer == nullptr ) + return false; + + PNS::LINE trace = placer->Trace(); + PNS::NODE* lastNode = placer->CurrentNode( true ); + PNS::TOPOLOGY topo( lastNode ); + + // If the user has drawn a line, get the anchor nearest to the line end + if( trace.SegmentCount() > 0 ) + return topo.NearestUnconnectedAnchorPoint( &trace, aOtherEnd, aOtherEndLayers ); + + // Otherwise, find the closest anchor to our start point + + // Get joint from placer start item + JOINT* jt = lastNode->FindJoint( placer->CurrentStart(), placer->CurrentLayer(), + placer->CurrentNets()[0] ); + + if( !jt ) + return false; + + // Get unconnected item from joint + int anchor; + PNS::ITEM* it = topo.NearestUnconnectedItem( jt, &anchor ); + + if( !it ) + return false; + + aOtherEnd = it->Anchor( anchor ); + aOtherEndLayers = it->Layers(); + + return true; +} + + +bool ROUTER::Finish() +{ + if( m_state != ROUTE_TRACK ) + return false; + + LINE_PLACER* placer = dynamic_cast( Placer() ); + + if( placer == nullptr ) + return false; + + // Get our current line and position and nearest ratsnest to them if it exists + PNS::LINE current = placer->Trace(); + VECTOR2I currentEnd = placer->CurrentEnd(); + VECTOR2I otherEnd; + LAYER_RANGE otherEndLayers; + + // Get the anchor nearest to the end of the trace the user is routing + if( !getNearestRatnestAnchor( otherEnd, otherEndLayers ) ) + return false; + + // Keep moving until we don't change position + VECTOR2I moveResultPoint; + do + { + moveResultPoint = Placer()->CurrentEnd(); + Move( otherEnd, ¤t ); + } while( Placer()->CurrentEnd() != moveResultPoint ); + + // If we've made it, fix the route and we're done + if( moveResultPoint == otherEnd && otherEndLayers.Overlaps( GetCurrentLayer() ) ) + { + return FixRoute( otherEnd, ¤t, false ); + } + + return false; +} + + bool ROUTER::moveDragging( const VECTOR2I& aP, ITEM* aEndItem ) { m_iface->EraseView(); diff --git a/pcbnew/router/pns_router.h b/pcbnew/router/pns_router.h index fe909d1387..193fc9a492 100644 --- a/pcbnew/router/pns_router.h +++ b/pcbnew/router/pns_router.h @@ -143,6 +143,7 @@ public: bool RoutingInProgress() const; bool StartRouting( const VECTOR2I& aP, ITEM* aItem, int aLayer ); bool Move( const VECTOR2I& aP, ITEM* aItem ); + bool Finish(); bool FixRoute( const VECTOR2I& aP, ITEM* aItem, bool aForceFinish = false ); void BreakSegment( ITEM *aItem, const VECTOR2I& aP ); @@ -221,6 +222,9 @@ private: void markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR& aRemoved ); bool isStartingPointRoutable( const VECTOR2I& aWhere, ITEM* aItem, int aLayer ); + bool getNearestRatnestAnchor( VECTOR2I& aOtherEnd, LAYER_RANGE& aOtherEndLayers ); + + private: BOX2I m_visibleViewArea; RouterState m_state; diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp index 6f8b356c33..c1eb9e8f3e 100644 --- a/pcbnew/router/router_tool.cpp +++ b/pcbnew/router/router_tool.cpp @@ -1263,8 +1263,35 @@ void ROUTER_TOOL::performRouting() updateEndItem( *evt ); m_router->Move( m_endSnapPoint, m_endItem ); } - else if( evt->IsAction( &PCB_ACTIONS::routerAttemptFinish ) - || evt->IsAction( &PCB_ACTIONS::routerContinueFromEnd ) ) + else if( evt->IsAction( &PCB_ACTIONS::routerAttemptFinish ) ) + { + bool* autoRouted = evt->Parameter(); + + if( m_router->Finish() ) + { + // 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; + } + else + { + // 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 + if( autoRouted != nullptr ) + { + *autoRouted = false; + m_iface->SetCommitFlags( 0 ); + } + + // Warp the mouse so the user is at the point we managed to route to + controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true ); + } + } + else if( evt->IsAction( &PCB_ACTIONS::routerContinueFromEnd ) ) { PNS::LINE_PLACER* placer = dynamic_cast( m_router->Placer() ); @@ -1311,49 +1338,6 @@ void ROUTER_TOOL::performRouting() // Warp the mouse to wherever we actually ended up routing to controls()->WarpMouseCursor( currentEnd, true, true ); } - else - { - VECTOR2I moveResultPoint; - bool* autoRouted = evt->Parameter(); - - if( autoRouted != nullptr ) - *autoRouted = false; - - // Keep moving until we don't change position - do - { - moveResultPoint = m_router->Placer()->CurrentEnd(); - m_router->Move( otherEnd, ¤t ); - } while( m_router->Placer()->CurrentEnd() != moveResultPoint ); - - // Fix the route and end routing if we made it to the destination - if( moveResultPoint == otherEnd - && 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 ) ) {