/** * @file ratsnest.cpp * @brief Ratsnets functions. */ #include "fctsys.h" #include "gr_basic.h" #include "common.h" #include "class_drawpanel.h" #include "confirm.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 "protos.h" static std::vector s_localPadBuffer; // for local ratsnest // calculations when moving a // footprint: buffer of pads to // consider static bool DisplayRastnestInProgress; // Enable the display of the // ratsnest during the ratsnest // computations /* Note about the ratsnest computation: * Building the general ratsnest: * I used the "lee algorithm". * This is a 2 steps algorithm. * the m_SubRatsnest member of pads handle a "block number" or a "cluster * number" or a "subnet number" * initially, m_SubRatsnest = 0 (pad not connected). * Build_Board_Ratsnest( wxDC* DC ) Create this ratsnest * for each net: * First: * we create a link (and therefore a logical block) between 2 pad. This is * achieved by: * search for a pad without link. * search its nearest pad * link these 2 pads (i.e. create a ratsnest item) * the pads are grouped in a logical block ( a cluster). * until no pad without link found. * Each logical block has a number called block number or "subnet number", * stored in m_SubRatsnest member for each pad of the block. * The first block has its block number = 1, the second is 2 ... * the function to do that is gen_rats_pad_to_pad() * * Secondly: * The first pass created many logical blocks * A block contains 2 or more pads. * we create links between 2 block. This is achieved by: * Test all pads in the first block, and search (for each pad) * a neighbor in other blocks and compute the distance between pads, * We select the pad pair which have the smallest distance. * These 2 pads are linked (i.e. a new ratsnest item is created between the 2 * pads) * and the 2 block are merged. * Therefore the logical block 1 contains the initial block 1 "eats" the pads * of the other block * The computation is made until only one block is found. * the function used is gen_rats_block_to_block() * * * How existing and new tracks are handled: * The complete ratsnest (using the pad analysis) is computed. * it is independent of the tracks and handle the "logical connections". * It depends only on the footprints geometry (and the netlist), * and must be computed only after a netlist read or a footprints geometry * change. * Each link (ratsnest) can only be INACTIVE (because pads are connected by a * track) or ACTIVE (no tracks) * * After the complete ratsnest is built, or when a track is added or deleted, * we run an algorithm derived from the complete ratsnest computation. * it is much faster because it analysis only the existing ratsnest and not all * the pads list * and determine only if an existing ratsnest must be activated * (no physical track exists) or not (a physical track exists) * if a track is added or deleted only the corresponding net is tested. * * the m_SubRatsnest member of pads is set to 0 (no blocks), and all links are * set to INACTIVE (ratsnest not show). * Before running this fast lee algorithm, we create blocks (and their * corresponding block number) * by grouping pads connected by tracks. * So, when tracks exists, the fast lee algorithm is started with some blocks * already created. * because the fast lee algorithm test only the ratsnest and does not search * for nearest pads (this search was previously made) the online ratsnest * can be done when a track is created without noticeable computing time * First: * for all links (in this step, all are inactive): * search for a link which have 1 (or 2) pad having the m_SubRatsnest member = * 0. * if found the link is set to ACTIVE (i.e. the ratsnest will be showed) and * the pad is merged with the block * or a new block is created ( see tst_rats_pad_to_pad() ). * Secondly: * blocks are tested: * for all links we search if the 2 pads linked are in 2 different block. * if yes, the link status is set to ACTIVE, and the 2 block are merged * until only one block is found * ( see tst_rats_block_to_block() ) */ /** * 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; DisplayRastnestInProgress = true; 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 track 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( aDC ); /* Compute the pad connections due to the existing tracks (physical connections) */ TestConnections( aDC ); /* Compute the active ratsnest, i.e. the unconnected links * it is faster than Build_Board_Ratsnest() * because many optimizations and computations are already made */ 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 int sortByNetcode( const void* o1, const void* o2 ) { D_PAD** pt_ref = (D_PAD**) o1; D_PAD** pt_compare = (D_PAD**) o2; return (*pt_ref)->GetNet() - (*pt_compare)->GetNet(); } /* Sort function used by QSORT * Sort ratsnest by length */ static int sort_by_length( const void* o1, const void* o2 ) { RATSNEST_ITEM* ref = (RATSNEST_ITEM*) o1; RATSNEST_ITEM* compare = (RATSNEST_ITEM*) o2; return ref->m_Lenght - compare->m_Lenght; } /** * Function used by Build_Board_Ratsnest * This function creates a ratsnest between two blocks ( which fit the same * net ) * A block is a group of pads already linked (by a previous ratsnest * computation, or tracks) * The search is made between the pads in block 1 (the reference block) and * other blocks * the block n ( n > 1 ) it connected to block 1 by their 2 nearest pads. * When the block is found, it is merged with the block 1 * the D_PAD member m_SubRatsnest handles the block number * @param aRatsnestBuffer = a std::vector buffer to fill with * new ratsnest items * @param aPadBuffer = a std::vector that is the list of pads to consider * @param aPadIdxStart = starting index (within the pad list) for search * @param aPadIdxMax = ending index (within the pad list) for search * @return blocks not connected count */ static int gen_rats_block_to_block ( std::vector& aRatsnestBuffer, std::vector& aPadBuffer, unsigned aPadIdxStart, unsigned aPadIdxMax ) { int dist_min, current_dist; int current_num_block = 1; int padBlock1Idx = -1; // Index in aPadBuffer for the "better" pad // found in block 1 int padBlockToMergeIdx = -1; // Index in aPadBuffer for the "better" pad // found in block to merge dist_min = 0x7FFFFFFF; /* Search the nearest pad from block 1 */ for( unsigned ii = aPadIdxStart; ii < aPadIdxMax; ii++ ) { D_PAD* ref_pad = aPadBuffer[ii]; /* search a pad which is in the block 1 */ if( ref_pad->GetSubRatsnest() != 1 ) continue; /* pad is found, search its nearest neighbor in other blocks */ for( unsigned jj = aPadIdxStart; jj < aPadIdxMax; jj++ ) { D_PAD* curr_pad = aPadBuffer[jj]; if( curr_pad->GetSubRatsnest() == 1 ) // not in an other block continue; /* Compare distance between pads ("Manhattan" distance) */ current_dist = abs( curr_pad->m_Pos.x - ref_pad->m_Pos.x ) + abs( curr_pad->m_Pos.y - ref_pad->m_Pos.y ); if( dist_min > current_dist ) // we have found a better pad pair { // The tested block can be a good candidate for merging // we memorize the "best" current values for merging current_num_block = curr_pad->GetSubRatsnest(); dist_min = current_dist; padBlockToMergeIdx = jj; padBlock1Idx = ii; } } } /* The reference block is labeled block 1. * if current_num_block != 1 we have found an other block, and we must * merge it with the reference block * The link is made by the 2 nearest pads */ if( current_num_block > 1 ) { /* The block n (n=current_num_block) is merged with the bloc 1 : * to do that, we set the m_SubRatsnest member to 1 for all pads in * block n */ for( unsigned ii = aPadIdxStart; ii < aPadIdxMax; ii++ ) { D_PAD* pad = aPadBuffer[ii]; if( pad->GetSubRatsnest() == current_num_block ) pad->SetSubRatsnest( 1 ); } if( padBlock1Idx < 0 ) { DisplayError( NULL, wxT( "gen_rats_block_to_block() internal error" ) ); } else { /* Create the new ratsnest */ RATSNEST_ITEM net; net.SetNet( aPadBuffer[padBlock1Idx]->GetNet() ); net.m_Status = CH_ACTIF | CH_VISIBLE; net.m_Lenght = dist_min; net.m_PadStart = aPadBuffer[padBlock1Idx]; net.m_PadEnd = aPadBuffer[padBlockToMergeIdx]; aRatsnestBuffer.push_back( net ); } } return current_num_block; } /** * Function used by Build_Board_Ratsnest * this is the first pass of the lee algorithm * This function creates the link (ratsnest) between 2 pads ( fitting the same * net ) * the function search for a first not connected pad and search its nearest * neighbor * Its creates a block if the 2 pads are not connected, or merge the * unconnected pad to the existing block. * These blocks include 2 pads and the 2 pads are linked by a ratsnest. * * @param aRatsnestBuffer = a std::vector buffer to fill with * new ratsnest items * @param aPadBuffer = a std::vector that is the list of pads to * consider * @param aPadIdxStart = starting index (within the pad list) for search * @param aPadIdxMax = ending index (within the pad list) for search * @param current_num_block = Last existing block number of pads * These block are created by the existing tracks analysis * * @return the last block number used */ static int gen_rats_pad_to_pad( vector& aRatsnestBuffer, std::vector& aPadBuffer, unsigned aPadIdxStart, unsigned aPadIdxMax, int current_num_block ) { int dist_min, current_dist; D_PAD* ref_pad, * pad; for( unsigned ii = aPadIdxStart; ii < aPadIdxMax; ii++ ) { ref_pad = aPadBuffer[ii]; if( ref_pad->GetSubRatsnest() ) continue; // Pad already connected dist_min = 0x7FFFFFFF; int padBlockToMergeIdx = -1; // Index in aPadBuffer for the "better" // pad found in block to merge for( unsigned jj = aPadIdxStart; jj < aPadIdxMax; jj++ ) { if( ii == jj ) continue; pad = aPadBuffer[jj]; /* Compare distance between pads ("Manhattan" distance) */ current_dist = abs( pad->m_Pos.x - ref_pad->m_Pos.x ) + abs( pad->m_Pos.y - ref_pad->m_Pos.y ); if( dist_min > current_dist ) { dist_min = current_dist; padBlockToMergeIdx = jj; } } if( padBlockToMergeIdx >= 0 ) { pad = aPadBuffer[padBlockToMergeIdx]; /* Update the block number * if the 2 pads are not already created : a new block is created */ if( (pad->GetSubRatsnest() == 0) && (ref_pad->GetSubRatsnest() == 0) ) { current_num_block++; // Creates a new block number (or subratsnest) pad->SetSubRatsnest( current_num_block ); ref_pad->SetSubRatsnest( current_num_block ); } /* If a pad is already connected connected : merge the other pad in * the block */ else { ref_pad->SetSubRatsnest( pad->GetSubRatsnest() ); } /* Create the new ratsnest item */ RATSNEST_ITEM rast; rast.SetNet( ref_pad->GetNet() ); rast.m_Status = CH_ACTIF | CH_VISIBLE; rast.m_Lenght = dist_min; rast.m_PadStart = ref_pad; rast.m_PadEnd = pad; aRatsnestBuffer.push_back( rast ); } } return current_num_block; } /** * Function to compute the full ratsnest (using the LEE algorithm ) * In the functions tracks are not considered * This is only 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 * * - Compute the ratsnest (LEE algorithm ): * a - Create the ratsnest between a not connected pad and its nearest * neighbor. Blocks of pads are created * b - Create the ratsnest between blocks: * Test the pads of the 1st block and create a link (ratsnest) * with the nearest pad found in an other block. * The other block is merged with the first block. * until only one block is left. * * 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 for an connection which have n active pads) . * */ void PCB_BASE_FRAME::Build_Board_Ratsnest( wxDC* DC ) { 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; iiGetPadsCount(); ++ii ) { pad = m_Pcb->m_NetInfo->GetPad( ii ); pad->SetSubRatsnest( 0 ); } if( m_Pcb->GetNodesCount() == 0 ) return; /* No useful connections. */ /* Ratsnest computation */ DisplayRastnestInProgress = true; unsigned current_net_code = 1; // First net code is analyzed. // (net_code = 0 -> no connect) noconn = 0; 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 { DisplayError( this, wxT( "Build_Board_Ratsnest() error: net not found" ) ); return; } net->m_RatsnestStartIdx = m_Pcb->GetRatsnestsCount(); // Search for the last subratsnest already in use int num_block = 0; for( unsigned ii = 0; ii < net->m_ListPad.size(); ii++ ) { pad = net->m_ListPad[ii]; if( num_block < pad->GetSubRatsnest() ) num_block = pad->GetSubRatsnest(); } /* Compute the ratsnest relative to the current net */ /* a - first pass : create the blocks from not already in block pads */ int icnt = gen_rats_pad_to_pad( m_Pcb->m_FullRatsnest, net->m_ListPad, 0, net->m_ListPad.size(), num_block ); /* b - blocks connection (Iteration) */ while( icnt > 1 ) { icnt = gen_rats_block_to_block( m_Pcb->m_FullRatsnest, net->m_ListPad, 0, net->m_ListPad.size() ); net = m_Pcb->FindNet( current_net_code ); } net->m_RatsnestEndIdx = m_Pcb->GetRatsnestsCount(); /* sort by length */ net = m_Pcb->FindNet( current_net_code ); if( ( net->m_RatsnestEndIdx - net->m_RatsnestStartIdx ) > 1 ) { RATSNEST_ITEM* rats = &m_Pcb->m_FullRatsnest[0]; qsort( rats + net->m_RatsnestStartIdx, net->m_RatsnestEndIdx - net->m_RatsnestStartIdx, sizeof(RATSNEST_ITEM), sort_by_length ); } } m_Pcb->m_NbNoconnect = noconn; m_Pcb->m_Status_Pcb |= LISTE_RATSNEST_ITEM_OK; // erase the ratsnest displayed on screen if needed 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; if( DC ) m_Pcb->m_FullRatsnest[ii].Draw( DrawPanel, DC, GR_XOR, wxPoint( 0, 0 ) ); } } /** * function Displays the general ratsnest * Only ratsnest with the status bit CH_VISIBLE is 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; 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 like gen_rats_block_to_block(..) * 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 * Difference between gen_rats_block_to_block(..): * The analysis is not made pads to pads but 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& 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 * Activates the ratsnest between 2 pads ( assumes the same net ) * The function links 1 pad not already connected an other pad and activate * some blocks linked by a ratsnest * Its test only the existing ratsnest and activate some ratsnest (status bit * CH_ACTIF set) * * @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; } 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( aDC ); 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 ) { DisplayRastnestInProgress = false; DrawGeneralRatsnest( aDC, aNetCode ); TestRatsNest( aDC, aNetCode ); DrawGeneralRatsnest( aDC, aNetCode ); return m_Pcb->GetRatsnestsCount(); } void PCB_BASE_FRAME::build_ratsnest_module( MODULE* aModule ) { 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** baseListePad; 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 ) goto CalculateExternalRatsnest; /* Compute the "internal" ratsnest, i.e the links between the current *footprint pads */ s_localPadBuffer.clear(); m_Pcb->m_LocalRatsnest.clear(); for( pad_ref = aModule->m_Pads; pad_ref != NULL; pad_ref = pad_ref->Next() ) { if( pad_ref->GetNet() == 0 ) continue; s_localPadBuffer.push_back( pad_ref ); pad_ref->SetSubRatsnest( 0 ); pad_ref->SetSubNet( 0 ); } pads_module_count = s_localPadBuffer.size(); if( pads_module_count == 0 ) return; /* no connection! */ qsort( &s_localPadBuffer[0], pads_module_count, sizeof( D_PAD* ), sortByNetcode ); /* Build the list of pads linked to the current footprint pads */ DisplayRastnestInProgress = false; current_net_code = 0; for( unsigned ii = 0; ii < pads_module_count; ii++ ) { pad_ref = s_localPadBuffer[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 { DisplayError( this, 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 ); s_localPadBuffer.push_back( pad_externe ); } } /* Sort the pad list by net_code */ baseListePad = &s_localPadBuffer[0]; qsort( baseListePad + pads_module_count, s_localPadBuffer.size() - pads_module_count, sizeof(D_PAD*), 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 = s_localPadBuffer[0]->GetNet(); 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( s_localPadBuffer[jj]->GetNet() != current_net_code ) break; } /* End of list found: */ /* a - first step of lee algorithm : build the pad to pad link list */ int icnt = gen_rats_pad_to_pad( m_Pcb->m_LocalRatsnest, s_localPadBuffer, ii, jj, 0 ); /* b - second step of lee algorithm : build the block to block link *list (Iteration) */ while( icnt > 1 ) { icnt = gen_rats_block_to_block( m_Pcb->m_LocalRatsnest, s_localPadBuffer, ii, jj ); } ii = jj; if( ii < s_localPadBuffer.size() ) current_net_code = s_localPadBuffer[ii]->GetNet(); } internalRatsCount = m_Pcb->m_LocalRatsnest.size(); /* set the ratsnest status, flag LOCAL_RATSNEST_ITEM */ 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; /* This section computes the "external" ratsnest: must be done when the * footprint position changes */ CalculateExternalRatsnest: /* 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 = 0x7FFFFFFF; local_rats.m_Status = 0; bool addRats = false; 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 = s_localPadBuffer[0]->GetNet(); for( unsigned ii = 0; ii < pads_module_count; ii++ ) { pad_ref = s_localPadBuffer[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 = 0x7FFFFFFF; } 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 < s_localPadBuffer.size(); jj++ ) { pad_externe = s_localPadBuffer[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 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 { DisplayError( this, 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 ); } }