From fb2fc2f43f7d60c21faa9eb20f225baf64a8bc73 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 6 Nov 2011 17:16:19 +0100 Subject: [PATCH] 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) --- include/wxBasePcbFrame.h | 6 +- pcbnew/CMakeLists.txt | 1 + pcbnew/edit.cpp | 5 +- pcbnew/minumun_spanning_tree.cpp | 144 ++++++++ pcbnew/minumun_spanning_tree.h | 77 +++++ pcbnew/ratsnest.cpp | 542 ++++++++----------------------- pcbnew/tool_pcb.cpp | 3 +- 7 files changed, 369 insertions(+), 409 deletions(-) create mode 100644 pcbnew/minumun_spanning_tree.cpp create mode 100644 pcbnew/minumun_spanning_tree.h diff --git a/include/wxBasePcbFrame.h b/include/wxBasePcbFrame.h index 5ba9ea69e0..474f3a8a8d 100644 --- a/include/wxBasePcbFrame.h +++ b/include/wxBasePcbFrame.h @@ -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 diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 9814aeeba9..2a19ae4266 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -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 diff --git a/pcbnew/edit.cpp b/pcbnew/edit.cpp index 6825f27d27..5737629be6 100644 --- a/pcbnew/edit.cpp +++ b/pcbnew/edit.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; } diff --git a/pcbnew/minumun_spanning_tree.cpp b/pcbnew/minumun_spanning_tree.cpp new file mode 100644 index 0000000000..552655a977 --- /dev/null +++ b/pcbnew/minumun_spanning_tree.cpp @@ -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 + +#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 ); + } +} diff --git a/pcbnew/minumun_spanning_tree.h b/pcbnew/minumun_spanning_tree.h new file mode 100644 index 0000000000..73f5890e99 --- /dev/null +++ b/pcbnew/minumun_spanning_tree.h @@ -0,0 +1,77 @@ +/** + * @file minumun_spanning_tree.h + */ + +#include + +/** + * @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 inTree; /* inTree[ii] is a flag set to 1 if the node ii + * is already in the minimum spanning tree; 0 otherwise + */ + std::vector 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 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 ); + +}; diff --git a/pcbnew/ratsnest.cpp b/pcbnew/ratsnest.cpp index b8ba4c1312..697e03d538 100644 --- a/pcbnew/ratsnest.cpp +++ b/pcbnew/ratsnest.cpp @@ -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 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 * 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 * 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 &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 &aRatsnestList ) +{ + std::vector & 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 buffer to fill with - * new ratsnest items - * @param aPadBuffer = a std::vector that is the list of pads to consider - * @param aPadIdxStart = starting index (within the pad list) for search - * @param aPadIdxMax = ending index (within the pad list) for search - * @return blocks not connected count - */ -static int gen_rats_block_to_block -( - std::vector& aRatsnestBuffer, - std::vector& aPadBuffer, - unsigned aPadIdxStart, - unsigned aPadIdxMax ) -{ - int dist_min, current_dist; - int current_num_block = 1; - int padBlock1Idx = -1; // Index in aPadBuffer for the "better" pad - // found in block 1 - int padBlockToMergeIdx = -1; // Index in aPadBuffer for the "better" pad - // found in block to merge - - dist_min = 0x7FFFFFFF; - - /* Search the nearest pad from block 1 */ - for( unsigned ii = aPadIdxStart; ii < aPadIdxMax; ii++ ) - { - D_PAD* ref_pad = aPadBuffer[ii]; - - /* search a pad which is in the block 1 */ - if( ref_pad->GetSubRatsnest() != 1 ) - continue; - - /* pad is found, search its nearest neighbor in other blocks */ - for( unsigned jj = aPadIdxStart; jj < aPadIdxMax; jj++ ) - { - D_PAD* curr_pad = aPadBuffer[jj]; - - if( curr_pad->GetSubRatsnest() == 1 ) // not in an other block - continue; - - /* Compare distance between pads ("Manhattan" distance) */ - current_dist = abs( curr_pad->m_Pos.x - ref_pad->m_Pos.x ) + - abs( curr_pad->m_Pos.y - ref_pad->m_Pos.y ); - - if( dist_min > current_dist ) // we have found a better pad pair - { - // The tested block can be a good candidate for merging - // we memorize the "best" current values for merging - current_num_block = curr_pad->GetSubRatsnest(); - dist_min = current_dist; - - padBlockToMergeIdx = jj; - padBlock1Idx = ii; - } - } - } - - /* The reference block is labeled block 1. - * if current_num_block != 1 we have found an other block, and we must - * merge it with the reference block - * The link is made by the 2 nearest pads - */ - if( current_num_block > 1 ) - { - /* The block n (n=current_num_block) is merged with the bloc 1 : - * to do that, we set the m_SubRatsnest member to 1 for all pads in - * block n - */ - for( unsigned ii = aPadIdxStart; ii < aPadIdxMax; ii++ ) - { - D_PAD* pad = aPadBuffer[ii]; - if( pad->GetSubRatsnest() == current_num_block ) - pad->SetSubRatsnest( 1 ); - } - - if( padBlock1Idx < 0 ) - { - DisplayError( NULL, - wxT( "gen_rats_block_to_block() internal error" ) ); - } - else - { - /* Create the new ratsnest */ - RATSNEST_ITEM net; - net.SetNet( aPadBuffer[padBlock1Idx]->GetNet() ); - net.m_Status = CH_ACTIF | CH_VISIBLE; - net.m_Lenght = dist_min; - net.m_PadStart = aPadBuffer[padBlock1Idx]; - net.m_PadEnd = aPadBuffer[padBlockToMergeIdx]; - aRatsnestBuffer.push_back( net ); - } - } - - return current_num_block; -} - - -/** - * Function used by Build_Board_Ratsnest - * this is the first pass of the lee algorithm - * This function creates the link (ratsnest) between 2 pads ( fitting the same - * net ) - * the function search for a first not connected pad and search its nearest - * neighbor - * Its creates a block if the 2 pads are not connected, or merge the - * unconnected pad to the existing block. - * These blocks include 2 pads and the 2 pads are linked by a ratsnest. - * - * @param aRatsnestBuffer = a std::vector buffer to fill with - * new ratsnest items - * @param aPadBuffer = a std::vector that is the list of pads to - * consider - * @param aPadIdxStart = starting index (within the pad list) for search - * @param aPadIdxMax = ending index (within the pad list) for search - * @param current_num_block = Last existing block number of pads - * These block are created by the existing tracks analysis - * - * @return the last block number used - */ -static int gen_rats_pad_to_pad( vector& aRatsnestBuffer, - std::vector& aPadBuffer, - unsigned aPadIdxStart, - unsigned aPadIdxMax, - int current_num_block ) -{ - int dist_min, current_dist; - D_PAD* ref_pad, * pad; - - for( unsigned ii = aPadIdxStart; ii < aPadIdxMax; ii++ ) - { - ref_pad = aPadBuffer[ii]; - - if( ref_pad->GetSubRatsnest() ) - continue; // Pad already connected - - dist_min = 0x7FFFFFFF; - int padBlockToMergeIdx = -1; // Index in aPadBuffer for the "better" - // pad found in block to merge - for( unsigned jj = aPadIdxStart; jj < aPadIdxMax; jj++ ) - { - if( ii == jj ) - continue; - - pad = aPadBuffer[jj]; - - /* Compare distance between pads ("Manhattan" distance) */ - current_dist = abs( pad->m_Pos.x - ref_pad->m_Pos.x ) + - abs( pad->m_Pos.y - ref_pad->m_Pos.y ); - - if( dist_min > current_dist ) - { - dist_min = current_dist; - padBlockToMergeIdx = jj; - } - } - - if( padBlockToMergeIdx >= 0 ) - { - pad = aPadBuffer[padBlockToMergeIdx]; - - /* Update the block number - * if the 2 pads are not already created : a new block is created - */ - if( (pad->GetSubRatsnest() == 0) && (ref_pad->GetSubRatsnest() == 0) ) - { - current_num_block++; // Creates a new block number (or subratsnest) - pad->SetSubRatsnest( current_num_block ); - ref_pad->SetSubRatsnest( current_num_block ); - } - /* If a pad is already connected connected : merge the other pad in - * the block */ - else - { - ref_pad->SetSubRatsnest( pad->GetSubRatsnest() ); - } - - /* Create the new ratsnest item */ - RATSNEST_ITEM rast; - rast.SetNet( ref_pad->GetNet() ); - rast.m_Status = CH_ACTIF | CH_VISIBLE; - rast.m_Lenght = dist_min; - rast.m_PadStart = ref_pad; - rast.m_PadEnd = pad; - aRatsnestBuffer.push_back( rast ); - } - } - - return current_num_block; -} - - -/** - * Function to compute the full ratsnest (using the LEE algorithm ) + * 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 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; } diff --git a/pcbnew/tool_pcb.cpp b/pcbnew/tool_pcb.cpp index a38a7411bc..dfec27be5e 100644 --- a/pcbnew/tool_pcb.cpp +++ b/pcbnew/tool_pcb.cpp @@ -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" ) );