You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							885 lines
						
					
					
						
							29 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							885 lines
						
					
					
						
							29 KiB
						
					
					
				| /** | |
|  * @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 AddTreeToRatsnest | |
|      * Adds the current minimum spanning tree as ratsnest items | |
|      * to the main ratsnest list | |
|      * @param aRatsnestList = the main ratsnest list | |
|      */ | |
|     void AddTreeToRatsnest( 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::AddTreeToRatsnest( 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->GetPosition().x - pad1->GetPosition().x ) + | |
|                  abs( pad2->GetPosition().y - pad1->GetPosition().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->GetPadCount() ); | |
|         AppendMsgPanel( wxT( "Pads" ), msg, RED ); | |
|         msg.Printf( wxT( " %d" ), m_Pcb->GetNetCount() ); | |
|         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(); | |
| 
 | |
|     /* Compute the active ratsnest, i.e. the unconnected links | |
|      */ | |
|     TestForActiveLinksInRatsnest( 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->GetPadCount() == 0 ) | |
|         return; | |
| 
 | |
|     // Created pad list and the net_codes if needed | |
|     if( (m_Pcb->m_Status_Pcb & NET_CODES_OK) == 0 ) | |
|         m_Pcb->BuildListOfNets(); | |
| 
 | |
|     for( unsigned ii = 0; ii<m_Pcb->GetPadCount(); ++ii ) | |
|     { | |
|         pad = m_Pcb->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->GetNetCount(); 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_PadInNetList ); | |
|         min_spanning_tree.BuildTree(); | |
|         min_spanning_tree.AddTreeToRatsnest( 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( m_canvas, aDC, GR_XOR, wxPoint( 0, 0 ) ); | |
|         } | |
|     } | |
| } | |
| 
 | |
| 
 | |
| /** | |
|  * Function used by TestForActiveLinksInRatsnest | |
|  *  Function testing the ratsnest between 2 blocks ( of the 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 and linked by the smallest ratsnest | |
|  *  between block 1 and the block n (activate the logical connection) | |
|  *  @param  aRatsnestBuffer = the buffer to store NETINFO_ITEM* items | |
|  *  @param  aNetinfo = the current NETINFO_ITEM for the current net | |
|  *  output: .state member, bit CH_ACTIF of the ratsnest item | |
|  *  @return  last subratsnest id in use | |
|  */ | |
| static int tst_links_between_blocks( NETINFO_ITEM*          aNetinfo, | |
|                                     vector<RATSNEST_ITEM>& aRatsnestBuffer ) | |
| { | |
|     int            subratsnest_id, min_id; | |
|     RATSNEST_ITEM* link, * best_link; | |
| 
 | |
|     // Search a link from a block to an other block | |
|     best_link = NULL; | |
| 
 | |
|     for( unsigned ii = aNetinfo->m_RatsnestStartIdx; ii < aNetinfo->m_RatsnestEndIdx; ii++ ) | |
|     { | |
|         link = &aRatsnestBuffer[ii]; | |
| 
 | |
|         // If this link joints 2 pads inside the same block, do nothing | |
|         // (these pads are already connected) | |
|         if( link->m_PadStart->GetSubRatsnest() == link->m_PadEnd->GetSubRatsnest() ) | |
|             continue; | |
| 
 | |
|         // This link joints 2 pads of different blocks: this is a candidate, | |
|         // but we want to select the shorter link, so use it only if it is shorter | |
|         // than the previous candidate: | |
|         if( best_link == NULL )  // no candidate | |
|             best_link = link; | |
|         else if( best_link->m_Lenght > link->m_Lenght )  // It is a better candidate. | |
|             best_link = link; | |
|     } | |
| 
 | |
|     if( best_link == NULL ) | |
|         return 1; | |
| 
 | |
|     /* At this point we have found a link between 2 different blocks (subratsnest) | |
|      * we must set its status to ACTIVE and merge the 2 blocks | |
|      */ | |
|     best_link->m_Status |= CH_ACTIF; | |
|     subratsnest_id   = best_link->m_PadStart->GetSubRatsnest(); | |
|     min_id = best_link->m_PadEnd->GetSubRatsnest(); | |
| 
 | |
|     if( min_id > subratsnest_id ) | |
|         EXCHG( min_id, subratsnest_id ); | |
| 
 | |
|     // Merge the 2 blocks in one sub ratsnest: | |
|     for( unsigned ii = 0; ii < aNetinfo->m_PadInNetList.size(); ii++ ) | |
|     { | |
|         if( aNetinfo->m_PadInNetList[ii]->GetSubRatsnest() == subratsnest_id ) | |
|         { | |
|             aNetinfo->m_PadInNetList[ii]->SetSubRatsnest( min_id ); | |
|         } | |
|     } | |
| 
 | |
|     return subratsnest_id; | |
| } | |
| 
 | |
