Commit 936d831b authored by jean-pierre charras's avatar jean-pierre charras

Pcbnew: more work on a better algorithm to calculate the full ratsnest.

Lee algorithm replaced by a Minimun Spanning Tree algo (using Prim's algorithm ).
Tested on a 5350 pads board, it is more than 100 times faster.
Also fix a crash when clicking on tool  "delete selected items" (main horizontal toolbar),
and remove this tool (currently, no code in Pcbnew for this tool)
parent ed2141a6
......@@ -352,7 +352,11 @@ public:
*/
void TraceModuleRatsNest( wxDC* aDC );
void Build_Board_Ratsnest( wxDC* DC );
/**
* Function Build_Board_Ratsnest.
* Calculates the full ratsnest depending only on pads.
*/
void Build_Board_Ratsnest();
/**
* function Displays the general ratsnest
......
......@@ -132,6 +132,7 @@ set(PCBNEW_SRCS
magnetic_tracks_functions.cpp
menubar_modedit.cpp
menubar_pcbframe.cpp
minumun_spanning_tree.cpp
mirepcb.cpp
modedit.cpp
modedit_onclick.cpp
......
......@@ -66,7 +66,6 @@ void PCB_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
int itmp;
INSTALL_UNBUFFERED_DC( dc, DrawPanel );
BOARD_ITEM* DrawStruct = GetCurItem();
MODULE* module;
DrawPanel->CrossHairOff( &dc );
......@@ -1080,8 +1079,8 @@ void PCB_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
default:
wxString msg;
msg.Printf( wxT( "PCB_EDIT_FRAME::Process_Special_Functions() id %d error" ),
DrawStruct->Type() );
msg.Printf( wxT( "PCB_EDIT_FRAME::Process_Special_Functions() unknown event id %d" ),
id );
DisplayError( this, msg );
break;
}
......
/**
* @file minumun_spanning_tree.cpp
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2011 Jean-Pierre Charras
* Copyright (C) 2004-2011 KiCad Developers, see change_log.txt for contributors.
*
* derived from this article:
* http://compprog.wordpress.com/2007/11/09/minimal-spanning-trees-prims-algorithm
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <limits.h>
#include "minumun_spanning_tree.h"
#include "class_pad.h"
/*
* The class MIN_SPAN_TREE calculates the rectilinear minimum spanning tree
* of a set of points (pads usually having the same net)
* using the Prim's algorithm.
*/
/*
* Prim's Algorithm
* Step 0
* Pick any vertex as a starting vertex. (Call it S).
* Mark it with any given flag, say 1.
*
* Step 1
* Find the nearest neighbour of S (call it P1).
* Mark both P1 and the edge SP1.
* cheapest unmarked edge in the graph that doesn't close a marked circuit.
* Mark this edge.
*
* Step 2
* Find the nearest unmarked neighbour to the marked subgraph
* (i.e., the closest vertex to any marked vertex).
* Mark it and the edge connecting the vertex.
*
* Step 3
* Repeat Step 2 until all vertices are marked.
* The marked subgraph is a minimum spanning tree.
*/
MIN_SPAN_TREE::MIN_SPAN_TREE()
{
MSP_Init( 0 );
}
void MIN_SPAN_TREE::MSP_Init( int aNodesCount )
{
m_Size = aNodesCount;
inTree.clear();
linkedTo.clear();
distTo.clear();
if( m_Size == 0 )
return;
// Reserve space in memory
inTree.reserve( m_Size );
linkedTo.reserve( m_Size );
distTo.reserve( m_Size );
// Initialize values:
for( int ii = 0; ii < m_Size; ii++ )
{
// Initialise dist with infinity:
distTo.push_back( INT_MAX );
// Mark all nodes as NOT beeing in the minimum spanning tree:
inTree.push_back( 0 );
linkedTo.push_back( 0 );
}
}
/* updateDistances(int target)
* should be called immediately after target is added to the tree;
* updates dist so that the values are correct (goes through target's
* neighbours making sure that the distances between them and the tree
* are indeed minimum)
*/
void MIN_SPAN_TREE::updateDistances( int target )
{
for( int ii = 0; ii < m_Size; ++ii )
{
if( !inTree[ii] ) // no need to evaluate weight for already in tree items
{
int weight = GetWeight( target, ii );
if( (weight > 0) && (distTo[ii] > weight ) )
{
distTo[ii] = weight;
linkedTo[ii] = target;
}
}
}
}
void MIN_SPAN_TREE::BuildTree()
{
/* Add the first node to the tree */
inTree[0] = 1;
updateDistances( 0 );
for( int treeSize = 1; treeSize < m_Size; ++treeSize )
{
// Find the node with the smallest distance to the tree
int min = -1;
for( int ii = 0; ii < m_Size; ++ii )
{
if( !inTree[ii] )
{
if( (min == -1) || (distTo[min] > distTo[ii]) )
min = ii;
}
}
inTree[min] = 1;
updateDistances( min );
}
}
/**
* @file minumun_spanning_tree.h
*/
#include <vector>
/**
* @brief The class MIN_SPAN_TREE calculates the rectilinear minimum spanning tree
* of a set of points (pads usually having the same net)
* this class is an abstract class because you must provide the function
* int GetWeight( int aItem1, int aItem2 )
* that calculate the distance between 2 items
* MIN_SPAN_TREE does not know anything about the actual items to link
* by the tree
*/
class MIN_SPAN_TREE
{
protected:
int m_Size; /* The number of nodes in the graph
*/
private:
std::vector<char> inTree; /* inTree[ii] is a flag set to 1 if the node ii
* is already in the minimum spanning tree; 0 otherwise
*/
std::vector<int> linkedTo; /* linkedTo[ii] holds the index of the node ii would have to be
* linked to in order to get a distance of d[ii]
* NOTE: linkedTo[0] is the starting point of the tree
* linkedTo[1] is the first linked point to use
* ii and linkedTo[ii] are the 2 ends of an edge in the graph
*/
std::vector<int> distTo; /* distTo[ii] is the distance between node ii and the minimum spanning
* tree;
* this is initially infinity (INT_MAX);
* if ii is already in the tree, then d[ii] is undefined;
* this is just a temporary variable. It's not necessary but speeds
* up execution considerably (by a factor of n)
*/
public:
MIN_SPAN_TREE();
void MSP_Init( int aNodesCount );
void BuildTree();
int GetWhoTo( int aIdx )
{
return linkedTo[aIdx];
}
int GetDist( int aIdx )
{
return distTo[aIdx];
}
/**
* Function GetWeight
* calculates the weight between 2 items
* NOTE: The weight between a node and itself should be 0
* It is virtual pure, you must provide your GetWeight function
* @param aItem1 = first item
* @param aItem2 = other item
* @return the weight between items ( usually the distance )
*/
virtual int GetWeight( int aItem1, int aItem2 ) = 0;
private:
/**
* Function updateDistances
* should be called immediately after target is added to the tree;
* updates d so that the values are correct (goes through target's
* neighbours making sure that the distances between them and the tree
* are indeed minimum)
* @param target = index of curr item
*/
void updateDistances( int aTarget );
};
......@@ -7,7 +7,6 @@
#include "gr_basic.h"
#include "common.h"
#include "class_drawpanel.h"
#include "confirm.h"
#include "colors_selection.h"
#include "wxBasePcbFrame.h"
#include "macros.h"
......@@ -17,96 +16,116 @@
#include "class_track.h"
#include "pcbnew.h"
#include "protos.h"
#include "minumun_spanning_tree.h"
static std::vector <D_PAD*> 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
/**
* @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:
* 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() )
* 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
*/
/**
......@@ -121,8 +140,6 @@ 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();
......@@ -140,18 +157,16 @@ void PCB_BASE_FRAME::Compile_Ratsnest( wxDC* aDC, bool aDisplayStatus )
/* 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)
* (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( aDC );
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
* it is faster than Build_Board_Ratsnest()
* because many optimizations and computations are already made
*/
TestRatsNest( aDC, 0 );
......@@ -167,256 +182,29 @@ void PCB_BASE_FRAME::Compile_Ratsnest( wxDC* aDC, bool aDisplayStatus )
/* Sort function used by QSORT
* Sort pads by net code
*/
static int sortByNetcode( const void* o1, const void* o2 )
static bool sortByNetcode( const D_PAD* const & ref, const D_PAD* const & item )
{
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;
return ref->GetNet() < item->GetNet();
}
/**
* 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<RATSNEST_ITEM> buffer to fill with
* new ratsnest items
* @param aPadBuffer = a std::vector<D_PAD*> 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<RATSNEST_ITEM>& aRatsnestBuffer,
std::vector<D_PAD*>& 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<RATSNEST_ITEM> buffer to fill with
* new ratsnest items
* @param aPadBuffer = a std::vector<D_PAD*> 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<RATSNEST_ITEM>& aRatsnestBuffer,
std::vector<D_PAD*>& 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 )
* Function to compute the full ratsnest
* 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.
* 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 for an connection which have n active pads) .
* (there are n-1 links for a connection which have n active pads) .
*
*/
void PCB_BASE_FRAME::Build_Board_Ratsnest( wxDC* DC )
void PCB_BASE_FRAME::Build_Board_Ratsnest()
{
D_PAD* pad;
int noconn;
......@@ -442,80 +230,36 @@ void PCB_BASE_FRAME::Build_Board_Ratsnest( wxDC* DC )
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;
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
{
DisplayError( this,
wxT( "Build_Board_Ratsnest() error: net not found" ) );
wxMessageBox( 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 );
}
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();
/* 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
// 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;
if( DC )
m_Pcb->m_FullRatsnest[ii].Draw( DrawPanel, DC, GR_XOR, wxPoint( 0, 0 ) );
}
}
......@@ -557,12 +301,10 @@ void PCB_BASE_FRAME::DrawGeneralRatsnest( wxDC* aDC, int aNetcode )
/**
* 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 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
......@@ -625,7 +367,7 @@ static int tst_rats_block_to_block( NETINFO_ITEM* net,
* 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
* It tests only the existing ratsnest and activate some ratsnest (status bit
* CH_ACTIF set)
*
* @param start_rat_list = starting address for the ratsnest list
......@@ -688,7 +430,7 @@ void PCB_BASE_FRAME::TestRatsNest( wxDC* aDC, int aNetCode )
return;
if( (m_Pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
Build_Board_Ratsnest( aDC );
Build_Board_Ratsnest();
for( int net_code = 1; net_code < (int) m_Pcb->m_NetInfo->GetCount(); net_code++ )
{
......@@ -740,7 +482,6 @@ void PCB_BASE_FRAME::TestRatsNest( wxDC* aDC, int aNetCode )
int PCB_BASE_FRAME::TestOneRatsNest( wxDC* aDC, int aNetCode )
{
DisplayRastnestInProgress = false;
DrawGeneralRatsnest( aDC, aNetCode );
TestRatsNest( aDC, aNetCode );
DrawGeneralRatsnest( aDC, aNetCode );
......@@ -755,7 +496,6 @@ void PCB_BASE_FRAME::build_ratsnest_module( MODULE* aModule )
// 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;
......@@ -778,7 +518,8 @@ void PCB_BASE_FRAME::build_ratsnest_module( MODULE* aModule )
goto CalculateExternalRatsnest;
/* Compute the "internal" ratsnest, i.e the links between the current
*footprint pads */
* footprint pads
*/
s_localPadBuffer.clear();
m_Pcb->m_LocalRatsnest.clear();
......@@ -797,11 +538,9 @@ void PCB_BASE_FRAME::build_ratsnest_module( MODULE* aModule )
if( pads_module_count == 0 )
return; /* no connection! */
qsort( &s_localPadBuffer[0], pads_module_count, sizeof( D_PAD* ), sortByNetcode );
sort( s_localPadBuffer.begin(), s_localPadBuffer.end(), 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++ )
......@@ -816,8 +555,7 @@ void PCB_BASE_FRAME::build_ratsnest_module( MODULE* aModule )
if( net == NULL ) //Should not occur
{
DisplayError( this,
wxT( "build_ratsnest_module() error: net not found" ) );
wxMessageBox( wxT( "build_ratsnest_module() error: net not found" ) );
return;
}
......@@ -836,10 +574,8 @@ void PCB_BASE_FRAME::build_ratsnest_module( MODULE* aModule )
}
/* 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 );
sort( s_localPadBuffer.begin() + pads_module_count, s_localPadBuffer.end(),
sortByNetcode );
/* Compute the internal rats nest:
* this is the same as general ratsnest, but considers only the current
......@@ -848,6 +584,9 @@ void PCB_BASE_FRAME::build_ratsnest_module( MODULE* aModule )
*/
current_net_code = s_localPadBuffer[0]->GetNet();
{
MIN_SPAN_TREE_PADS min_spanning_tree;
std::vector<D_PAD*> padsBuffer;
for( unsigned ii = 0; ii < pads_module_count; ii++ )
{
/* Search the end of pad list relative to the current net */
......@@ -862,22 +601,17 @@ void PCB_BASE_FRAME::build_ratsnest_module( MODULE* aModule )
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 );
}
for(unsigned kk = ii; kk < jj; kk++ )
padsBuffer.push_back( s_localPadBuffer[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 < s_localPadBuffer.size() )
current_net_code = s_localPadBuffer[ii]->GetNet();
}
}
internalRatsCount = m_Pcb->m_LocalRatsnest.size();
......@@ -1088,7 +822,7 @@ void PCB_BASE_FRAME::build_ratsnest_pad( BOARD_ITEM* ref, const wxPoint& refpos,
if( net == NULL ) // Should not occur
{
DisplayError( this, wxT( "build_ratsnest_pad() error: net not found" ) );
wxMessageBox( wxT( "build_ratsnest_pad() error: net not found" ) );
return;
}
......
......@@ -204,11 +204,12 @@ void PCB_EDIT_FRAME::ReCreateHToolbar()
m_HToolBar->AddTool( ID_OPEN_MODULE_EDITOR, wxEmptyString, KiBitmap( modedit_xpm ),
_( "Open module editor" ) );
#if 0
// Not yet existing commands
m_HToolBar->AddSeparator();
m_HToolBar->AddTool( wxID_CUT, wxEmptyString, KiBitmap( cut_button_xpm ),
_( "Cut selected item" ) );
#if 0
m_HToolBar->AddTool( wxID_COPY, wxEmptyString, KiBitmap( copy_button_xpm ),
_( "Copy selected item" ) );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment