PCB: Finish Route, Route From End

Adds restart routing from other ratsnest' end and automatically finish
routing.

Fixes: https://gitlab.com/kicad/code/kicad/-/issues/5051
This commit is contained in:
Mike Williams 2022-07-14 12:38:39 -04:00 committed by Jon Evans
parent 994e702cce
commit f06e28b7a7
6 changed files with 205 additions and 18 deletions

View File

@ -93,7 +93,8 @@ const TOPOLOGY::JOINT_SET TOPOLOGY::ConnectedJoints( JOINT* aStart )
} }
bool TOPOLOGY::LeadingRatLine( const LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine ) bool TOPOLOGY::NearestUnconnectedAnchorPoint( const LINE* aTrack, VECTOR2I& aPoint,
LAYER_RANGE& aLayers )
{ {
LINE track( *aTrack ); LINE track( *aTrack );
VECTOR2I end; VECTOR2I end;
@ -113,6 +114,7 @@ bool TOPOLOGY::LeadingRatLine( const LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine )
|| ( track.EndsWithVia() && jt->LinkCount() >= 3 ) ) // we got something connected || ( track.EndsWithVia() && jt->LinkCount() >= 3 ) ) // we got something connected
{ {
end = jt->Pos(); end = jt->Pos();
aLayers = jt->Layers();
} }
else else
{ {
@ -125,10 +127,25 @@ bool TOPOLOGY::LeadingRatLine( const LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine )
return false; return false;
end = it->Anchor( anchor ); end = it->Anchor( anchor );
aLayers = it->Layers();
} }
aPoint = end;
return true;
}
bool TOPOLOGY::LeadingRatLine( const LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine )
{
VECTOR2I end;
// Ratline doesn't care about the layer
LAYER_RANGE layers;
if( !NearestUnconnectedAnchorPoint( aTrack, end, layers ) )
return false;
aRatLine.Clear(); aRatLine.Clear();
aRatLine.Append( track.CPoint( -1 ) ); aRatLine.Append( aTrack->CPoint( -1 ) );
aRatLine.Append( end ); aRatLine.Append( end );
return true; return true;
} }

View File

@ -49,6 +49,9 @@ public:
bool SimplifyLine( LINE *aLine ); bool SimplifyLine( LINE *aLine );
ITEM* NearestUnconnectedItem( JOINT* aStart, int* aAnchor = nullptr, ITEM* NearestUnconnectedItem( JOINT* aStart, int* aAnchor = nullptr,
int aKindMask = ITEM::ANY_T ); int aKindMask = ITEM::ANY_T );
bool NearestUnconnectedAnchorPoint( const LINE* aTrack, VECTOR2I& aPoint,
LAYER_RANGE& aLayers );
bool LeadingRatLine( const LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine ); bool LeadingRatLine( const LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine );
const JOINT_SET ConnectedJoints( JOINT* aStart ); const JOINT_SET ConnectedJoints( JOINT* aStart );

View File

