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:
parent
994e702cce
commit
f06e28b7a7
|
@ -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 );
|
||||
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
|
||||
{
|
||||
end = jt->Pos();
|
||||
aLayers = jt->Layers();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -125,10 +127,25 @@ bool TOPOLOGY::LeadingRatLine( const LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine )
|
|||
return false;
|
||||
|
||||
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.Append( track.CPoint( -1 ) );
|
||||
aRatLine.Append( aTrack->CPoint( -1 ) );
|
||||
aRatLine.Append( end );
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,9 @@ public:
|
|||
bool SimplifyLine( LINE *aLine );
|
||||
ITEM* NearestUnconnectedItem( JOINT* aStart, int* aAnchor = nullptr,
|
||||
int aKindMask = ITEM::ANY_T );
|
||||
|
||||
bool NearestUnconnectedAnchorPoint( const LINE* aTrack, VECTOR2I& aPoint,
|
||||
LAYER_RANGE& aLayers );
|
||||
bool LeadingRatLine( const LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine );
|
||||
|
||||
const JOINT_SET ConnectedJoints( JOINT* aStart );
|
||||
|
|
|
@ -60,9 +60,14 @@ using namespace std::placeholders;
|
|||
#include "pns_router.h"
|
||||
#include "pns_itemset.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 <ratsnest/ratsnest_data.h>
|
||||
|
||||
#include <plugins/kicad/pcb_plugin.h>
|
||||
|
||||
using namespace KIGFX;
|
||||
|
@ -96,11 +101,6 @@ static const TOOL_ACTION ACT_EndTrack( "pcbnew.InteractiveRouter.EndTrack",
|
|||
_( "Finish Track" ), _( "Stops laying the current track." ),
|
||||
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",
|
||||
AS_CONTEXT,
|
||||
'V', LEGACY_HK_NAME( "Add Through Via" ),
|
||||
|
@ -461,6 +461,13 @@ bool ROUTER_TOOL::Init()
|
|||
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.AddSeparator( 1 );
|
||||
|
||||
|
@ -471,12 +478,13 @@ bool ROUTER_TOOL::Init()
|
|||
menu.AddItem( PCB_ACTIONS::routeDiffPair, notRoutingCond );
|
||||
menu.AddItem( ACT_EndTrack, 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::drag45Degree, 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_PlaceBlindVia, 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( ¤t, 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 )
|
||||
{
|
||||
switch( aFlags & VIA_ACTION_FLAGS::VIA_MASK )
|
||||
|
@ -1122,6 +1202,21 @@ void ROUTER_TOOL::performRouting()
|
|||
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
|
||||
setCursor();
|
||||
|
||||
|
@ -1151,6 +1246,71 @@ 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 ) )
|
||||
{
|
||||
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, ¤t, currentLayer );
|
||||
else
|
||||
{
|
||||
m_router->StartRouting( otherEnd, ¤t, otherEndLayers.Start() );
|
||||
syncRouterAndFrameLayer();
|
||||
}
|
||||
|
||||
m_startSnapPoint = otherEnd;
|
||||
|
||||
// Attempt to route to our current position
|
||||
m_router->Move( currentEnd, ¤t );
|
||||
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, ¤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 ) )
|
||||
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 ) )
|
||||
{
|
||||
updateEndItem( *evt );
|
||||
|
@ -1166,16 +1326,7 @@ void ROUTER_TOOL::performRouting()
|
|||
switchLayerOnViaPlacement();
|
||||
|
||||
// Synchronize the indicated layer
|
||||
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();
|
||||
}
|
||||
syncRouterAndFrameLayer();
|
||||
|
||||
updateEndItem( *evt );
|
||||
m_router->Move( m_endSnapPoint, m_endItem );
|
||||
|
|
|
@ -75,6 +75,7 @@ private:
|
|||
|
||||
int getStartLayer( const PNS::ITEM* aItem );
|
||||
void switchLayerOnViaPlacement();
|
||||
bool getNearestRatnestAnchor( VECTOR2I& aPoint, LAYER_RANGE& aLayers );
|
||||
|
||||
int onLayerCommand( const TOOL_EVENT& aEvent );
|
||||
int onViaCommand( const TOOL_EVENT& aEvent );
|
||||
|
|
|
@ -1418,6 +1418,18 @@ TOOL_ACTION PCB_ACTIONS::routerUndoLastSegment( "pcbnew.InteractiveRouter.UndoLa
|
|||
WXK_BACK, "",
|
||||
_( "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",
|
||||
AS_GLOBAL, 0, "",
|
||||
_( "Break Track" ),
|
||||
|
|
|
@ -195,6 +195,9 @@ public:
|
|||
|
||||
static TOOL_ACTION routerUndoLastSegment;
|
||||
|
||||
static TOOL_ACTION routerContinueFromEnd;
|
||||
static TOOL_ACTION routerAttemptFinish;
|
||||
|
||||
/// Activation of the Push and Shove settings dialogs
|
||||
static TOOL_ACTION routerSettingsDialog;
|
||||
static TOOL_ACTION routerDiffPairDialog;
|
||||
|
|
Loading…
Reference in New Issue