| 
 | |
| /** | |
|  * Function used by TestForActiveLinksInRatsnest_general | |
|  *  The general ratsnest list must exists because this function explores this ratsnest | |
|  *  Activates (i.e. set the CH_ACTIF flag) the ratsnest links between 2 pads when | |
|  *  at least one pad not already connected (SubRatsnest = 0) | |
|  *  and actives the corresponding link | |
|  * | |
|  * @param   aFirstItem = starting address for the ratsnest list | |
|  * @param   aLastItem   = ending address for the ratsnest list | |
|  * @param   aCurrSubRatsnestId =  last sub ratsnest id in use (computed from the track | |
|  * analysis) | |
|  * | |
|  *      output: | |
|  *          ratsnest list (status member bit CH_ACTIF set) | |
|  *          and pads linked (m_SubRatsnest value set) | |
|  * | |
|  * @return new block number | |
|  */ | |
| static void tst_links_between_pads( int &      aCurrSubRatsnestId, | |
|                                 RATSNEST_ITEM* aFirstItem, | |
|                                 RATSNEST_ITEM* aLastItem ) | |
| { | |
|     for( RATSNEST_ITEM* item = aFirstItem; item < aLastItem; item++ ) | |
|     { | |
|         D_PAD* pad_start = item->m_PadStart; | |
|         D_PAD* pad_end   = item->m_PadEnd; | |
| 
 | |
|         /* Update the current SubRatsnest if the 2 pads are not connected : | |
|          * a new cluster is created and the link activated | |
|          */ | |
|         if( (pad_start->GetSubRatsnest() == 0) && (pad_end->GetSubRatsnest() == 0) ) | |
|         { | |
|             aCurrSubRatsnestId++; | |
|             pad_start->SetSubRatsnest( aCurrSubRatsnestId ); | |
|             pad_end->SetSubRatsnest( aCurrSubRatsnestId ); | |
|             item->m_Status |= CH_ACTIF; | |
|         } | |
| 
 | |
|         /* If a pad is already connected to a subratsnest: activate the link | |
|          * the pad other is merged in the existing subratsnest | |
|          */ | |
|         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; | |
|         } | |
|     } | |
| } | |
| 
 | |
