summaryrefslogtreecommitdiff
path: root/pcbnew/connect.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pcbnew/connect.cpp')
-rw-r--r--pcbnew/connect.cpp1003
1 files changed, 1003 insertions, 0 deletions
diff --git a/pcbnew/connect.cpp b/pcbnew/connect.cpp
new file mode 100644
index 0000000..848c30b
--- /dev/null
+++ b/pcbnew/connect.cpp
@@ -0,0 +1,1003 @@
+/**
+ * @file connect.cpp
+ * @brief Functions to handle existing tracks in ratsnest calculations.
+ */
+
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
+ * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
+ * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * 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 <fctsys.h>
+#include <common.h>
+#include <macros.h>
+#include <wxBasePcbFrame.h>
+
+#include <pcbnew.h>
+
+// Helper classes to handle connection points
+#include <connect.h>
+
+extern void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb );
+extern void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode );
+
+// Local functions
+static void RebuildTrackChain( BOARD* pcb );
+
+
+CONNECTIONS::CONNECTIONS( BOARD * aBrd )
+{
+ m_brd = aBrd;
+ m_firstTrack = NULL;
+ m_lastTrack = NULL;
+}
+
+
+/* Fills m_sortedPads with all pads that be connected to tracks
+ * pads are sorted by X coordinate ( and Y coordinates for same X value )
+ * aNetcode = net code to filter pads or < 0 to put all pads in list
+ */
+void CONNECTIONS::BuildPadsList( int aNetcode )
+{
+ // Creates sorted pad list if not exists
+ m_sortedPads.clear();
+ m_brd->GetSortedPadListByXthenYCoord( m_sortedPads, aNetcode < 0 ? -1 : aNetcode );
+}
+
+/* Explores the list of pads and adds to m_PadsConnected member
+ * of each pad pads connected to
+ * Here, connections are due to intersecting pads, not tracks
+ */
+void CONNECTIONS::SearchConnectionsPadsToIntersectingPads()
+{
+ std::vector<CONNECTED_POINT*> candidates;
+
+ BuildPadsCandidatesList();
+
+ for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
+ {
+ D_PAD* pad = m_sortedPads[ii];
+
+ pad->m_PadsConnected.clear();
+ candidates.clear();
+
+ CollectItemsNearTo( candidates, pad->ShapePos(), pad->GetBoundingRadius() );
+
+ // add pads to pad.m_PadsConnected, if they are connected
+ for( unsigned jj = 0; jj < candidates.size(); jj++ )
+ {
+ CONNECTED_POINT* item = candidates[jj];
+
+ D_PAD* candidate_pad = item->GetPad();
+
+ if( pad == candidate_pad )
+ continue;
+
+ if( !( pad->GetLayerSet() & candidate_pad->GetLayerSet() ).any() )
+ continue;
+ if( pad->HitTest( item->GetPoint() ) )
+ {
+ pad->m_PadsConnected.push_back( candidate_pad );
+ }
+ }
+ }
+}
+
+/* Explores the list of pads
+ * Adds to m_PadsConnected member of each track the pad(s) connected to
+ * Adds to m_TracksConnected member of each pad the track(s) connected to
+ * D_PAD::m_TracksConnected is cleared before adding items
+ * TRACK::m_PadsConnected is not cleared
+ */
+void CONNECTIONS::SearchTracksConnectedToPads( bool add_to_padlist, bool add_to_tracklist)
+{
+ std::vector<CONNECTED_POINT*> candidates;
+
+ for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
+ {
+ D_PAD * pad = m_sortedPads[ii];
+ pad->m_TracksConnected.clear();
+ candidates.clear();
+
+ CollectItemsNearTo( candidates, pad->GetPosition(), pad->GetBoundingRadius() );
+
+ // add this pad to track.m_PadsConnected, if it is connected
+ for( unsigned jj = 0; jj < candidates.size(); jj++ )
+ {
+ CONNECTED_POINT* cp_item = candidates[jj];
+
+ if( !( pad->GetLayerSet() & cp_item->GetTrack()->GetLayerSet() ).any() )
+ continue;
+
+ if( pad->HitTest( cp_item->GetPoint() ) )
+ {
+ if( add_to_padlist )
+ cp_item->GetTrack()->m_PadsConnected.push_back( pad );
+
+ if( add_to_tracklist )
+ pad->m_TracksConnected.push_back( cp_item->GetTrack() );
+ }
+ }
+ }
+}
+
+void CONNECTIONS::CollectItemsNearTo( std::vector<CONNECTED_POINT*>& aList,
+ const wxPoint& aPosition, int aDistMax )
+{
+ /* Search items in m_Candidates that position is <= aDistMax from aPosition
+ * (Rectilinear distance)
+ * m_Candidates is sorted by X then Y values, so a fast binary search is used
+ * to locate the "best" entry point in list
+ * The best entry is a pad having its m_Pos.x == (or near) aPosition.x
+ * All candidates are near this candidate in list
+ * So from this entry point, a linear search is made to find all candidates
+ */
+ int idxmax = m_candidates.size()-1;
+
+ int delta = m_candidates.size();
+
+ int idx = 0; // Starting index is the beginning of list
+ while( delta )
+ {
+ // Calculate half size of remaining interval to test.
+ // Ensure the computed value is not truncated (too small)
+ if( (delta & 1) && ( delta > 1 ) )
+ delta++;
+ delta /= 2;
+
+ CONNECTED_POINT& item = m_candidates[idx];
+
+ int dist = item.GetPoint().x - aPosition.x;
+ if( abs(dist) <= aDistMax )
+ {
+ break; // A good entry point is found. The list can be scanned from this point.
+ }
+
+ else if( item.GetPoint().x < aPosition.x ) // We should search after this item
+ {
+ idx += delta;
+ if( idx > idxmax )
+ idx = idxmax;
+ }
+ else // We should search before this item
+ {
+ idx -= delta;
+ if( idx < 0 )
+ idx = 0;
+ }
+ }
+
+ /* Now explore the candidate list from the "best" entry point found
+ * (candidate "near" aPosition.x)
+ * We explore the list until abs(candidate->m_Point.x - aPosition.x) > aDistMax
+ * because the list is sorted by X position (and for a given X pos, by Y pos)
+ * Currently a linear search is made because the number of candidates
+ * having the right X position is usually small
+ */
+ // search next candidates in list
+ wxPoint diff;
+ for( int ii = idx; ii <= idxmax; ii++ )
+ {
+ CONNECTED_POINT* item = &m_candidates[ii];
+ diff = item->GetPoint() - aPosition;
+ if( abs(diff.x) > aDistMax )
+ break; // Exit: the distance is to long, we cannot find other candidates
+ if( abs(diff.y) > aDistMax )
+ continue; // the y distance is to long, but we can find other candidates
+ // We have here a good candidate: add it
+ aList.push_back( item );
+ }
+ // search previous candidates in list
+ for( int ii = idx-1; ii >=0; ii-- )
+ {
+ CONNECTED_POINT * item = &m_candidates[ii];
+ diff = item->GetPoint() - aPosition;
+ if( abs(diff.x) > aDistMax )
+ break;
+ if( abs(diff.y) > aDistMax )
+ continue;
+ // We have here a good candidate:add it
+ aList.push_back( item );
+ }
+}
+
+
+void CONNECTIONS::BuildPadsCandidatesList()
+{
+ m_candidates.clear();
+ m_candidates.reserve( m_sortedPads.size() );
+ for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
+ {
+ D_PAD * pad = m_sortedPads[ii];
+ CONNECTED_POINT candidate( pad, pad->GetPosition() );
+ m_candidates.push_back( candidate );
+ }
+}
+
+/* sort function used to sort .m_Connected by X the Y values
+ * items are sorted by X coordinate value,
+ * and for same X value, by Y coordinate value.
+ */
+static bool sortConnectedPointByXthenYCoordinates( const CONNECTED_POINT & aRef,
+ const CONNECTED_POINT & aTst )
+{
+ if( aRef.GetPoint().x == aTst.GetPoint().x )
+ return aRef.GetPoint().y < aTst.GetPoint().y;
+ return aRef.GetPoint().x < aTst.GetPoint().x;
+}
+
+void CONNECTIONS::BuildTracksCandidatesList( TRACK* aBegin, TRACK* aEnd)
+{
+ m_candidates.clear();
+ m_firstTrack = m_lastTrack = aBegin;
+
+ unsigned ii = 0;
+
+ // Count candidates ( i.e. end points )
+ for( const TRACK* track = aBegin; track; track = track->Next() )
+ {
+ if( track->Type() == PCB_VIA_T )
+ ii++;
+ else
+ ii += 2;
+
+ m_lastTrack = track;
+
+ if( track == aEnd )
+ break;
+ }
+
+ // Build candidate list
+ m_candidates.reserve( ii );
+ for( TRACK* track = aBegin; track; track = track->Next() )
+ {
+ CONNECTED_POINT candidate( track, track->GetStart() );
+
+ m_candidates.push_back( candidate );
+ if( track->Type() != PCB_VIA_T )
+ {
+ CONNECTED_POINT candidate2( track, track->GetEnd());
+ m_candidates.push_back( candidate2 );
+ }
+
+ if( track == aEnd )
+ break;
+ }
+
+ // Sort list by increasing X coordinate,
+ // and for increasing Y coordinate when items have the same X coordinate
+ // So candidates to the same location are consecutive in list.
+ sort( m_candidates.begin(), m_candidates.end(), sortConnectedPointByXthenYCoordinates );
+}
+
+
+/* Populates .m_connected with tracks/vias connected to aTrack
+ * param aTrack = track or via to use as reference
+ * For calculation time reason, an exhaustive search cannot be made
+ * and a proximity search is made:
+ * Only tracks with one end near one end of aTrack are collected.
+ * near means dist <= aTrack width / 2
+ * because with this constraint we can make a fast search in track list
+ * m_candidates is expected to be populated by the track candidates ends list
+ */
+int CONNECTIONS::SearchConnectedTracks( const TRACK* aTrack )
+{
+ int count = 0;
+ m_connected.clear();
+
+ LSET layerMask = aTrack->GetLayerSet();
+
+ // Search for connections to starting point:
+#define USE_EXTENDED_SEARCH
+
+#ifdef USE_EXTENDED_SEARCH
+ int dist_max = aTrack->GetWidth() / 2;
+ static std::vector<CONNECTED_POINT*> tracks_candidates;
+#endif
+
+ wxPoint position = aTrack->GetStart();
+
+ for( int kk = 0; kk < 2; kk++ )
+ {
+#ifndef USE_EXTENDED_SEARCH
+ int idx = searchEntryPointInCandidatesList( position );
+
+ if( idx >= 0 )
+ {
+ // search after:
+ for( unsigned ii = idx; ii < m_candidates.size(); ii ++ )
+ {
+ if( m_candidates[ii].GetTrack() == aTrack )
+ continue;
+
+ if( m_candidates[ii].GetPoint() != position )
+ break;
+
+ if( ( m_candidates[ii].GetTrack()->GetLayerSet() & layerMask ).any() )
+ m_connected.push_back( m_candidates[ii].GetTrack() );
+ }
+
+ // search before:
+ for( int ii = idx-1; ii >= 0; ii -- )
+ {
+ if( m_candidates[ii].GetTrack() == aTrack )
+ continue;
+
+ if( m_candidates[ii].GetPoint() != position )
+ break;
+
+ if( ( m_candidates[ii].GetTrack()->GetLayerSet() & layerMask ).any() )
+ m_connected.push_back( m_candidates[ii].GetTrack() );
+ }
+ }
+#else
+
+ tracks_candidates.clear();
+
+ CollectItemsNearTo( tracks_candidates, position, dist_max );
+
+ for( unsigned ii = 0; ii < tracks_candidates.size(); ii++ )
+ {
+ TRACK* ctrack = tracks_candidates[ii]->GetTrack();
+
+ if( !( ctrack->GetLayerSet() & layerMask ).any() )
+ continue;
+
+ if( ctrack == aTrack )
+ continue;
+
+ // We have a good candidate: calculate the actual distance
+ // between ends, which should be <= dist max.
+ wxPoint delta = tracks_candidates[ii]->GetPoint() - position;
+
+ int dist = KiROUND( EuclideanNorm( delta ) );
+
+ if( dist > dist_max )
+ continue;
+
+ m_connected.push_back( ctrack );
+ }
+#endif
+
+ // Search for connections to ending point:
+ if( aTrack->Type() == PCB_VIA_T )
+ break;
+
+ position = aTrack->GetEnd();
+ }
+
+ return count;
+}
+
+
+int CONNECTIONS::searchEntryPointInCandidatesList( const wxPoint& aPoint )
+{
+ // Search the aPoint coordinates in m_Candidates
+ // m_Candidates is sorted by X then Y values, and a fast binary search is used
+ int idxmax = m_candidates.size()-1;
+
+ int delta = m_candidates.size();
+
+ int idx = 0; // Starting index is the beginning of list
+
+ while( delta )
+ {
+ // Calculate half size of remaining interval to test.
+ // Ensure the computed value is not truncated (too small)
+ if( ( delta & 1 ) && ( delta > 1 ) )
+ delta++;
+
+ delta /= 2;
+
+ CONNECTED_POINT& candidate = m_candidates[idx];
+
+ if( candidate.GetPoint() == aPoint ) // candidate found
+ {
+ return idx;
+ }
+
+ // Not found: test the middle of the remaining sub list
+ if( candidate.GetPoint().x == aPoint.x ) // Must search considering Y coordinate
+ {
+ if(candidate.GetPoint().y < aPoint.y) // Must search after this item
+ {
+ idx += delta;
+ if( idx > idxmax )
+ idx = idxmax;
+ }
+ else // Must search before this item
+ {
+ idx -= delta;
+ if( idx < 0 )
+ idx = 0;
+ }
+ }
+ else if( candidate.GetPoint().x < aPoint.x ) // Must search after this item
+ {
+ idx += delta;
+ if( idx > idxmax )
+ idx = idxmax;
+ }
+ else // Must search before this item
+ {
+ idx -= delta;
+ if( idx < 0 )
+ idx = 0;
+ }
+ }
+
+ return -1;
+}
+
+/* Used after a track change (delete a track ou add a track)
+ * Connections to pads are recalculated
+ * Note also aFirstTrack (and aLastTrack ) can be NULL
+ */
+void CONNECTIONS::Build_CurrNet_SubNets_Connections( TRACK* aFirstTrack, TRACK* aLastTrack, int aNetcode )
+{
+ m_firstTrack = aFirstTrack; // The first track used to build m_Candidates
+ m_lastTrack = aLastTrack; // The last track used to build m_Candidates
+
+ // Pads subnets are expected already cleared, because this function
+ // does not know the full list of pads
+ BuildTracksCandidatesList( aFirstTrack, aLastTrack );
+ TRACK* curr_track;
+ for( curr_track = aFirstTrack; curr_track != NULL; curr_track = curr_track->Next() )
+ {
+ // Clear track subnet id (Pads subnets are cleared outside this function)
+ curr_track->SetSubNet( 0 );
+ curr_track->m_TracksConnected.clear();
+ curr_track->m_PadsConnected.clear();
+
+ // Update connections between tracks:
+ SearchConnectedTracks( curr_track );
+ curr_track->m_TracksConnected = m_connected;
+
+ if( curr_track == aLastTrack )
+ break;
+ }
+
+ // Update connections between tracks and pads
+ BuildPadsList( aNetcode );
+ SearchTracksConnectedToPads();
+
+ // Update connections between intersecting pads (no tracks)
+ SearchConnectionsPadsToIntersectingPads();
+
+ // Creates sub nets (clusters) for the current net:
+ Propagate_SubNets();
+}
+
+
+/**
+ * Change a subnet value to a new value, in m_sortedPads pad list
+ * After that, 2 cluster (or subnets) are merged into only one.
+ * Note: the resulting subnet value is the smallest between aOldSubNet et aNewSubNet
+ */
+int CONNECTIONS::Merge_PadsSubNets( int aOldSubNet, int aNewSubNet )
+{
+ int change_count = 0;
+
+ if( aOldSubNet == aNewSubNet )
+ return 0;
+
+ if( (aOldSubNet > 0) && (aOldSubNet < aNewSubNet) )
+ std::swap( aOldSubNet, aNewSubNet );
+
+ // Examine connections between intersecting pads
+ for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
+ {
+ D_PAD * curr_pad = m_sortedPads[ii];
+ if( curr_pad->GetSubNet() != aOldSubNet )
+ continue;
+
+ change_count++;
+ curr_pad->SetSubNet( aNewSubNet );
+ }
+
+ return change_count;
+}
+
+/*
+ * Change a subnet value to a new value, for tracks and pads which are connected to.
+ * The result is merging 2 clusters (or subnets) into only one cluster.
+ * Note: the resulting sub net value is the smallest between aOldSubNet et aNewSubNet
+ */
+int CONNECTIONS::Merge_SubNets( int aOldSubNet, int aNewSubNet )
+{
+ TRACK* curr_track;
+ int change_count = 0;
+
+ if( aOldSubNet == aNewSubNet )
+ return 0;
+
+ if( (aOldSubNet > 0) && (aOldSubNet < aNewSubNet) )
+ std::swap( aOldSubNet, aNewSubNet );
+
+ curr_track = (TRACK*)m_firstTrack;
+
+ for( ; curr_track != NULL; curr_track = curr_track->Next() )
+ {
+ if( curr_track->GetSubNet() != aOldSubNet )
+ {
+ if( curr_track == m_lastTrack )
+ break;
+
+ continue;
+ }
+
+ change_count++;
+ curr_track->SetSubNet( aNewSubNet );
+
+ for( unsigned ii = 0; ii < curr_track->m_PadsConnected.size(); ii++ )
+ {
+ D_PAD * pad = curr_track->m_PadsConnected[ii];
+ if( pad->GetSubNet() == aOldSubNet )
+ {
+ pad->SetSubNet( curr_track->GetSubNet() );
+ }
+ }
+
+ if( curr_track == m_lastTrack )
+ break;
+ }
+
+ return change_count;
+}
+
+
+/* Test a list of track segments, to create or propagate a sub netcode to pads and
+ * segments connected together.
+ * The track list must be sorted by nets, and all segments
+ * from m_firstTrack to m_lastTrack have the same net
+ * When 2 items are connected (a track to a pad, or a track to an other track),
+ * they are grouped in a cluster.
+ * The .m_Subnet member is the cluster identifier (subnet id)
+ * For a given net, if all tracks are created, there is only one cluster.
+ * but if not all tracks are created, there are more than one cluster,
+ * and some ratsnests will be left active.
+ * A ratsnest is active when it "connect" 2 items having different subnet id
+ */
+void CONNECTIONS::Propagate_SubNets()
+{
+ int sub_netcode = 1;
+
+ TRACK* curr_track = (TRACK*)m_firstTrack;
+ if( curr_track )
+ curr_track->SetSubNet( sub_netcode );
+
+ // Examine connections between tracks and pads
+ for( ; curr_track != NULL; curr_track = curr_track->Next() )
+ {
+ // First: handling connections to pads
+ for( unsigned ii = 0; ii < curr_track->m_PadsConnected.size(); ii++ )
+ {
+ D_PAD * pad = curr_track->m_PadsConnected[ii];
+
+ if( curr_track->GetSubNet() ) // the track segment is already a cluster member
+ {
+ if( pad->GetSubNet() > 0 )
+ {
+ // The pad is already a cluster member, so we can merge the 2 clusters
+ Merge_SubNets( pad->GetSubNet(), curr_track->GetSubNet() );
+ }
+ else
+ {
+ /* The pad is not yet attached to a cluster , so we can add this pad to
+ * the cluster */
+ pad->SetSubNet( curr_track->GetSubNet() );
+ }
+ }
+ else // the track segment is not attached to a cluster
+ {
+ if( pad->GetSubNet() > 0 )
+ {
+ // it is connected to a pad in a cluster, merge this track
+ curr_track->SetSubNet( pad->GetSubNet() );
+ }
+ else
+ {
+ /* it is connected to a pad not in a cluster, so we must create a new
+ * cluster (only with the 2 items: the track and the pad) */
+ sub_netcode++;
+ curr_track->SetSubNet( sub_netcode );
+ pad->SetSubNet( curr_track->GetSubNet() );
+ }
+ }
+ }
+
+ // Test connections between segments
+ for( unsigned ii = 0; ii < curr_track->m_TracksConnected.size(); ii++ )
+ {
+ BOARD_CONNECTED_ITEM* track = curr_track->m_TracksConnected[ii];
+
+ if( curr_track->GetSubNet() ) // The current track is already a cluster member
+ {
+ // The other track is already a cluster member, so we can merge the 2 clusters
+ if( track->GetSubNet() )
+ {
+ Merge_SubNets( track->GetSubNet(), curr_track->GetSubNet() );
+ }
+ else
+ {
+ // The other track is not yet attached to a cluster , so we can add this
+ // other track to the cluster
+ track->SetSubNet( curr_track->GetSubNet() );
+ }
+ }
+ else // the current track segment is not yet attached to a cluster
+ {
+ if( track->GetSubNet() )
+ {
+ // The other track is already a cluster member, so we can add
+ // the current segment to the cluster
+ curr_track->SetSubNet( track->GetSubNet() );
+ }
+ else
+ {
+ // it is connected to an other segment not in a cluster, so we must
+ // create a new cluster (only with the 2 track segments)
+ sub_netcode++;
+ curr_track->SetSubNet( sub_netcode );
+ track->SetSubNet( curr_track->GetSubNet() );
+ }
+ }
+ }
+
+ if( curr_track == m_lastTrack )
+ break;
+ }
+
+ // Examine connections between intersecting pads, and propagate
+ // sub_netcodes to intersecting pads
+ for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
+ {
+ D_PAD* curr_pad = m_sortedPads[ii];
+
+ for( unsigned jj = 0; jj < curr_pad->m_PadsConnected.size(); jj++ )
+ {
+ D_PAD* pad = curr_pad->m_PadsConnected[jj];
+
+ if( curr_pad->GetSubNet() ) // the current pad is already attached to a cluster
+ {
+ if( pad->GetSubNet() > 0 )
+ {
+ // The pad is already a cluster member, so we can merge the 2 clusters
+ // Store the initial subnets, which will be modified by Merge_PadsSubNets
+ int subnet1 = pad->GetSubNet();
+ int subnet2 = curr_pad->GetSubNet();
+
+ // merge subnets of pads only, even those not connected by tracks
+ Merge_PadsSubNets( subnet1, subnet2 );
+
+ // merge subnets of tracks (and pads, which are already merged)
+ Merge_SubNets( subnet1, subnet2 );
+ }
+ else
+ {
+ // The pad is not yet attached to a cluster,
+ // so we can add this pad to the cluster
+ pad->SetSubNet( curr_pad->GetSubNet() );
+ }
+ }
+ else // the current pad is not attached to a cluster
+ {
+ if( pad->GetSubNet() > 0 )
+ {
+ // the connected pad is in a cluster,
+ // so we can add the current pad to the cluster
+ curr_pad->SetSubNet( pad->GetSubNet() );
+ }
+ else
+ {
+ // the connected pad is not in a cluster,
+ // so we must create a new cluster, with the 2 pads.
+ sub_netcode++;
+ curr_pad->SetSubNet( sub_netcode );
+ pad->SetSubNet( curr_pad->GetSubNet() );
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Test all connections of the board,
+ * and update subnet variable of pads and tracks
+ * TestForActiveLinksInRatsnest must be called after this function
+ * to update active/inactive ratsnest items status
+ */
+void PCB_BASE_FRAME::TestConnections()
+{
+ // Clear the cluster identifier for all pads
+ for( unsigned i = 0; i< m_Pcb->GetPadCount(); ++i )
+ {
+ D_PAD* pad = m_Pcb->GetPad(i);
+
+ pad->SetZoneSubNet( 0 );
+ pad->SetSubNet( 0 );
+ }
+
+ m_Pcb->Test_Connections_To_Copper_Areas();
+
+ // Test existing connections net by net
+ // note some nets can have no tracks, and pads intersecting
+ // so Build_CurrNet_SubNets_Connections must be called for each net
+ CONNECTIONS connections( m_Pcb );
+
+ int last_net_tested = 0;
+ int current_net_code = 0;
+
+ for( TRACK* track = m_Pcb->m_Track; track; )
+ {
+ // At this point, track is the first track of a given net
+ current_net_code = track->GetNetCode();
+
+ // Get last track of the current net
+ TRACK* lastTrack = track->GetEndNetCode( current_net_code );
+
+ if( current_net_code > 0 ) // do not spend time if net code = 0 ( dummy net )
+ {
+ // Test all previous nets having no tracks
+ for( int net = last_net_tested+1; net < current_net_code; net++ )
+ connections.Build_CurrNet_SubNets_Connections( NULL, NULL, net );
+
+ connections.Build_CurrNet_SubNets_Connections( track, lastTrack, current_net_code );
+ last_net_tested = current_net_code;
+ }
+
+ track = lastTrack->Next(); // this is now the first track of the next net
+ }
+
+ // Test last nets without tracks, if any
+ int netsCount = m_Pcb->GetNetCount();
+ for( int net = last_net_tested+1; net < netsCount; net++ )
+ connections.Build_CurrNet_SubNets_Connections( NULL, NULL, net );
+
+ Merge_SubNets_Connected_By_CopperAreas( m_Pcb );
+
+ return;
+}
+
+
+void PCB_BASE_FRAME::TestNetConnection( wxDC* aDC, int aNetCode )
+{
+ // Skip dummy net -1, and "not connected" net 0 (grouping all not connected pads)
+ if( aNetCode <= 0 )
+ return;
+
+ if( (m_Pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
+ Compile_Ratsnest( aDC, true );
+
+ // Clear the cluster identifier (subnet) of pads for this net
+ // Pads are grouped by netcode (and in netname alphabetic order)
+ for( unsigned i = 0; i < m_Pcb->GetPadCount(); ++i )
+ {
+ D_PAD* pad = m_Pcb->GetPad(i);
+
+ if( m_Pcb->GetPad(i)->GetNetCode() == aNetCode )
+ pad->SetSubNet( 0 );
+ }
+
+ m_Pcb->Test_Connections_To_Copper_Areas( aNetCode );
+
+ // Search for the first and the last segment relative to the given net code
+ if( m_Pcb->m_Track )
+ {
+ CONNECTIONS connections( m_Pcb );
+
+ TRACK* lastTrack = NULL;
+ TRACK* firstTrack = m_Pcb->m_Track.GetFirst()->GetStartNetCode( aNetCode );
+
+ if( firstTrack )
+ lastTrack = firstTrack->GetEndNetCode( aNetCode );
+
+ if( firstTrack && lastTrack ) // i.e. if there are segments
+ {
+ connections.Build_CurrNet_SubNets_Connections( firstTrack, lastTrack, aNetCode );
+ }
+ }
+
+ Merge_SubNets_Connected_By_CopperAreas( m_Pcb, aNetCode );
+
+ // rebuild the active ratsnest for this net
+ DrawGeneralRatsnest( aDC, aNetCode );
+ TestForActiveLinksInRatsnest( aNetCode );
+ DrawGeneralRatsnest( aDC, aNetCode );
+
+ // Display results
+ wxString msg;
+ int net_notconnected_count = 0;
+ NETINFO_ITEM* net = m_Pcb->FindNet( aNetCode );
+
+ if( net ) // Should not occur, but ...
+ {
+ for( unsigned ii = net->m_RatsnestStartIdx; ii < net->m_RatsnestEndIdx; ii++ )
+ {
+ if( m_Pcb->m_FullRatsnest[ii].IsActive() )
+ net_notconnected_count++;
+ }
+
+ msg.Printf( wxT( "links %d nc %d net %d: not conn %d" ),
+ m_Pcb->GetRatsnestsCount(), m_Pcb->GetUnconnectedNetCount(), aNetCode,
+ net_notconnected_count );
+ }
+ else
+ msg.Printf( wxT( "net not found: netcode %d" ), aNetCode );
+
+ SetStatusText( msg );
+
+ return;
+}
+
+
+/* search connections between tracks and pads and propagate pad net codes to the track
+ * segments.
+ * Pads netcodes are assumed to be up to date.
+ */
+void PCB_BASE_FRAME::RecalculateAllTracksNetcode()
+{
+ // Build the net info list
+ GetBoard()->BuildListOfNets();
+
+ // Reset variables and flags used in computation
+ for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() )
+ {
+ t->m_TracksConnected.clear();
+ t->m_PadsConnected.clear();
+ t->start = NULL;
+ t->end = NULL;
+ t->SetState( BUSY | IN_EDIT | BEGIN_ONPAD | END_ONPAD, false );
+ t->SetZoneSubNet( 0 );
+ t->SetNetCode( NETINFO_LIST::UNCONNECTED );
+ }
+
+ // If no pad, reset pointers and netcode, and do nothing else
+ if( m_Pcb->GetPadCount() == 0 )
+ return;
+
+ CONNECTIONS connections( m_Pcb );
+ connections.BuildPadsList();
+ connections.BuildTracksCandidatesList(m_Pcb->m_Track);
+
+ // First pass: build connections between track segments and pads.
+ connections.SearchTracksConnectedToPads();
+
+ // For tracks connected to at least one pad,
+ // set the track net code to the pad netcode
+ for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() )
+ {
+ if( t->m_PadsConnected.size() )
+ t->SetNetCode( t->m_PadsConnected[0]->GetNetCode() );
+ }
+
+ // Pass 2: build connections between track ends
+ for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() )
+ {
+ connections.SearchConnectedTracks( t );
+ connections.GetConnectedTracks( t );
+ }
+
+ // Propagate net codes from a segment to other connected segments
+ bool new_pass_request = true; // set to true if a track has its netcode changed from 0
+ // to a known netcode to re-evaluate netcodes
+ // of connected items
+ while( new_pass_request )
+ {
+ new_pass_request = false;
+
+ for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() )
+ {
+ int netcode = t->GetNetCode();
+
+ if( netcode == 0 )
+ {
+ // try to find a connected item having a netcode
+ for( unsigned kk = 0; kk < t->m_TracksConnected.size(); kk++ )
+ {
+ int altnetcode = t->m_TracksConnected[kk]->GetNetCode();
+ if( altnetcode )
+ {
+ new_pass_request = true;
+ netcode = altnetcode;
+ t->SetNetCode(netcode);
+ break;
+ }
+ }
+ }
+
+ if( netcode ) // this track has a netcode
+ {
+ // propagate this netcode to connected tracks having no netcode
+ for( unsigned kk = 0; kk < t->m_TracksConnected.size(); kk++ )
+ {
+ int altnetcode = t->m_TracksConnected[kk]->GetNetCode();
+ if( altnetcode == 0 )
+ {
+ t->m_TracksConnected[kk]->SetNetCode(netcode);
+ new_pass_request = true;
+ }
+ }
+ }
+ }
+ }
+
+ // Sort the track list by net codes:
+ RebuildTrackChain( m_Pcb );
+}
+
+
+
+/*
+ * Function SortTracksByNetCode used in RebuildTrackChain()
+ * to sort track segments by net code.
+ */
+static bool SortTracksByNetCode( const TRACK* const & ref, const TRACK* const & compare )
+{
+ // For items having the same Net, keep the order in list
+ if( ref->GetNetCode() == compare->GetNetCode())
+ return ref->m_Param < compare->m_Param;
+
+ return ref->GetNetCode() < compare->GetNetCode();
+}
+
+/**
+ * Helper function RebuildTrackChain
+ * rebuilds the track segment linked list in order to have a chain
+ * sorted by increasing netcodes.
+ * We try to keep order of track segments in list, when possible
+ * @param pcb = board to rebuild
+ */
+static void RebuildTrackChain( BOARD* pcb )
+{
+ if( pcb->m_Track == NULL )
+ return;
+
+ int item_count = pcb->m_Track.GetCount();
+
+ std::vector<TRACK*> trackList;
+ trackList.reserve( item_count );
+
+ // Put track list in a temporary list to sort tracks by netcode
+ // We try to keep the initial order of track segments in list, when possible
+ // so we use m_Param (a member variable used for temporary storage)
+ // to temporary keep trace of the order of segments
+ // The sort function uses this variable to sort items that
+ // have the same net code.
+ // Without this, during sorting, the initial order is sometimes lost
+ // by the sort algorithm
+ for( int ii = 0; ii < item_count; ++ii )
+ {
+ pcb->m_Track->m_Param = ii;
+ trackList.push_back( pcb->m_Track.PopFront() );
+ }
+
+ // the list is empty now
+ wxASSERT( pcb->m_Track == NULL && pcb->m_Track.GetCount()==0 );
+
+ sort( trackList.begin(), trackList.end(), SortTracksByNetCode );
+
+ // add them back to the list
+ for( int i = 0; i < item_count; ++i )
+ pcb->m_Track.PushBack( trackList[i] );
+}