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)
This commit is contained in:
parent
ed2141a6d6
commit
936d831b82
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* @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 );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* @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" ) );
|
||||
|
||||
|
|
Loading…
Reference in New Issue