@ -60,9 +60,14 @@ using namespace std::placeholders;
#include "pns_router.h" #include "pns_router.h"
#include "pns_itemset.h" #include "pns_itemset.h"
#include "pns_logger.h" #include "pns_logger.h"
#include "pns_placement_algo.h"
#include "pns_line_placer.h"
#include "pns_topology.h"
#include "pns_kicad_iface.h" #include "pns_kicad_iface.h"
#include <ratsnest/ratsnest_data.h>
#include <plugins/kicad/pcb_plugin.h> #include <plugins/kicad/pcb_plugin.h>
using namespace KIGFX; using namespace KIGFX;
@ -96,11 +101,6 @@ static const TOOL_ACTION ACT_EndTrack( "pcbnew.InteractiveRouter.EndTrack",
_( "Finish Track" ), _( "Stops laying the current track." ), _( "Finish Track" ), _( "Stops laying the current track." ),
BITMAPS::checked_ok ); BITMAPS::checked_ok );
static const TOOL_ACTION ACT_AutoEndRoute( "pcbnew.InteractiveRouter.AutoEndRoute",
AS_CONTEXT,
'F', "",
_( "Auto-finish Track" ), _( "Automatically finishes laying the current track." ) );
static const TOOL_ACTION ACT_PlaceThroughVia( "pcbnew.InteractiveRouter.PlaceVia", static const TOOL_ACTION ACT_PlaceThroughVia( "pcbnew.InteractiveRouter.PlaceVia",
AS_CONTEXT, AS_CONTEXT,
'V', LEGACY_HK_NAME( "Add Through Via" ), 'V', LEGACY_HK_NAME( "Add Through Via" ),
@ -461,6 +461,13 @@ bool ROUTER_TOOL::Init()
return !m_router->RoutingInProgress(); return !m_router->RoutingInProgress();
}; };
auto hasOtherEnd = [&]( const SELECTION& )
{
VECTOR2I unusedEnd;
LAYER_RANGE unusedEndLayers;
return getNearestRatnestAnchor( unusedEnd, unusedEndLayers );
};
menu.AddItem( ACTIONS::cancelInteractive, SELECTION_CONDITIONS::ShowAlways, 1 ); menu.AddItem( ACTIONS::cancelInteractive, SELECTION_CONDITIONS::ShowAlways, 1 );
menu.AddSeparator( 1 ); menu.AddSeparator( 1 );
@ -471,12 +478,13 @@ bool ROUTER_TOOL::Init()
menu.AddItem( PCB_ACTIONS::routeDiffPair, notRoutingCond ); menu.AddItem( PCB_ACTIONS::routeDiffPair, notRoutingCond );
menu.AddItem( ACT_EndTrack, SELECTION_CONDITIONS::ShowAlways ); menu.AddItem( ACT_EndTrack, SELECTION_CONDITIONS::ShowAlways );
menu.AddItem( PCB_ACTIONS::routerUndoLastSegment, SELECTION_CONDITIONS::ShowAlways ); menu.AddItem( PCB_ACTIONS::routerUndoLastSegment, SELECTION_CONDITIONS::ShowAlways );
menu.AddItem( PCB_ACTIONS::routerContinueFromEnd, hasOtherEnd );
menu.AddItem( PCB_ACTIONS::routerAttemptFinish, hasOtherEnd );
menu.AddItem( PCB_ACTIONS::breakTrack, notRoutingCond ); menu.AddItem( PCB_ACTIONS::breakTrack, notRoutingCond );
menu.AddItem( PCB_ACTIONS::drag45Degree, notRoutingCond ); menu.AddItem( PCB_ACTIONS::drag45Degree, notRoutingCond );
menu.AddItem( PCB_ACTIONS::dragFreeAngle, notRoutingCond ); menu.AddItem( PCB_ACTIONS::dragFreeAngle, notRoutingCond );
// Add( ACT_AutoEndRoute ); // fixme: not implemented yet. Sorry.
menu.AddItem( ACT_PlaceThroughVia, SELECTION_CONDITIONS::ShowAlways ); menu.AddItem( ACT_PlaceThroughVia, SELECTION_CONDITIONS::ShowAlways );
menu.AddItem( ACT_PlaceBlindVia, SELECTION_CONDITIONS::ShowAlways ); menu.AddItem( ACT_PlaceBlindVia, SELECTION_CONDITIONS::ShowAlways );
menu.AddItem( ACT_PlaceMicroVia, SELECTION_CONDITIONS::ShowAlways ); menu.AddItem( ACT_PlaceMicroVia, SELECTION_CONDITIONS::ShowAlways );
@ -638,6 +646,78 @@ void ROUTER_TOOL::switchLayerOnViaPlacement()
} }
bool ROUTER_TOOL::getNearestRatnestAnchor( VECTOR2I& aOtherEnd, LAYER_RANGE& aOtherEndLayers )
{
// Can't finish something with no connections
if( m_router->GetCurrentNets().empty() )
return false;
PNS::LINE_PLACER* placer = dynamic_cast<PNS::LINE_PLACER*>( m_router->Placer() );
if( placer == nullptr )
return false;
PNS::LINE current = placer->Trace();
PNS::NODE* lastNode = placer->CurrentNode( true );
PNS::TOPOLOGY topo( lastNode );
VECTOR2I currentEndPos = placer->CurrentEnd();
VECTOR2I currentStartPos = m_startSnapPoint;
int currentLayer = m_router->GetCurrentLayer();
// If we have an active line being routed, attempt finish from line front using
// dynamic ratnest topology
if( current.PointCount() > 0 )
return topo.NearestUnconnectedAnchorPoint( &current, aOtherEnd, aOtherEndLayers );
// No line, find nearest static ratsnest item for net
auto connectivity = getEditFrame<PCB_EDIT_FRAME>()->GetBoard()->GetConnectivity();
RN_NET* net = connectivity->GetRatsnestForNet( m_router->GetCurrentNets()[0] );
auto edges = net->GetEdges();
// Need to have something unconnected to finish to
if( edges.size() == 0 )
return false;
// Default to the first item if we don't have a position
std::shared_ptr<CN_ANCHOR> nearest = edges[0].GetSourceNode();
double currentDistance = DBL_MAX;
// Find nearest unconnected end
for( const CN_EDGE& edge : edges )
{
VECTOR2I sourcePos = edge.GetSourcePos();
VECTOR2I targetPos = edge.GetTargetPos();
double sourceDistance = ( currentEndPos - sourcePos ).EuclideanNorm();
double targetDistance = ( currentEndPos - targetPos ).EuclideanNorm();
LAYER_RANGE sourceLayers = edge.GetSourceNode()->Item()->Layers();
LAYER_RANGE targetLayers = edge.GetTargetNode()->Item()->Layers();
// Basically look for the closest ratnest item that isn't this item
// (same point, same layer)
if( ( sourcePos != m_startSnapPoint || !sourceLayers.Overlaps( currentLayer ) )
&& ( sourceDistance < currentDistance ) )
{
nearest = edge.GetSourceNode();
currentDistance = sourceDistance;
}
if( ( targetPos != m_startSnapPoint || !targetLayers.Overlaps( currentLayer ) )
&& ( targetDistance < currentDistance ) )
{
nearest = edge.GetTargetNode();
currentDistance = targetDistance;
}
}
aOtherEnd = nearest->Pos();
aOtherEndLayers = nearest->Item()->Layers();
return true;
}
static VIATYPE getViaTypeFromFlags( int aFlags ) static VIATYPE getViaTypeFromFlags( int aFlags )
{ {
switch( aFlags & VIA_ACTION_FLAGS::VIA_MASK ) switch( aFlags & VIA_ACTION_FLAGS::VIA_MASK )
@ -1122,6 +1202,21 @@ void ROUTER_TOOL::performRouting()
frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL ); frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
}; };
auto syncRouterAndFrameLayer =
[&]()
{
PCB_LAYER_ID routingLayer = ToLAYER_ID( m_router->GetCurrentLayer() );
PCB_EDIT_FRAME* editFrame = getEditFrame<PCB_EDIT_FRAME>();
editFrame->SetActiveLayer( routingLayer );
if( !getView()->IsLayerVisible( routingLayer ) )
{
editFrame->GetAppearancePanel()->SetLayerVisible( routingLayer, true );
editFrame->GetCanvas()->Refresh();
}
};
// Set initial cursor // Set initial cursor
setCursor(); setCursor();
@ -1151,6 +1246,71 @@ void ROUTER_TOOL::performRouting()
updateEndItem( *evt ); updateEndItem( *evt );
m_router->Move( m_endSnapPoint, m_endItem ); m_router->Move( m_endSnapPoint, m_endItem );
} }
else if( evt->IsAction( &PCB_ACTIONS::routerAttemptFinish )
|| evt->IsAction( &PCB_ACTIONS::routerContinueFromEnd ) )
{
PNS::LINE_PLACER* placer = dynamic_cast<PNS::LINE_PLACER*>( m_router->Placer() );
if( placer == nullptr )
break;
// 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;
if( !getNearestRatnestAnchor( otherEnd, otherEndLayers ) )
break;
if( evt->IsAction( &PCB_ACTIONS::routerContinueFromEnd ) )
{
// Commit routing will put the router on no layer, so save it
int currentLayer = m_router->GetCurrentLayer();
// Commit whatever we've fixed and restart routing from the other end
m_router->CommitRouting();
if( otherEndLayers.Overlaps( currentLayer ) )
m_router->StartRouting( otherEnd, &current, currentLayer );
else
{
m_router->StartRouting( otherEnd, &current, otherEndLayers.Start() );
syncRouterAndFrameLayer();
}
m_startSnapPoint = otherEnd;
// Attempt to route to our current position
m_router->Move( currentEnd, &current );
VECTOR2I moveResultPoint = m_router->Placer()->CurrentEnd();
// Warp the mouse to wherever we actually ended up routing to
controls()->WarpMouseCursor( currentEnd, true, true );
}
else
{
VECTOR2I moveResultPoint;
// Keep moving until we don't change position
do
{
moveResultPoint = m_router->Placer()->CurrentEnd();
m_router->Move( otherEnd, &current );
} 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, &current, false ) )
break;
}
// Otherwise warp the mouse so the user is at the point we managed to route to
else
controls()->WarpMouseCursor( moveResultPoint, true, true );
}
}
else if( evt->IsClick( BUT_LEFT ) || evt->IsDrag( BUT_LEFT ) || evt->IsAction( &PCB_ACTIONS::routeSingleTrack ) ) else if( evt->IsClick( BUT_LEFT ) || evt->IsDrag( BUT_LEFT ) || evt->IsAction( &PCB_ACTIONS::routeSingleTrack ) )
{ {
updateEndItem( *evt ); updateEndItem( *evt );
@ -1166,16 +1326,7 @@ void ROUTER_TOOL::performRouting()
switchLayerOnViaPlacement(); switchLayerOnViaPlacement();
// Synchronize the indicated layer // Synchronize the indicated layer
PCB_LAYER_ID routingLayer = ToLAYER_ID( m_router->GetCurrentLayer() ); syncRouterAndFrameLayer();
PCB_EDIT_FRAME* editFrame = getEditFrame<PCB_EDIT_FRAME>();
editFrame->SetActiveLayer( routingLayer );
if( !getView()->IsLayerVisible( routingLayer ) )
{
editFrame->GetAppearancePanel()->SetLayerVisible( routingLayer, true );
editFrame->GetCanvas()->Refresh();
}
updateEndItem( *evt ); updateEndItem( *evt );
m_router->Move( m_endSnapPoint, m_endItem ); m_router->Move( m_endSnapPoint, m_endItem );

View File

@ -75,6 +75,7 @@ private:
int getStartLayer( const PNS::ITEM* aItem ); int getStartLayer( const PNS::ITEM* aItem );
void switchLayerOnViaPlacement(); void switchLayerOnViaPlacement();
bool getNearestRatnestAnchor( VECTOR2I& aPoint, LAYER_RANGE& aLayers );
int onLayerCommand( const TOOL_EVENT& aEvent ); int onLayerCommand( const TOOL_EVENT& aEvent );
int onViaCommand( const TOOL_EVENT& aEvent ); int onViaCommand( const TOOL_EVENT& aEvent );

View File

@ -1418,6 +1418,18 @@ TOOL_ACTION PCB_ACTIONS::routerUndoLastSegment( "pcbnew.InteractiveRouter.UndoLa
WXK_BACK, "", WXK_BACK, "",
_( "Undo Last Segment" ), _( "Walks the current track back one segment." ) ); _( "Undo Last Segment" ), _( "Walks the current track back one segment." ) );
TOOL_ACTION PCB_ACTIONS::routerContinueFromEnd( "pcbnew.InteractiveRouter.ContinueFromEnd",
AS_CONTEXT,
'E', "",
_( "Route From Other End" ),
_( "Commits current segments and starts next segment from nearest ratsnest end." ) );
TOOL_ACTION PCB_ACTIONS::routerAttemptFinish( "pcbnew.InteractiveRouter.AttemptFinish",
AS_CONTEXT,
'F', "",
_( "Attempt Finish" ),
_( "Attempts to complete current route to nearest ratsnest end." ) );
TOOL_ACTION PCB_ACTIONS::breakTrack( "pcbnew.InteractiveRouter.BreakTrack", TOOL_ACTION PCB_ACTIONS::breakTrack( "pcbnew.InteractiveRouter.BreakTrack",
AS_GLOBAL, 0, "", AS_GLOBAL, 0, "",
_( "Break Track" ), _( "Break Track" ),

View File

@ -195,6 +195,9 @@ public:
static TOOL_ACTION routerUndoLastSegment; static TOOL_ACTION routerUndoLastSegment;
static TOOL_ACTION routerContinueFromEnd;
static TOOL_ACTION routerAttemptFinish;
/// Activation of the Push and Shove settings dialogs /// Activation of the Push and Shove settings dialogs
static TOOL_ACTION routerSettingsDialog; static TOOL_ACTION routerSettingsDialog;
static TOOL_ACTION routerDiffPairDialog; static TOOL_ACTION routerDiffPairDialog;