| /* function TestForActiveLinksInRatsnest | |
|  * determine the active links inside the full ratsnest | |
|  * | |
|  * I used an algorithm inspired by the "Lee algorithm". | |
|  * The idea is all pads must be connected by a physical track or a logical track | |
|  * a physical track is the existing track on copper layers. | |
|  * a logical track is the link that must be activated (visible) if | |
|  * no track found between 2 pads. | |
|  * 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. | |
|  * | |
|  * This is usually fast because the ratsnest is not built here: it is just explored | |
|  * to see what link must be activated | |
|  */ | |
| void PCB_BASE_FRAME::TestForActiveLinksInRatsnest( int aNetCode ) | |
| { | |
|     RATSNEST_ITEM* rats; | |
|     D_PAD*         pad; | |
|     NETINFO_ITEM*  net; | |
| 
 | |
|     if( m_Pcb->GetPadCount() == 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->GetNetCount(); 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; | |
| 
 | |
|         // Create subratsnests id from subnets created by existing tracks: | |
|         int subratsnest = 0; | |
|         for( unsigned ip = 0; ip < net->m_PadInNetList.size(); ip++ ) | |
|         { | |
|             pad = net->m_PadInNetList[ip]; | |
|             int subnet = pad->GetSubNet(); | |
|             pad->SetSubRatsnest( subnet ); | |
|             subratsnest = MAX( subratsnest, subnet ); | |
|         } | |
| 
 | |
|         for( unsigned ii = net->m_RatsnestStartIdx; ii < net->m_RatsnestEndIdx; ii++ ) | |
|         { | |
|             m_Pcb->m_FullRatsnest[ii].m_Status &= ~CH_ACTIF; | |
|         } | |
| 
 | |
|         // First pass - activate links for not connected pads | |
|         rats = &m_Pcb->m_FullRatsnest[0]; | |
|         tst_links_between_pads( subratsnest, | |
|                                 rats + net->m_RatsnestStartIdx, | |
|                                 rats + net->m_RatsnestEndIdx ); | |
| 
 | |
|         // Second pass activate links between blocks (Iteration) | |
|         while( subratsnest > 1 ) | |
|         { | |
|             subratsnest = tst_links_between_blocks( 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].IsActive() ) | |
|             m_Pcb->m_NbNoconnect++; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| 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()->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_PadInNetList.size(); jj++ ) | |
|             { | |
|                 pad_externe = net->m_PadInNetList[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.AddTreeToRatsnest( 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->GetPosition() - 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->GetPosition().x - pad_pos.x ) + | |
|                        abs( pad_externe->GetPosition().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( m_canvas, DC, GR_XOR, g_Offset_Module ); | |
|         } | |
|         else | |
|         { | |
|             g_ColorsSettings.SetItemColor(RATSNEST_VISIBLE, tmpcolor); | |
| 
 | |
|             wxPoint tmp = rats->m_PadStart->GetPosition(); | |
| 
 | |
|             rats->m_PadStart->SetPosition( tmp - g_Offset_Module ); | |
|             rats->Draw( m_canvas, DC, GR_XOR, wxPoint( 0, 0 ) ); | |
| 
 | |
|             rats->m_PadStart->SetPosition( tmp ); | |
|         } | |
|     } | |
| 
 | |
|     g_ColorsSettings.SetItemColor( RATSNEST_VISIBLE, tmpcolor ); | |
| } | |
| 
 | |
| 
 | |
| /* | |
|  * PCB_BASE_FRAME::BuildAirWiresTargetsList and | |
|  * PCB_BASE_FRAME::TraceAirWiresToTargets | |
|  * are 2 function to show the near connecting points when | |
|  * a new track is created, by displaying g_MaxLinksShowed airwires | |
|  * between the on grid mouse cursor and these connecting points | |
|  * during the creation of a track | |
|  */ | |
| 
 | |
| /* 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_TargetsLocations; | |
| static wxPoint s_CursorPos; // Coordinate of the moving point (mouse cursor and | |
|                             // end of current track segment) | |
|  | |
| /* Used by BuildAirWiresTargetsList(): sort function by link length | |
|  * (rectilinear distance between s_CursorPos and item pos) | |
|  */ | |
| static bool sort_by_distance( const wxPoint& ref, const wxPoint& compare ) | |
| { | |
|     wxPoint deltaref = ref - s_CursorPos;       // relative coordinate of ref | |
|     wxPoint deltacmp = compare - s_CursorPos;   // relative coordinate of compare | |
|  | |
|     // rectilinear distance between ref and s_CursorPos: | |
|     int     lengthref = abs( deltaref.x ) + abs( deltaref.y ); | |
| 
 | |
|     // rectilinear distance between compare and s_CursorPos: | |
|     int     lengthcmp = abs( deltacmp.x ) + abs( deltacmp.y ); | |
| 
 | |
|     return lengthref < lengthcmp; | |
| } | |
| static bool sort_by_point( const wxPoint& ref, const wxPoint& compare ) | |
| { | |
|     if( ref.x == compare.x ) | |
|         return ref.y < compare.y; | |
| 
 | |
|     return ref.x < compare.x; | |
| } | |
| 
 | |
| /* Function BuildAirWiresTargetsList | |
|  * Build a list of candidates that can be a coonection point | |
|  * when a track is started. | |
|  * This functions prepares data to show airwires to nearest connecting points (pads) | |
|  * from the current new track to candidates during track creation | |
|  */ | |
| void PCB_BASE_FRAME::BuildAirWiresTargetsList( BOARD_CONNECTED_ITEM* aItemRef, | |
|                                                const wxPoint& aPosition, bool aInit ) | |
| { | |
|     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_TargetsLocations.clear(); | |
|         return; | |
|     } | |
| 
 | |
|     s_CursorPos = aPosition;    // needed for sort_by_distance | |
|  | |
|     if( aInit ) | |
|     { | |
|         s_TargetsLocations.clear(); | |
| 
 | |
|         if( aItemRef == NULL ) | |
|             return; | |
| 
 | |
|         int net_code = aItemRef->GetNet(); | |
|         int subnet = aItemRef->GetSubNet(); | |
| 
 | |
|         if( net_code <= 0 ) | |
|             return; | |
| 
 | |
|         NETINFO_ITEM* net = m_Pcb->FindNet( net_code ); | |
| 
 | |
|         if( net == NULL )        // Should not occur | |
|         { | |
|             wxMessageBox( wxT( "BuildAirWiresTargetsList() 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_PadInNetList.size(); ii++ ) | |
|         { | |
|             D_PAD* pad = net->m_PadInNetList[ii]; | |
| 
 | |
|             if( pad == aItemRef ) | |
|                 continue; | |
| 
 | |
|             if( !pad->GetSubNet() || (pad->GetSubNet() != subnet) ) | |
|                 s_TargetsLocations.push_back( pad->GetPosition() ); | |
|         } | |
| 
 | |
|         // Create a list of tracks ends candidates, not already connected to the | |
|         // current track: | |
|         for( TRACK* track = m_Pcb->m_Track; track; track = track->Next() ) | |
|         { | |
|             if( track->GetNet() < net_code ) | |
|                 continue; | |
|             if( track->GetNet() > net_code ) | |
|                 break;; | |
| 
 | |
|             if( !track->GetSubNet() || (track->GetSubNet() != subnet) ) | |
|             { | |
|                 if( aPosition != track->m_Start ) | |
|                     s_TargetsLocations.push_back( track->m_Start ); | |
|                 if( aPosition != track->m_End && track->m_Start != track->m_End ) | |
|                     s_TargetsLocations.push_back( track->m_End ); | |
|             } | |
|         } | |
| 
 | |
|         // Remove duplicate targets, using the C++ unique algorithm | |
|         sort( s_TargetsLocations.begin(), s_TargetsLocations.end(), sort_by_point ); | |
|         std::vector< wxPoint >::iterator it = unique( s_TargetsLocations.begin(), s_TargetsLocations.end() ); | |
| 
 | |
|         // Using the C++ unique algorithm only moves the duplicate entries to the end of | |
|         // of the array.  This removes the duplicate entries from the array. | |
|         s_TargetsLocations.resize( it - s_TargetsLocations.begin() ); | |
|     }   // end if Init | |
|  | |
|     // in all cases, sort by distances: | |
|     sort( s_TargetsLocations.begin(), s_TargetsLocations.end(), sort_by_distance ); | |
| } | |
| 
 | |
| 
 | |
| void PCB_BASE_FRAME::TraceAirWiresToTargets( wxDC* aDC ) | |
| { | |
|     if( aDC == NULL ) | |
|         return; | |
| 
 | |
|     if( s_TargetsLocations.size() == 0 ) | |
|         return; | |
| 
 | |
|     GRSetDrawMode( aDC, GR_XOR ); | |
| 
 | |
|     for( int ii = 0; ii < (int) s_TargetsLocations.size(); ii++ ) | |
|     { | |
|         if( ii >= g_MaxLinksShowed ) | |
|             break; | |
| 
 | |
|         GRLine( m_canvas->GetClipBox(), aDC, s_CursorPos, s_TargetsLocations[ii], 0, YELLOW ); | |
|     } | |
| }
 |