kicad/pcbnew/ratsnest.cpp

877 lines
27 KiB
C++

/**
* @file ratsnest.cpp
* @brief Ratsnets functions.
*/
#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "class_drawpanel.h"
#include "colors_selection.h"
#include "wxBasePcbFrame.h"
#include "macros.h"
#include "class_board.h"
#include "class_module.h"
#include "class_track.h"
#include "pcbnew.h"
#include "minimun_spanning_tree.h"
/**
* @brief class MIN_SPAN_TREE_PADS (derived from MIN_SPAN_TREE) specialize
* the basic class to calculate a minimum spanning tree from a list of pads,
* and to add this tree as ratsnest to the main ratsnest list.
*/
class MIN_SPAN_TREE_PADS: public MIN_SPAN_TREE
{
friend class MIN_SPAN_TREE;
public:
std::vector <D_PAD*>* m_PadsList; // list of pads:
/* these pads are the parents of nodes of the tree.
* Each node position is the corresponding pad position.
* This pad list is used to evaluate the weight of an edge in tree.
* -> edge = link between 2 nodes = links between 2 pads.
* -> weight of a link = rectilinear distance between the 2 pads
*/
public:
MIN_SPAN_TREE_PADS(): MIN_SPAN_TREE()
{
m_PadsList = NULL;
}
void MSP_Init( std::vector <D_PAD*>* aPadsList )
{
m_PadsList = aPadsList;
MIN_SPAN_TREE::MSP_Init( (int) m_PadsList->size() );
}
/**
* Function AddItemsToRatsnest
* Adds the current minimum spanning tree as ratsnest items
* to the main ratsnest list
* @param aRatsnestList = the main ratsnest list
*/
void AddItemsToRatsnest( std::vector<RATSNEST_ITEM> &aRatsnestList );
/**
* Function GetWeight
* calculates the weight between 2 items
* NOTE: The weight between a node and itself should be 0
* @param aItem1 = first item
* @param aItem2 = other item
* @return the weight between items ( the rectilinear distance )
*/
int GetWeight( int aItem1, int aItem2 );
};
void MIN_SPAN_TREE_PADS::AddItemsToRatsnest( std::vector<RATSNEST_ITEM> &aRatsnestList )
{
std::vector<D_PAD*> & padsBuffer = *m_PadsList;
int netcode = padsBuffer[0]->GetNet();
// Note: to get edges in minimum spanning tree,
// the index value 0 is not used: it is just
// the entry point of the minimum spanning tree.
// The first edge (i.e. rastnest) starts at index 1
for( int ii = 1; ii < m_Size; ii++ )
{
/* Create the new ratsnest */
RATSNEST_ITEM net;
net.SetNet( netcode );
net.m_Status = CH_ACTIF | CH_VISIBLE;
net.m_Lenght = GetDist(ii);
net.m_PadStart = padsBuffer[ii];
net.m_PadEnd = padsBuffer[ GetWhoTo(ii) ];
aRatsnestList.push_back( net );
}
}
/* Function GetWeight
* calculates the weight between 2 items
* Here it calculate the rectilinear distance between 2 pads (2 items)
* NOTE: The weight between a node and itself should be <=0
* aItem1 and aItem2 are the 2 items
* return the rectilinear distance
*/
int MIN_SPAN_TREE_PADS::GetWeight( int aItem1, int aItem2 )
{
// NOTE: The distance (weight) between a node and itself should be 0
// so we add 1 to other distances th be sure we never have 0
// in cases other than a node and itself
D_PAD* pad1 = (*m_PadsList)[aItem1];
D_PAD* pad2 = (*m_PadsList)[aItem2];
if( pad1 == pad2 )
return 0;
int weight = abs( pad2->m_Pos.x - pad1->m_Pos.x ) +
abs( pad2->m_Pos.y - pad1->m_Pos.y );
return weight + 1;
}
/* Note about the ratsnest computation:
* Building the general ratsnest:
* For each net, the ratsnest is the set of lines connecting pads,
* using the shorter distance
* Therefore this problem is well known in graph therory, and sloved
* using the "minimum spanning tree".
* We use here an algorithm to build the minimum spanning tree known as Prim's algorithm
*/
/**
* Function Compile_Ratsnest
* Create the entire board ratsnest.
* Must be called after a board change (changes for
* pads, footprints or a read netlist ).
* @param aDC = the current device context (can be NULL)
* @param aDisplayStatus : if true, display the computation results
*/
void PCB_BASE_FRAME::Compile_Ratsnest( wxDC* aDC, bool aDisplayStatus )
{
wxString msg;
GetBoard()->m_Status_Pcb = 0; /* we want a full ratsnest computation, from the scratch */
ClearMsgPanel();
// Rebuild the full pads and net info list
RecalculateAllTracksNetcode();
if( aDisplayStatus )
{
msg.Printf( wxT( " %d" ), m_Pcb->GetPadsCount() );
AppendMsgPanel( wxT( "Pads" ), msg, RED );
msg.Printf( wxT( " %d" ), m_Pcb->m_NetInfo->GetCount() );
AppendMsgPanel( wxT( "Nets" ), msg, CYAN );
}
/* Compute the full ratsnest
* which can be see like all the possible links or logical connections.
* some of them are active (no track connected) and others are inactive
* (when tracks connect pads)
* This full ratsnest is not modified by track editing.
* It changes only when a netlist is read, or footprints are modified
*/
Build_Board_Ratsnest();
/* Compute the pad connections due to the existing tracks (physical connections) */
TestConnections( aDC );
/* Compute the active ratsnest, i.e. the unconnected links
*/
TestRatsNest( aDC, 0 );
// Redraw the active ratsnest ( if enabled )
if( GetBoard()->IsElementVisible(RATSNEST_VISIBLE) && aDC )
DrawGeneralRatsnest( aDC, 0 );
if( aDisplayStatus )
m_Pcb->DisplayInfo( this );
}
/* Sort function used by QSORT
* Sort pads by net code
*/
static bool sortByNetcode( const D_PAD* const & ref, const D_PAD* const & item )
{
return ref->GetNet() < item->GetNet();
}
/**
* Function to compute the full ratsnest
* This is the "basic" ratsnest depending only on pads.
*
* Create the sorted pad list (if necessary)
* The active pads (i.e included in a net ) are called nodes
* This pad list is sorted by net codes
* A ratsnest can be seen as a logical connection.
*
* Update :
* nb_nodes = Active pads count for the board
* nb_links = link count for the board (logical connection count)
* (there are n-1 links in a net which counting n active pads) .
*/
void PCB_BASE_FRAME::Build_Board_Ratsnest()
{
D_PAD* pad;
int noconn;
m_Pcb->m_NbNoconnect = 0;
m_Pcb->m_FullRatsnest.clear();
if( m_Pcb->GetPadsCount() == 0 )
return;
/* Created pad list and the net_codes if needed */
if( (m_Pcb->m_Status_Pcb & NET_CODES_OK) == 0 )
m_Pcb->m_NetInfo->BuildListOfNets();
for( unsigned ii = 0; ii<m_Pcb->GetPadsCount(); ++ii )
{
pad = m_Pcb->m_NetInfo->GetPad( ii );
pad->SetSubRatsnest( 0 );
}
if( m_Pcb->GetNodesCount() == 0 )
return; /* No useful connections. */
/* Ratsnest computation */
unsigned current_net_code = 1; // First net code is analyzed.
// (net_code = 0 -> no connect)
noconn = 0;
MIN_SPAN_TREE_PADS min_spanning_tree;
for( ; current_net_code < m_Pcb->m_NetInfo->GetCount(); current_net_code++ )
{
NETINFO_ITEM* net = m_Pcb->FindNet( current_net_code );
if( net == NULL ) //Should not occur
{
wxMessageBox( wxT( "Build_Board_Ratsnest() error: net not found" ) );
return;
}
net->m_RatsnestStartIdx = m_Pcb->GetRatsnestsCount();
min_spanning_tree.MSP_Init( &net->m_ListPad );
min_spanning_tree.BuildTree();
min_spanning_tree.AddItemsToRatsnest( m_Pcb->m_FullRatsnest );
net->m_RatsnestEndIdx = m_Pcb->GetRatsnestsCount();
}
m_Pcb->m_NbNoconnect = noconn;
m_Pcb->m_Status_Pcb |= LISTE_RATSNEST_ITEM_OK;
// Update the ratsnest display option (visible/invisible) flag
for( unsigned ii = 0; ii < m_Pcb->GetRatsnestsCount(); ii++ )
{
if( !GetBoard()->IsElementVisible(RATSNEST_VISIBLE) ) // Clear VISIBLE flag
m_Pcb->m_FullRatsnest[ii].m_Status &= ~CH_VISIBLE;
}
}
/**
* function DrawGeneralRatsnest
* Only ratsnest items with the status bit CH_VISIBLE set are displayed
* @param aDC = the current device context (can be NULL)
* @param aNetcode: if > 0, Display only the ratsnest relative to the
* corresponding net_code
*/
void PCB_BASE_FRAME::DrawGeneralRatsnest( wxDC* aDC, int aNetcode )
{
if( ( m_Pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK ) == 0 )
return;
if( ( m_Pcb->m_Status_Pcb & DO_NOT_SHOW_GENERAL_RASTNEST ) )
return;
if( aDC == NULL )
return;
const int state = CH_VISIBLE | CH_ACTIF;
for( unsigned ii = 0; ii < m_Pcb->GetRatsnestsCount(); ii++ )
{
RATSNEST_ITEM& item = m_Pcb->m_FullRatsnest[ii];
if( ( item.m_Status & state ) != state )
continue;
if( ( aNetcode <= 0 ) || ( aNetcode == item.GetNet() ) )
{
item.Draw( DrawPanel, aDC, GR_XOR, wxPoint( 0, 0 ) );
}
}
}
/**
* Function used by TestRatsNest
* Function testing the ratsnest between 2 blocks ( same net )
* The search is made between pads in block 1 and the others blocks
* The block n ( n > 1 ) is merged with block 1 by the smallest ratsnest
* The analysis uses the general ratsnest list.
* The function activate the smallest ratsnest between block 1 and the block n
* (activate a logical connexion)
* @param aRatsnestBuffer = the buffer to store NETINFO_ITEM* items
* @param net = the current NETINFO_ITEM for the current net
* output:
* .state member of the ratsnest
* @return blocks not connected count
*/
static int tst_rats_block_to_block( NETINFO_ITEM* net,
vector<RATSNEST_ITEM>& aRatsnestBuffer )
{
int current_num_block, min_block;
RATSNEST_ITEM* rats, * min_rats;
/* Search a link from a block to an other block */
min_rats = NULL;
for( unsigned ii = net->m_RatsnestStartIdx; ii < net->m_RatsnestEndIdx; ii++ )
{
rats = &aRatsnestBuffer[ii];
if( rats->m_PadStart->GetSubRatsnest() == rats->m_PadEnd->GetSubRatsnest() ) // Same block
continue;
if( min_rats == NULL )
min_rats = rats;
else if( min_rats->m_Lenght > rats->m_Lenght )
min_rats = rats;
}
if( min_rats == NULL )
return 1;
/* At this point we have found a link between 2 different blocks (clusters)
* we must set its status to ACTIVE and merge the 2 blocks
*/
min_rats->m_Status |= CH_ACTIF;
current_num_block = min_rats->m_PadStart->GetSubRatsnest();
min_block = min_rats->m_PadEnd->GetSubRatsnest();
if( min_block > current_num_block )
EXCHG( min_block, current_num_block );
/* Merging the 2 blocks in one cluster */
for( unsigned ii = 0; ii < net->m_ListPad.size(); ii++ )
{
if( net->m_ListPad[ii]->GetSubRatsnest() == current_num_block )
{
net->m_ListPad[ii]->SetSubRatsnest( min_block );
}
}
return current_num_block;
}
/**
* Function used by TestRatsNest_general
* The general ratsnest list must exists because this function explore this ratsnest
* Activates (set the CH_ACTIF flag) the ratsnest links between 2 pads when needed
* The function links 1 pad not already connected to an other pad (SubRatsnest = 0)
* and active the correspondint link
*
* @param start_rat_list = starting address for the ratsnest list
* @param end_rat_list = ending address for the ratsnest list
* @param current_num_block = last block number (computed from the track
* analysis)
*
* output:
* ratsnest list (status member set)
* and pad list (m_SubRatsnest set)
*
* @return new block number
*/
static int tst_rats_pad_to_pad( int current_num_block,
RATSNEST_ITEM* start_rat_list,
RATSNEST_ITEM* end_rat_list )
{
D_PAD* pad_start, * pad_end;
RATSNEST_ITEM* item;
for( item = start_rat_list; item < end_rat_list; item++ )
{
pad_start = item->m_PadStart;
pad_end = item->m_PadEnd;
/* Update the block if the 2 pads are not connected : a new block is created
*/
if( (pad_start->GetSubRatsnest() == 0) && (pad_end->GetSubRatsnest() == 0) )
{
current_num_block++;
pad_start->SetSubRatsnest( current_num_block );
pad_end->SetSubRatsnest( current_num_block );
item->m_Status |= CH_ACTIF;
}
/* If a pad is already connected : the other is merged in the current block */
else if( pad_start->GetSubRatsnest() == 0 )
{
pad_start->SetSubRatsnest( pad_end->GetSubRatsnest() );
item->m_Status |= CH_ACTIF;
}
else if( pad_end->GetSubRatsnest() == 0 )
{
pad_end->SetSubRatsnest( pad_start->GetSubRatsnest() );
item->m_Status |= CH_ACTIF;
}
}
return current_num_block;
}
/* function TestRatsNest
* determine the active links inside the full ratsnest
*
* I used an derived from the "lee algorithm".
* The algorithm explore the existing full ratnest
* This is a 2 steps algorithm (executed for each net).
* - First:
* Initialise for each pad the subratsnest id to its subnet value
* explore the full ratnest (relative to the net) and active a link each time at least one pad of
* the given link is not connected to an other pad by a track ( subratsnest = 0)
* If the 2 pads linked have both the subratsnest id = 0, a new subratsnest value is created
* - Second:
* explore the full ratnest (relative to the net) and find a link that links
* 2 pads having different subratsnest values
* Active the link and merge the 2 subratsnest value.
*
*/
void PCB_BASE_FRAME::TestRatsNest( wxDC* aDC, int aNetCode )
{
RATSNEST_ITEM* rats;
D_PAD* pad;
NETINFO_ITEM* net;
if( m_Pcb->GetPadsCount() == 0 )
return;
if( (m_Pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
Build_Board_Ratsnest();
for( int net_code = 1; net_code < (int) m_Pcb->m_NetInfo->GetCount(); net_code++ )
{
net = m_Pcb->FindNet( net_code );
wxCHECK_RET( net != NULL,
wxString::Format( wxT( "Net code %d not found!" ), net_code ) );
if( aNetCode && (net_code != aNetCode) )
continue;
int num_block = 0;
for( unsigned ip = 0; ip < net->m_ListPad.size(); ip++ )
{
pad = net->m_ListPad[ip];
int subnet = pad->GetSubNet();
pad->SetSubRatsnest( subnet );
num_block = MAX( num_block, subnet );
}
for( unsigned ii = net->m_RatsnestStartIdx; ii < net->m_RatsnestEndIdx; ii++ )
{
m_Pcb->m_FullRatsnest[ii].m_Status &= ~CH_ACTIF;
}
/* a - test connection between pads */
rats = &m_Pcb->m_FullRatsnest[0];
int icnt = tst_rats_pad_to_pad( num_block,
rats + net->m_RatsnestStartIdx,
rats + net->m_RatsnestEndIdx );
/* b - test connection between blocks (Iteration) */
while( icnt > 1 )
{
icnt = tst_rats_block_to_block( net, m_Pcb->m_FullRatsnest );
}
}
m_Pcb->m_NbNoconnect = 0;
for( unsigned ii = 0; ii < m_Pcb->GetRatsnestsCount(); ii++ )
{
if( m_Pcb->m_FullRatsnest[ii].m_Status & CH_ACTIF )
m_Pcb->m_NbNoconnect++;
}
}
int PCB_BASE_FRAME::TestOneRatsNest( wxDC* aDC, int aNetCode )
{
DrawGeneralRatsnest( aDC, aNetCode );
TestRatsNest( aDC, aNetCode );
DrawGeneralRatsnest( aDC, aNetCode );
return m_Pcb->GetRatsnestsCount();
}
void PCB_BASE_FRAME::build_ratsnest_module( MODULE* aModule )
{
// for local ratsnest calculation when moving a footprint:
// list of pads to use for this local ratsnets:
// this is the list of connected pads of the current module,
// and all pads connected to these pads:
static std::vector <D_PAD*> localPadList;
static unsigned pads_module_count; // node count (node = pad with a net
// code) for the footprint being moved
static unsigned internalRatsCount; // number of internal links (links
// between pads of the module)
D_PAD* pad_ref;
D_PAD* pad_externe;
int current_net_code;
int distance;
wxPoint pad_pos; // True pad position according to the
// current footprint position
if( (GetBoard()->m_Status_Pcb & LISTE_PAD_OK) == 0 )
{
GetBoard()->m_Status_Pcb = 0;
GetBoard()->m_NetInfo->BuildListOfNets();
}
/* Compute the "local" ratsnest if needed (when this footprint starts move)
* and the list of external pads to consider, i.e pads in others
* footprints which are "connected" to
* a pad in the current footprint
*/
if( (m_Pcb->m_Status_Pcb & RATSNEST_ITEM_LOCAL_OK) == 0 )
{
/* Compute the "internal" ratsnest, i.e the links between the current
* footprint pads
*/
localPadList.clear();
m_Pcb->m_LocalRatsnest.clear();
// collect active pads of the module:
for( pad_ref = aModule->m_Pads; pad_ref != NULL; pad_ref = pad_ref->Next() )
{
if( pad_ref->GetNet() == 0 )
continue;
localPadList.push_back( pad_ref );
pad_ref->SetSubRatsnest( 0 );
pad_ref->SetSubNet( 0 );
}
pads_module_count = localPadList.size();
if( pads_module_count == 0 )
return; /* no connection! */
sort( localPadList.begin(), localPadList.end(), sortByNetcode );
/* Build the list of pads linked to the current footprint pads */
current_net_code = 0;
for( unsigned ii = 0; ii < pads_module_count; ii++ )
{
pad_ref = localPadList[ii];
if( pad_ref->GetNet() == current_net_code )
continue;
// A new net was found, load all pads of others modules members of this net:
NETINFO_ITEM* net = m_Pcb->FindNet( pad_ref->GetNet() );
if( net == NULL ) //Should not occur
{
wxMessageBox( wxT( "build_ratsnest_module() error: net not found" ) );
return;
}
for( unsigned jj = 0; jj < net->m_ListPad.size(); jj++ )
{
pad_externe = net->m_ListPad[jj];
if( pad_externe->GetParent() == aModule )
continue;
pad_externe->SetSubRatsnest( 0 );
pad_externe->SetSubNet( 0 );
localPadList.push_back( pad_externe );
}
}
/* Sort the pad list by net_code */
sort( localPadList.begin() + pads_module_count, localPadList.end(),
sortByNetcode );
/* Compute the internal rats nest:
* this is the same as general ratsnest, but considers only the current
* footprint pads it is therefore not time consuming, and it is made only
* once
*/
current_net_code = localPadList[0]->GetNet();
MIN_SPAN_TREE_PADS min_spanning_tree;
std::vector<D_PAD*> padsBuffer; // contains pads of only one net
for( unsigned ii = 0; ii < pads_module_count; ii++ )
{
/* Search the end of pad list relative to the current net */
unsigned jj = ii + 1;
for( ; jj <= pads_module_count; jj++ )
{
if( jj >= pads_module_count )
break;
if( localPadList[jj]->GetNet() != current_net_code )
break;
}
for(unsigned kk = ii; kk < jj; kk++ )
padsBuffer.push_back( localPadList[kk] );
min_spanning_tree.MSP_Init( &padsBuffer );
min_spanning_tree.BuildTree();
min_spanning_tree.AddItemsToRatsnest( m_Pcb->m_LocalRatsnest );
padsBuffer.clear();
ii = jj;
if( ii < localPadList.size() )
current_net_code = localPadList[ii]->GetNet();
}
internalRatsCount = m_Pcb->m_LocalRatsnest.size();
// set the flag LOCAL_RATSNEST_ITEM of the ratsnest status:
for( unsigned ii = 0; ii < m_Pcb->m_LocalRatsnest.size(); ii++ )
m_Pcb->m_LocalRatsnest[ii].m_Status = LOCAL_RATSNEST_ITEM;
m_Pcb->m_Status_Pcb |= RATSNEST_ITEM_LOCAL_OK;
} // End of internal ratsnest build
/* This section computes the "external" ratsnest: it is done when the
* footprint position changes
*
* This section search:
* for each current module pad the nearest neighbor external pad (of
* course for the same net code).
* For each current footprint cluster of pad (pads having the same net
* code),
* we search the smaller rats nest.
* so, for each net, only one rats nest item is created
*/
RATSNEST_ITEM local_rats;
local_rats.m_Lenght = INT_MAX;
local_rats.m_Status = 0;
bool addRats = false;
// Erase external ratsnest items:
if( internalRatsCount < m_Pcb->m_LocalRatsnest.size() )
m_Pcb->m_LocalRatsnest.erase( m_Pcb->m_LocalRatsnest.begin() + internalRatsCount,
m_Pcb->m_LocalRatsnest.end() );
current_net_code = localPadList[0]->GetNet();
for( unsigned ii = 0; ii < pads_module_count; ii++ )
{
pad_ref = localPadList[ii];
if( pad_ref->GetNet() != current_net_code )
{
/* if needed, creates a new ratsnest for the old net */
if( addRats )
{
m_Pcb->m_LocalRatsnest.push_back( local_rats );
}
addRats = false;
current_net_code = pad_ref->GetNet();
local_rats.m_Lenght = INT_MAX;
}
pad_pos = pad_ref->m_Pos - g_Offset_Module;
// Search the nearest external pad of this current pad
for( unsigned jj = pads_module_count; jj < localPadList.size(); jj++ )
{
pad_externe = localPadList[jj];
/* we search pads having the same net code */
if( pad_externe->GetNet() < pad_ref->GetNet() )
continue;
if( pad_externe->GetNet() > pad_ref->GetNet() ) // pads are sorted by net code
break;
distance = abs( pad_externe->m_Pos.x - pad_pos.x ) +
abs( pad_externe->m_Pos.y - pad_pos.y );
if( distance < local_rats.m_Lenght )
{
local_rats.m_PadStart = pad_ref;
local_rats.m_PadEnd = pad_externe;
local_rats.SetNet( pad_ref->GetNet() );
local_rats.m_Lenght = distance;
local_rats.m_Status = 0;
addRats = true;
}
}
}
if( addRats ) // Ensure the last created rats nest item is stored in buffer
m_Pcb->m_LocalRatsnest.push_back( local_rats );
}
void PCB_BASE_FRAME::TraceModuleRatsNest( wxDC* DC )
{
if( DC == NULL )
return;
if( ( m_Pcb->m_Status_Pcb & RATSNEST_ITEM_LOCAL_OK ) == 0 )
return;
int tmpcolor = g_ColorsSettings.GetItemColor(RATSNEST_VISIBLE);
for( unsigned ii = 0; ii < m_Pcb->m_LocalRatsnest.size(); ii++ )
{
RATSNEST_ITEM* rats = &m_Pcb->m_LocalRatsnest[ii];
if( rats->m_Status & LOCAL_RATSNEST_ITEM )
{
g_ColorsSettings.SetItemColor(RATSNEST_VISIBLE, YELLOW);
rats->Draw( DrawPanel, DC, GR_XOR, g_Offset_Module );
}
else
{
g_ColorsSettings.SetItemColor(RATSNEST_VISIBLE, tmpcolor);
wxPoint tmp = rats->m_PadStart->m_Pos;
rats->m_PadStart->m_Pos -= g_Offset_Module;
rats->Draw( DrawPanel, DC, GR_XOR, wxPoint( 0, 0 ) );
rats->m_PadStart->m_Pos = tmp;
}
}
g_ColorsSettings.SetItemColor( RATSNEST_VISIBLE, tmpcolor );
}
/*
* Construction of the list mode display for quick calculation
* in real time the net of a pad in the paths of a track starting
* on the pad.
*
* Parameters:
* Pad_ref (if null: 0 has put the number of ratsnest)
* Ox, oy = coord of extremity of the track record
* Init (flag)
* = 0: update of the ratsnest.
* <> 0: Creating a list
*/
/* Buffer to store pads coordinates when creating a track.
* these pads are members of the net
* and when the mouse is moved, the g_MaxLinksShowed links to neighbors are
* drawn
*/
static std::vector <wxPoint> s_RatsnestMouseToPads;
static wxPoint s_CursorPos; // Coordinate of the moving point (mouse cursor and
// end of current track segment)
/* Used by build_ratsnest_pad(): sort function by link length (manhattan
* distance)
*/
static bool sort_by_localnetlength( const wxPoint& ref, const wxPoint& compare )
{
wxPoint deltaref = ref - s_CursorPos;
wxPoint deltacmp = compare - s_CursorPos;
// = distance between ref coordinate and pad ref
int lengthref = abs( deltaref.x ) + abs( deltaref.y );
// distance between ref coordinate and the other pad
int lengthcmp = abs( deltacmp.x ) + abs( deltacmp.y );
return lengthref < lengthcmp;
}
void PCB_BASE_FRAME::build_ratsnest_pad( BOARD_ITEM* ref, const wxPoint& refpos, bool init )
{
int current_net_code = 0, conn_number = 0;
D_PAD* pad_ref = NULL;
if( ( ( m_Pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK ) == 0 )
|| ( ( m_Pcb->m_Status_Pcb & LISTE_PAD_OK ) == 0 )
|| ( ( m_Pcb->m_Status_Pcb & NET_CODES_OK ) == 0 ) )
{
s_RatsnestMouseToPads.clear();
return;
}
s_CursorPos = refpos;
if( init )
{
s_RatsnestMouseToPads.clear();
if( ref == NULL )
return;
switch( ref->Type() )
{
case PCB_PAD_T:
pad_ref = (D_PAD*) ref;
current_net_code = pad_ref->GetNet();
conn_number = pad_ref->GetSubNet();
break;
case PCB_TRACE_T:
case PCB_VIA_T:
{
TRACK* track_ref = (TRACK*) ref;
current_net_code = track_ref->GetNet();
conn_number = track_ref->GetSubNet();
break;
}
default:
;
}
if( current_net_code <= 0 )
return;
NETINFO_ITEM* net = m_Pcb->FindNet( current_net_code );
if( net == NULL ) // Should not occur
{
wxMessageBox( wxT( "build_ratsnest_pad() error: net not found" ) );
return;
}
// Create a list of pads candidates ( pads not already connected to the
// current track:
for( unsigned ii = 0; ii < net->m_ListPad.size(); ii++ )
{
D_PAD* pad = net->m_ListPad[ii];
if( pad == pad_ref )
continue;
if( !pad->GetSubNet() || (pad->GetSubNet() != conn_number) )
s_RatsnestMouseToPads.push_back( pad->m_Pos );
}
} /* end if Init */
if( s_RatsnestMouseToPads.size() > 1 )
sort( s_RatsnestMouseToPads.begin(), s_RatsnestMouseToPads.end(), sort_by_localnetlength );
}
/*
* Displays a "ratsnest" during track creation
*/
void PCB_BASE_FRAME::trace_ratsnest_pad( wxDC* DC )
{
if( DC == NULL )
return;
if( s_RatsnestMouseToPads.size() == 0 )
return;
GRSetDrawMode( DC, GR_XOR );
for( int ii = 0; ii < (int) s_RatsnestMouseToPads.size(); ii++ )
{
if( ii >= g_MaxLinksShowed )
break;
GRLine( &DrawPanel->m_ClipBox, DC, s_CursorPos, s_RatsnestMouseToPads[ii], 0, YELLOW );
}
}