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 );
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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( ¤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 )
|
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, ¤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 ) )
|
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 );
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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" ),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue