summaryrefslogtreecommitdiff
path: root/pcbnew/autorouter/solve.cpp
diff options
context:
space:
mode:
authorsaurabhb172020-02-26 15:57:49 +0530
committersaurabhb172020-02-26 15:57:49 +0530
commitaa35045840b78d3f48212db45da59a2e5c69b223 (patch)
tree6acee185a4dc19113fcbf0f9a3d6941085dedaf7 /pcbnew/autorouter/solve.cpp
parent0db48f6533517ecebfd9f0693f89deca28408b76 (diff)
downloadKiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.tar.gz
KiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.tar.bz2
KiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.zip
Added main execs
Diffstat (limited to 'pcbnew/autorouter/solve.cpp')
-rw-r--r--pcbnew/autorouter/solve.cpp1348
1 files changed, 1348 insertions, 0 deletions
diff --git a/pcbnew/autorouter/solve.cpp b/pcbnew/autorouter/solve.cpp
new file mode 100644
index 0000000..c10e57f
--- /dev/null
+++ b/pcbnew/autorouter/solve.cpp
@@ -0,0 +1,1348 @@
+/*
+ * 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-2012 KiCad Developers, see change_log.txt for contributors.
+ *
+ * First copyright (C) Randy Nevin, 1989 (see PCBCA package)
+ *
+ * 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
+ */
+
+/* see "Autorouting With the A* Algorithm" (Dr.Dobbs journal)
+*/
+
+/**
+ * @file solve.cpp
+ */
+
+#include <fctsys.h>
+#include <class_drawpanel.h>
+#include <confirm.h>
+#include <wxPcbStruct.h>
+#include <gr_basic.h>
+#include <macros.h>
+
+#include <class_board.h>
+#include <class_track.h>
+
+#include <pcbnew.h>
+#include <protos.h>
+#include <autorout.h>
+#include <cell.h>
+
+
+static int Autoroute_One_Track( PCB_EDIT_FRAME* pcbframe,
+ wxDC* DC,
+ int two_sides,
+ int row_source,
+ int col_source,
+ int row_target,
+ int col_target,
+ RATSNEST_ITEM* pt_rat );
+
+static int Retrace( PCB_EDIT_FRAME* pcbframe,
+ wxDC* DC,
+ int,
+ int,
+ int,
+ int,
+ int,
+ int net_code );
+
+static void OrCell_Trace( BOARD* pcb,
+ int col,
+ int row,
+ int side,
+ int orient,
+ int current_net_code );
+
+static void AddNewTrace( PCB_EDIT_FRAME* pcbframe, wxDC* DC );
+
+
+static int segm_oX, segm_oY;
+static int segm_fX, segm_fY; /* Origin and position of the current
+ * trace segment. */
+static RATSNEST_ITEM* pt_cur_ch;
+static int s_Clearance; // Clearance value used in autorouter
+
+static PICKED_ITEMS_LIST s_ItemsListPicker;
+
+int OpenNodes; // total number of nodes opened
+int ClosNodes; // total number of nodes closed
+int MoveNodes; // total number of nodes moved
+int MaxNodes; // maximum number of nodes opened at one time
+
+#define NOSUCCESS 0
+#define STOP_FROM_ESC -1
+#define ERR_MEMORY -2
+#define SUCCESS 1
+#define TRIVIAL_SUCCESS 2
+
+/*
+** visit neighboring cells like this (where [9] is on the other side):
+**
+** +---+---+---+
+** | 1 | 2 | 3 |
+** +---+---+---+
+** | 4 |[9]| 5 |
+** +---+---+---+
+** | 6 | 7 | 8 |
+** +---+---+---+
+*/
+
+/* for visiting neighbors on the same side: increments/decrements coord of
+ * [] [0] = row [] (1] = col was added to the coord of the midpoint for
+ * Get the coord of the 8 neighboring points.
+ */
+static const int delta[8][2] =
+{
+ { 1, -1 }, // northwest
+ { 1, 0 }, // north
+ { 1, 1 }, // northeast
+ { 0, -1 }, // west
+ { 0, 1 }, // east
+ { -1, -1 }, // southwest
+ { -1, 0 }, // south
+ { -1, 1 } // southeast
+};
+
+static const int ndir[8] =
+{
+ // for building paths back to source
+ FROM_SOUTHEAST, FROM_SOUTH, FROM_SOUTHWEST,
+ FROM_EAST, FROM_WEST,
+ FROM_NORTHEAST, FROM_NORTH, FROM_NORTHWEST
+};
+
+// blocking masks for neighboring cells
+#define BLOCK_NORTHEAST ( DIAG_NEtoSW | BENT_StoNE | BENT_WtoNE \
+ | ANGLE_NEtoSE | ANGLE_NWtoNE \
+ | SHARP_NtoNE | SHARP_EtoNE | HOLE )
+#define BLOCK_SOUTHEAST ( DIAG_SEtoNW | BENT_NtoSE | BENT_WtoSE \
+ | ANGLE_NEtoSE | ANGLE_SEtoSW \
+ | SHARP_EtoSE | SHARP_StoSE | HOLE )
+#define BLOCK_SOUTHWEST ( DIAG_NEtoSW | BENT_NtoSW | BENT_EtoSW \
+ | ANGLE_SEtoSW | ANGLE_SWtoNW \
+ | SHARP_StoSW | SHARP_WtoSW | HOLE )
+#define BLOCK_NORTHWEST ( DIAG_SEtoNW | BENT_EtoNW | BENT_StoNW \
+ | ANGLE_SWtoNW | ANGLE_NWtoNE \
+ | SHARP_WtoNW | SHARP_NtoNW | HOLE )
+#define BLOCK_NORTH ( LINE_VERTICAL | BENT_NtoSE | BENT_NtoSW \
+ | BENT_EtoNW | BENT_WtoNE \
+ | BENT_StoNE | BENT_StoNW \
+ | CORNER_NORTHEAST | CORNER_NORTHWEST \
+ | ANGLE_NEtoSE | ANGLE_SWtoNW | ANGLE_NWtoNE \
+ | DIAG_NEtoSW | DIAG_SEtoNW \
+ | SHARP_NtoNE | SHARP_NtoNW \
+ | SHARP_EtoNE | SHARP_WtoNW | HOLE )
+#define BLOCK_EAST ( LINE_HORIZONTAL | BENT_EtoSW | BENT_EtoNW \
+ | BENT_NtoSE | BENT_StoNE \
+ | BENT_WtoNE | BENT_WtoSE \
+ | CORNER_NORTHEAST | CORNER_SOUTHEAST \
+ | ANGLE_NEtoSE | ANGLE_SEtoSW | ANGLE_NWtoNE \
+ | DIAG_NEtoSW | DIAG_SEtoNW \
+ | SHARP_EtoNE | SHARP_EtoSE \
+ | SHARP_NtoNE | SHARP_StoSE | HOLE )
+#define BLOCK_SOUTH ( LINE_VERTICAL | BENT_StoNE | BENT_StoNW \
+ | BENT_EtoSW | BENT_WtoSE \
+ | BENT_NtoSE | BENT_NtoSW \
+ | CORNER_SOUTHEAST | CORNER_SOUTHWEST \
+ | ANGLE_NEtoSE | ANGLE_SWtoNW | ANGLE_SEtoSW \
+ | DIAG_NEtoSW | DIAG_SEtoNW \
+ | SHARP_StoSE | SHARP_StoSW \
+ | SHARP_EtoSE | SHARP_WtoSW | HOLE )
+#define BLOCK_WEST ( LINE_HORIZONTAL | BENT_WtoNE | BENT_WtoSE \
+ | BENT_NtoSW | BENT_StoNW \
+ | BENT_EtoSW | BENT_EtoNW \
+ | CORNER_SOUTHWEST | CORNER_NORTHWEST \
+ | ANGLE_SWtoNW | ANGLE_SEtoSW | ANGLE_NWtoNE \
+ | DIAG_NEtoSW | DIAG_SEtoNW \
+ | SHARP_WtoSW | SHARP_WtoNW \
+ | SHARP_NtoNW | SHARP_StoSW | HOLE )
+
+struct block
+{
+ int r1, c1;
+ long b1;
+ int r2, c2;
+ long b2;
+};
+
+// blocking masks for diagonal traces
+static struct block blocking[8] =
+{ {
+ 0, -1,
+ BLOCK_NORTHEAST,
+ 1, 0,
+ BLOCK_SOUTHWEST
+ },
+ {
+ 0, 0, 0,
+ 0, 0, 0
+ },
+ {
+ 1, 0,
+ BLOCK_SOUTHEAST,
+ 0, 1,
+ BLOCK_NORTHWEST
+ },
+ {
+ 0, 0, 0,
+ 0, 0, 0
+ },
+ {
+ 0, 0, 0,
+ 0, 0, 0
+ },
+ {
+ 0, -1,
+ BLOCK_SOUTHEAST,
+ -1, 0,
+ BLOCK_NORTHWEST
+ },
+ {
+ 0, 0, 0,
+ 0, 0, 0
+ },
+ {
+ -1, 0,
+ BLOCK_NORTHEAST,
+ 0, 1,
+ BLOCK_SOUTHWEST
+ } };
+
+// mask for hole-related blocking effects
+static struct
+{
+ long trace;
+ int present;
+} selfok2[8] =
+{
+ { HOLE_NORTHWEST, 0 },
+ { HOLE_NORTH, 0 },
+ { HOLE_NORTHEAST, 0 },
+ { HOLE_WEST, 0 },
+ { HOLE_EAST, 0 },
+ { HOLE_SOUTHWEST, 0 },
+ { HOLE_SOUTH, 0 },
+ { HOLE_SOUTHEAST, 0 }
+};
+
+static long newmask[8] =
+{
+ // patterns to mask out in neighbor cells
+ 0,
+ CORNER_NORTHWEST | CORNER_NORTHEAST,
+ 0,
+ CORNER_NORTHWEST | CORNER_SOUTHWEST,
+ CORNER_NORTHEAST | CORNER_SOUTHEAST,
+ 0,
+ CORNER_SOUTHWEST | CORNER_SOUTHEAST,
+ 0
+};
+
+
+/* Route all traces
+ * :
+ * 1 if OK
+ * -1 if escape (stop being routed) request
+ * -2 if default memory allocation
+ */
+int PCB_EDIT_FRAME::Solve( wxDC* DC, int aLayersCount )
+{
+ int current_net_code;
+ int row_source, col_source, row_target, col_target;
+ int success, nbsucces = 0, nbunsucces = 0;
+ NETINFO_ITEM* net;
+ bool stop = false;
+ wxString msg;
+ int routedCount = 0; // routed ratsnest count
+ bool two_sides = aLayersCount == 2;
+
+ m_canvas->SetAbortRequest( false );
+
+ s_Clearance = GetBoard()->GetDesignSettings().GetDefault()->GetClearance();
+
+ // Prepare the undo command info
+ s_ItemsListPicker.ClearListAndDeleteItems(); // Should not be necessary, but...
+
+ // go until no more work to do
+ GetWork( &row_source, &col_source, &current_net_code,
+ &row_target, &col_target, &pt_cur_ch ); // First net to route.
+
+ for( ; row_source != ILLEGAL; GetWork( &row_source, &col_source,
+ &current_net_code, &row_target,
+ &col_target,
+ &pt_cur_ch ) )
+ {
+ // Test to stop routing ( escape key pressed )
+ wxYield();
+
+ if( m_canvas->GetAbortRequest() )
+ {
+ if( IsOK( this, _( "Abort routing?" ) ) )
+ {
+ success = STOP_FROM_ESC;
+ stop = true;
+ break;
+ }
+ else
+ {
+ m_canvas->SetAbortRequest( false );
+ }
+ }
+
+ EraseMsgBox();
+
+ routedCount++;
+ net = GetBoard()->FindNet( current_net_code );
+
+ if( net )
+ {
+ msg.Printf( wxT( "[%8.8s]" ), GetChars( net->GetNetname() ) );
+ AppendMsgPanel( wxT( "Net route" ), msg, BROWN );
+ msg.Printf( wxT( "%d / %d" ), routedCount, RoutingMatrix.m_RouteCount );
+ AppendMsgPanel( wxT( "Activity" ), msg, BROWN );
+ }
+
+ segm_oX = GetBoard()->GetBoundingBox().GetX() + (RoutingMatrix.m_GridRouting * col_source);
+ segm_oY = GetBoard()->GetBoundingBox().GetY() + (RoutingMatrix.m_GridRouting * row_source);
+ segm_fX = GetBoard()->GetBoundingBox().GetX() + (RoutingMatrix.m_GridRouting * col_target);
+ segm_fY = GetBoard()->GetBoundingBox().GetY() + (RoutingMatrix.m_GridRouting * row_target);
+
+ // Draw segment.
+ GRLine( m_canvas->GetClipBox(), DC,
+ segm_oX, segm_oY, segm_fX, segm_fY,
+ 0, WHITE );
+ pt_cur_ch->m_PadStart->Draw( m_canvas, DC, GR_OR | GR_HIGHLIGHT );
+ pt_cur_ch->m_PadEnd->Draw( m_canvas, DC, GR_OR | GR_HIGHLIGHT );
+
+ success = Autoroute_One_Track( this, DC,
+ two_sides, row_source, col_source,
+ row_target, col_target, pt_cur_ch );
+
+ switch( success )
+ {
+ case NOSUCCESS:
+ pt_cur_ch->m_Status |= CH_UNROUTABLE;
+ nbunsucces++;
+ break;
+
+ case STOP_FROM_ESC:
+ stop = true;
+ break;
+
+ case ERR_MEMORY:
+ stop = true;
+ break;
+
+ default:
+ nbsucces++;
+ break;
+ }
+
+ msg.Printf( wxT( "%d" ), nbsucces );
+ AppendMsgPanel( wxT( "OK" ), msg, GREEN );
+ msg.Printf( wxT( "%d" ), nbunsucces );
+ AppendMsgPanel( wxT( "Fail" ), msg, RED );
+ msg.Printf( wxT( " %d" ), GetBoard()->GetUnconnectedNetCount() );
+ AppendMsgPanel( wxT( "Not Connected" ), msg, CYAN );
+
+ // Delete routing from display.
+ pt_cur_ch->m_PadStart->Draw( m_canvas, DC, GR_AND );
+ pt_cur_ch->m_PadEnd->Draw( m_canvas, DC, GR_AND );
+
+ if( stop )
+ break;
+ }
+
+ SaveCopyInUndoList( s_ItemsListPicker, UR_UNSPECIFIED );
+ s_ItemsListPicker.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items
+
+ return SUCCESS;
+}
+
+
+/* Route a trace on the BOARD.
+ * Parameters:
+ * 1 side / 2 sides (0 / 1)
+ * Coord source (row, col)
+ * Coord destination (row, col)
+ * Net_code
+ * Pointer to the ratsnest reference
+ *
+ * Returns:
+ * SUCCESS if routed
+ * TRIVIAL_SUCCESS if pads are connected by overlay (no track needed)
+ * If failure NOSUCCESS
+ * Escape STOP_FROM_ESC if demand
+ * ERR_MEMORY if memory allocation failed.
+ */
+static int Autoroute_One_Track( PCB_EDIT_FRAME* pcbframe,
+ wxDC* DC,
+ int two_sides,
+ int row_source,
+ int col_source,
+ int row_target,
+ int col_target,
+ RATSNEST_ITEM* pt_rat )
+{
+ int r, c, side, d, apx_dist, nr, nc;
+ int result, skip;
+ int i;
+ long curcell, newcell, buddy, lastopen, lastclos, lastmove;
+ int newdist, olddir, _self;
+ int current_net_code;
+ int marge;
+ LSET padLayerMaskStart; // Mask layers belonging to the starting pad.
+ LSET padLayerMaskEnd; // Mask layers belonging to the ending pad.
+
+ LSET topLayerMask( g_Route_Layer_TOP );
+
+ LSET bottomLayerMask( g_Route_Layer_BOTTOM );
+
+ LSET routeLayerMask; // Mask two layers for routing.
+
+ LSET tab_mask[2]; // Enables the calculation of the mask layer being
+ // tested. (side = TOP or BOTTOM)
+ int start_mask_layer = 0;
+ wxString msg;
+
+ // @todo this could be a bottle neck
+ LSET all_cu = LSET::AllCuMask( pcbframe->GetBoard()->GetCopperLayerCount() );
+
+ wxBusyCursor dummy_cursor; // Set an hourglass cursor while routing a
+ // track
+
+ result = NOSUCCESS;
+
+ marge = s_Clearance + ( pcbframe->GetDesignSettings().GetCurrentTrackWidth() / 2 );
+
+ // clear direction flags
+ i = RoutingMatrix.m_Nrows * RoutingMatrix.m_Ncols * sizeof(DIR_CELL);
+
+ if( two_sides )
+ memset( RoutingMatrix.m_DirSide[TOP], FROM_NOWHERE, i );
+ memset( RoutingMatrix.m_DirSide[BOTTOM], FROM_NOWHERE, i );
+
+ lastopen = lastclos = lastmove = 0;
+
+ // Set tab_masque[side] for final test of routing.
+ if( two_sides )
+ tab_mask[TOP] = topLayerMask;
+ tab_mask[BOTTOM] = bottomLayerMask;
+
+ // Set active layers mask.
+ routeLayerMask = topLayerMask | bottomLayerMask;
+
+ pt_cur_ch = pt_rat;
+
+ current_net_code = pt_rat->GetNet();
+ padLayerMaskStart = pt_cur_ch->m_PadStart->GetLayerSet();
+
+ padLayerMaskEnd = pt_cur_ch->m_PadEnd->GetLayerSet();
+
+
+ /* First Test if routing possible ie if the pads are accessible
+ * on the routing layers.
+ */
+ if( ( routeLayerMask & padLayerMaskStart ) == 0 )
+ goto end_of_route;
+
+ if( ( routeLayerMask & padLayerMaskEnd ) == 0 )
+ goto end_of_route;
+
+ /* Then test if routing possible ie if the pads are accessible
+ * On the routing grid (1 grid point must be in the pad)
+ */
+ {
+ int cX = ( RoutingMatrix.m_GridRouting * col_source )
+ + pcbframe->GetBoard()->GetBoundingBox().GetX();
+ int cY = ( RoutingMatrix.m_GridRouting * row_source )
+ + pcbframe->GetBoard()->GetBoundingBox().GetY();
+ int dx = pt_cur_ch->m_PadStart->GetSize().x / 2;
+ int dy = pt_cur_ch->m_PadStart->GetSize().y / 2;
+ int px = pt_cur_ch->m_PadStart->GetPosition().x;
+ int py = pt_cur_ch->m_PadStart->GetPosition().y;
+
+ if( ( ( int( pt_cur_ch->m_PadStart->GetOrientation() ) / 900 ) & 1 ) != 0 )
+ std::swap( dx, dy );
+
+ if( ( abs( cX - px ) > dx ) || ( abs( cY - py ) > dy ) )
+ goto end_of_route;
+
+ cX = ( RoutingMatrix.m_GridRouting * col_target )
+ + pcbframe->GetBoard()->GetBoundingBox().GetX();
+ cY = ( RoutingMatrix.m_GridRouting * row_target )
+ + pcbframe->GetBoard()->GetBoundingBox().GetY();
+ dx = pt_cur_ch->m_PadEnd->GetSize().x / 2;
+ dy = pt_cur_ch->m_PadEnd->GetSize().y / 2;
+ px = pt_cur_ch->m_PadEnd->GetPosition().x;
+ py = pt_cur_ch->m_PadEnd->GetPosition().y;
+
+ if( ( ( int( pt_cur_ch->m_PadEnd->GetOrientation() ) / 900) & 1 ) != 0 )
+ std::swap( dx, dy );
+
+ if( ( abs( cX - px ) > dx ) || ( abs( cY - py ) > dy ) )
+ goto end_of_route;
+ }
+
+ // Test the trivial case: direct connection overlay pads.
+ if( row_source == row_target && col_source == col_target &&
+ ( padLayerMaskEnd & padLayerMaskStart & all_cu ).any() )
+ {
+ result = TRIVIAL_SUCCESS;
+ goto end_of_route;
+ }
+
+ // Placing the bit to remove obstacles on 2 pads to a link.
+ pcbframe->SetStatusText( wxT( "Gen Cells" ) );
+
+ PlacePad( pt_cur_ch->m_PadStart, CURRENT_PAD, marge, WRITE_OR_CELL );
+ PlacePad( pt_cur_ch->m_PadEnd, CURRENT_PAD, marge, WRITE_OR_CELL );
+
+ // Regenerates the remaining barriers (which may encroach on the
+ // placement bits precedent)
+ i = pcbframe->GetBoard()->GetPadCount();
+
+ for( unsigned ii = 0; ii < pcbframe->GetBoard()->GetPadCount(); ii++ )
+ {
+ D_PAD* ptr = pcbframe->GetBoard()->GetPad( ii );
+
+ if( ( pt_cur_ch->m_PadStart != ptr ) && ( pt_cur_ch->m_PadEnd != ptr ) )
+ {
+ PlacePad( ptr, ~CURRENT_PAD, marge, WRITE_AND_CELL );
+ }
+ }
+
+ InitQueue(); // initialize the search queue
+ apx_dist = RoutingMatrix.GetApxDist( row_source, col_source, row_target, col_target );
+
+ // Initialize first search.
+ if( two_sides ) // Preferred orientation.
+ {
+ if( abs( row_target - row_source ) > abs( col_target - col_source ) )
+ {
+ if( ( padLayerMaskStart & topLayerMask ).any() )
+ {
+ start_mask_layer = 2;
+
+ if( SetQueue( row_source, col_source, TOP, 0, apx_dist,
+ row_target, col_target ) == 0 )
+ {
+ return ERR_MEMORY;
+ }
+ }
+
+ if( ( padLayerMaskStart & bottomLayerMask ).any() )
+ {
+ start_mask_layer |= 1;
+
+ if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist,
+ row_target, col_target ) == 0 )
+ {
+ return ERR_MEMORY;
+ }
+ }
+ }
+ else
+ {
+ if( ( padLayerMaskStart & bottomLayerMask ).any() )
+ {
+ start_mask_layer = 1;
+
+ if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist,
+ row_target, col_target ) == 0 )
+ {
+ return ERR_MEMORY;
+ }
+ }
+
+ if( ( padLayerMaskStart & topLayerMask ).any() )
+ {
+ start_mask_layer |= 2;
+
+ if( SetQueue( row_source, col_source, TOP, 0, apx_dist,
+ row_target, col_target ) == 0 )
+ {
+ return ERR_MEMORY;
+ }
+ }
+ }
+ }
+ else if( ( padLayerMaskStart & bottomLayerMask ).any() )
+ {
+ start_mask_layer = 1;
+
+ if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist, row_target, col_target ) == 0 )
+ {
+ return ERR_MEMORY;
+ }
+ }
+
+ // search until success or we exhaust all possibilities
+ GetQueue( &r, &c, &side, &d, &apx_dist );
+
+ for( ; r != ILLEGAL; GetQueue( &r, &c, &side, &d, &apx_dist ) )
+ {
+ curcell = RoutingMatrix.GetCell( r, c, side );
+
+ if( curcell & CURRENT_PAD )
+ curcell &= ~HOLE;
+
+ if( (r == row_target) && (c == col_target) // success if layer OK
+ && (tab_mask[side] & padLayerMaskEnd).any() )
+ {
+ // Remove link.
+ GRSetDrawMode( DC, GR_XOR );
+ GRLine( pcbframe->GetCanvas()->GetClipBox(),
+ DC,
+ segm_oX,
+ segm_oY,
+ segm_fX,
+ segm_fY,
+ 0,
+ WHITE );
+
+ // Generate trace.
+ if( Retrace( pcbframe, DC, row_source, col_source,
+ row_target, col_target, side, current_net_code ) )
+ {
+ result = SUCCESS; // Success : Route OK
+ }
+
+ break; // Routing complete.
+ }
+
+ if( pcbframe->GetCanvas()->GetAbortRequest() )
+ {
+ result = STOP_FROM_ESC;
+ break;
+ }
+
+ // report every COUNT new nodes or so
+ #define COUNT 20000
+
+ if( ( OpenNodes - lastopen > COUNT )
+ || ( ClosNodes - lastclos > COUNT )
+ || ( MoveNodes - lastmove > COUNT ) )
+ {
+ lastopen = OpenNodes;
+ lastclos = ClosNodes;
+ lastmove = MoveNodes;
+ msg.Printf( wxT( "Activity: Open %d Closed %d Moved %d" ),
+ OpenNodes, ClosNodes, MoveNodes );
+ pcbframe->SetStatusText( msg );
+ }
+
+ _self = 0;
+
+ if( curcell & HOLE )
+ {
+ _self = 5;
+
+ // set 'present' bits
+ for( i = 0; i < 8; i++ )
+ {
+ selfok2[i].present = 0;
+
+ if( curcell & selfok2[i].trace )
+ selfok2[i].present = 1;
+ }
+ }
+
+ for( i = 0; i < 8; i++ ) // consider neighbors
+ {
+ nr = r + delta[i][0];
+ nc = c + delta[i][1];
+
+ // off the edge?
+ if( nr < 0 || nr >= RoutingMatrix.m_Nrows ||
+ nc < 0 || nc >= RoutingMatrix.m_Ncols )
+ continue; // off the edge
+
+ if( _self == 5 && selfok2[i].present )
+ continue;
+
+ newcell = RoutingMatrix.GetCell( nr, nc, side );
+
+ if( newcell & CURRENT_PAD )
+ newcell &= ~HOLE;
+
+ // check for non-target hole
+ if( newcell & HOLE )
+ {
+ if( nr != row_target || nc != col_target )
+ continue;
+ }
+ // check for traces
+ else if( newcell & HOLE & ~(newmask[i]) )
+ {
+ continue;
+ }
+
+ // check blocking on corner neighbors
+ if( delta[i][0] && delta[i][1] )
+ {
+ // check first buddy
+ buddy = RoutingMatrix.GetCell( r + blocking[i].r1, c + blocking[i].c1, side );
+
+ if( buddy & CURRENT_PAD )
+ buddy &= ~HOLE;
+
+ if( buddy & HOLE )
+ continue;
+
+// if (buddy & (blocking[i].b1)) continue;
+ // check second buddy
+ buddy = RoutingMatrix.GetCell( r + blocking[i].r2, c + blocking[i].c2, side );
+
+ if( buddy & CURRENT_PAD )
+ buddy &= ~HOLE;
+
+ if( buddy & HOLE )
+ continue;
+
+// if (buddy & (blocking[i].b2)) continue;
+ }
+
+ olddir = RoutingMatrix.GetDir( r, c, side );
+ newdist = d + RoutingMatrix.CalcDist( ndir[i], olddir,
+ ( olddir == FROM_OTHERSIDE ) ?
+ RoutingMatrix.GetDir( r, c, 1 - side ) : 0, side );
+
+ // if (a) not visited yet, or (b) we have
+ // found a better path, add it to queue
+ if( !RoutingMatrix.GetDir( nr, nc, side ) )
+ {
+ RoutingMatrix.SetDir( nr, nc, side, ndir[i] );
+ RoutingMatrix.SetDist( nr, nc, side, newdist );
+
+ if( SetQueue( nr, nc, side, newdist,
+ RoutingMatrix.GetApxDist( nr, nc, row_target, col_target ),
+ row_target, col_target ) == 0 )
+ {
+ return ERR_MEMORY;
+ }
+ }
+ else if( newdist < RoutingMatrix.GetDist( nr, nc, side ) )
+ {
+ RoutingMatrix.SetDir( nr, nc, side, ndir[i] );
+ RoutingMatrix.SetDist( nr, nc, side, newdist );
+ ReSetQueue( nr, nc, side, newdist,
+ RoutingMatrix.GetApxDist( nr, nc, row_target, col_target ),
+ row_target, col_target );
+ }
+ }
+
+ //* Test the other layer. *
+ if( two_sides )
+ {
+ olddir = RoutingMatrix.GetDir( r, c, side );
+
+ if( olddir == FROM_OTHERSIDE )
+ continue; // useless move, so don't bother
+
+ if( curcell ) // can't drill via if anything here
+ continue;
+
+ // check for holes or traces on other side
+ if( ( newcell = RoutingMatrix.GetCell( r, c, 1 - side ) ) != 0 )
+ continue;
+
+ // check for nearby holes or traces on both sides
+ for( skip = 0, i = 0; i < 8; i++ )
+ {
+ nr = r + delta[i][0]; nc = c + delta[i][1];
+
+ if( nr < 0 || nr >= RoutingMatrix.m_Nrows ||
+ nc < 0 || nc >= RoutingMatrix.m_Ncols )
+ continue; // off the edge !!
+
+ if( RoutingMatrix.GetCell( nr, nc, side ) /* & blocking2[i] */ )
+ {
+ skip = 1; // can't drill via here
+ break;
+ }
+
+ if( RoutingMatrix.GetCell( nr, nc, 1 - side ) /* & blocking2[i] */ )
+ {
+ skip = 1; // can't drill via here
+ break;
+ }
+ }
+
+ if( skip ) // neighboring hole or trace?
+ continue; // yes, can't drill via here
+
+ newdist = d + RoutingMatrix.CalcDist( FROM_OTHERSIDE, olddir, 0, side );
+
+ /* if (a) not visited yet,
+ * or (b) we have found a better path,
+ * add it to queue */
+ if( !RoutingMatrix.GetDir( r, c, 1 - side ) )
+ {
+ RoutingMatrix.SetDir( r, c, 1 - side, FROM_OTHERSIDE );
+ RoutingMatrix.SetDist( r, c, 1 - side, newdist );
+
+ if( SetQueue( r, c, 1 - side, newdist, apx_dist, row_target, col_target ) == 0 )
+ {
+ return ERR_MEMORY;
+ }
+ }
+ else if( newdist < RoutingMatrix.GetDist( r, c, 1 - side ) )
+ {
+ RoutingMatrix.SetDir( r, c, 1 - side, FROM_OTHERSIDE );
+ RoutingMatrix.SetDist( r, c, 1 - side, newdist );
+ ReSetQueue( r, c,
+ 1 - side,
+ newdist,
+ apx_dist,
+ row_target,
+ col_target );
+ }
+ } // Finished attempt to route on other layer.
+ }
+
+end_of_route:
+ PlacePad( pt_cur_ch->m_PadStart, ~CURRENT_PAD, marge, WRITE_AND_CELL );
+ PlacePad( pt_cur_ch->m_PadEnd, ~CURRENT_PAD, marge, WRITE_AND_CELL );
+
+ msg.Printf( wxT( "Activity: Open %d Closed %d Moved %d"),
+ OpenNodes, ClosNodes, MoveNodes );
+ pcbframe->SetStatusText( msg );
+
+ return result;
+}
+
+
+static long bit[8][9] =
+{
+ // OT=Otherside
+ // N, NE, E, SE, S, SW, W, NW, OT
+// N
+ { LINE_VERTICAL,
+ BENT_StoNE,
+ CORNER_SOUTHEAST,
+ SHARP_StoSE,
+ 0,
+ SHARP_StoSW,
+ CORNER_SOUTHWEST,
+ BENT_StoNW,
+ ( HOLE | HOLE_SOUTH )
+ },
+// NE
+ {
+ BENT_NtoSW,
+ DIAG_NEtoSW,
+ BENT_EtoSW,
+ ANGLE_SEtoSW,
+ SHARP_StoSW,
+ 0,
+ SHARP_WtoSW,
+ ANGLE_SWtoNW,
+ ( HOLE | HOLE_SOUTHWEST )
+ },
+// E
+ {
+ CORNER_NORTHWEST,
+ BENT_WtoNE,
+ LINE_HORIZONTAL,
+ BENT_WtoSE,
+ CORNER_SOUTHWEST,
+ SHARP_WtoSW,
+ 0,
+ SHARP_WtoNW,
+ ( HOLE | HOLE_WEST )
+ },
+// SE
+ {
+ SHARP_NtoNW,
+ ANGLE_NWtoNE,
+ BENT_EtoNW,
+ DIAG_SEtoNW,
+ BENT_StoNW,
+ ANGLE_SWtoNW,
+ SHARP_WtoNW,
+ 0,
+ ( HOLE | HOLE_NORTHWEST )
+ },
+// S
+ {
+ 0,
+ SHARP_NtoNE,
+ CORNER_NORTHEAST,
+ BENT_NtoSE,
+ LINE_VERTICAL,
+ BENT_NtoSW,
+ CORNER_NORTHWEST,
+ SHARP_NtoNW,
+ ( HOLE | HOLE_NORTH )
+ },
+// SW
+ {
+ SHARP_NtoNE,
+ 0,
+ SHARP_EtoNE,
+ ANGLE_NEtoSE,
+ BENT_StoNE,
+ DIAG_NEtoSW,
+ BENT_WtoNE,
+ ANGLE_NWtoNE,
+ ( HOLE | HOLE_NORTHEAST )
+ },
+// W
+ {
+ CORNER_NORTHEAST,
+ SHARP_EtoNE,
+ 0,
+ SHARP_EtoSE,
+ CORNER_SOUTHEAST,
+ BENT_EtoSW,
+ LINE_HORIZONTAL,
+ BENT_EtoNW,
+ ( HOLE | HOLE_EAST )
+ },
+// NW
+ {
+ BENT_NtoSE,
+ ANGLE_NEtoSE,
+ SHARP_EtoSE,
+ 0,
+ SHARP_StoSE,
+ ANGLE_SEtoSW,
+ BENT_WtoSE,
+ DIAG_SEtoNW,
+ ( HOLE | HOLE_SOUTHEAST )
+ }
+};
+
+
+/* work from target back to source, actually laying the traces
+ * Parameters:
+ * start on side target_side, of coordinates row_target, col_target.
+ * arrive on side masque_layer_start, coordinate row_source, col_source
+ * The search is done in reverse routing, the point of arrival (target) to
+ * the starting point (source)
+ * The router.
+ *
+ * Target_side = symbol (TOP / BOTTOM) of departure
+ * = Mask_layer_source mask layers Arrival
+ *
+ * Returns:
+ * 0 if error
+ * > 0 if Ok
+ */
+static int Retrace( PCB_EDIT_FRAME* pcbframe, wxDC* DC,
+ int row_source, int col_source,
+ int row_target, int col_target, int target_side,
+ int current_net_code )
+{
+ int r0, c0, s0;
+ int r1, c1, s1; // row, col, starting side.
+ int r2, c2, s2; // row, col, ending side.
+ int x, y = -1;
+ long b;
+
+ r1 = row_target;
+ c1 = col_target; // start point is target ( end point is source )
+ s1 = target_side;
+ r0 = c0 = s0 = ILLEGAL;
+
+ wxASSERT( g_CurrentTrackList.GetCount() == 0 );
+
+ do
+ {
+ // find where we came from to get here
+ r2 = r1; c2 = c1; s2 = s1;
+ x = RoutingMatrix.GetDir( r1, c1, s1 );
+
+ switch( x )
+ {
+ case FROM_NORTH:
+ r2++;
+ break;
+
+ case FROM_EAST:
+ c2++;
+ break;
+
+ case FROM_SOUTH:
+ r2--;
+ break;
+
+ case FROM_WEST:
+ c2--;
+ break;
+
+ case FROM_NORTHEAST:
+ r2++;
+ c2++;
+ break;
+
+ case FROM_SOUTHEAST:
+ r2--;
+ c2++;
+ break;
+
+ case FROM_SOUTHWEST:
+ r2--;
+ c2--;
+ break;
+
+ case FROM_NORTHWEST:
+ r2++;
+ c2--;
+ break;
+
+ case FROM_OTHERSIDE:
+ s2 = 1 - s2;
+ break;
+
+ default:
+ wxMessageBox( wxT( "Retrace: internal error: no way back" ) );
+ return 0;
+ }
+
+ if( r0 != ILLEGAL )
+ y = RoutingMatrix.GetDir( r0, c0, s0 );
+
+ // see if target or hole
+ if( ( ( r1 == row_target ) && ( c1 == col_target ) ) || ( s1 != s0 ) )
+ {
+ int p_dir;
+
+ switch( x )
+ {
+ case FROM_NORTH:
+ p_dir = HOLE_NORTH;
+ break;
+
+ case FROM_EAST:
+ p_dir = HOLE_EAST;
+ break;
+
+ case FROM_SOUTH:
+ p_dir = HOLE_SOUTH;
+ break;
+
+ case FROM_WEST:
+ p_dir = HOLE_WEST;
+ break;
+
+ case FROM_NORTHEAST:
+ p_dir = HOLE_NORTHEAST;
+ break;
+
+ case FROM_SOUTHEAST:
+ p_dir = HOLE_SOUTHEAST;
+ break;
+
+ case FROM_SOUTHWEST:
+ p_dir = HOLE_SOUTHWEST;
+ break;
+
+ case FROM_NORTHWEST:
+ p_dir = HOLE_NORTHWEST;
+ break;
+
+ case FROM_OTHERSIDE:
+ default:
+ DisplayError( pcbframe, wxT( "Retrace: error 1" ) );
+ return 0;
+ }
+
+ OrCell_Trace( pcbframe->GetBoard(), r1, c1, s1, p_dir, current_net_code );
+ }
+ else
+ {
+ if( ( y == FROM_NORTH || y == FROM_NORTHEAST
+ || y == FROM_EAST || y == FROM_SOUTHEAST
+ || y == FROM_SOUTH || y == FROM_SOUTHWEST
+ || y == FROM_WEST || y == FROM_NORTHWEST )
+ && ( x == FROM_NORTH || x == FROM_NORTHEAST
+ || x == FROM_EAST || x == FROM_SOUTHEAST
+ || x == FROM_SOUTH || x == FROM_SOUTHWEST
+ || x == FROM_WEST || x == FROM_NORTHWEST
+ || x == FROM_OTHERSIDE )
+ && ( ( b = bit[y - 1][x - 1] ) != 0 ) )
+ {
+ OrCell_Trace( pcbframe->GetBoard(), r1, c1, s1, b, current_net_code );
+
+ if( b & HOLE )
+ OrCell_Trace( pcbframe->GetBoard(), r2, c2, s2, HOLE, current_net_code );
+ }
+ else
+ {
+ wxMessageBox( wxT( "Retrace: error 2" ) );
+ return 0;
+ }
+ }
+
+ if( ( r2 == row_source ) && ( c2 == col_source ) ) // see if source
+ {
+ int p_dir;
+
+ switch( x )
+ {
+ case FROM_NORTH:
+ p_dir = HOLE_SOUTH;
+ break;
+
+ case FROM_EAST:
+ p_dir = HOLE_WEST;
+ break;
+
+ case FROM_SOUTH:
+ p_dir = HOLE_NORTH;
+ break;
+
+ case FROM_WEST:
+ p_dir = HOLE_EAST;
+ break;
+
+ case FROM_NORTHEAST:
+ p_dir = HOLE_SOUTHWEST;
+ break;
+
+ case FROM_SOUTHEAST:
+ p_dir = HOLE_NORTHWEST;
+ break;
+
+ case FROM_SOUTHWEST:
+ p_dir = HOLE_NORTHEAST;
+ break;
+
+ case FROM_NORTHWEST:
+ p_dir = HOLE_SOUTHEAST;
+ break;
+
+ case FROM_OTHERSIDE:
+ default:
+ wxMessageBox( wxT( "Retrace: error 3" ) );
+ return 0;
+ }
+
+ OrCell_Trace( pcbframe->GetBoard(), r2, c2, s2, p_dir, current_net_code );
+ }
+
+ // move to next cell
+ r0 = r1;
+ c0 = c1;
+ s0 = s1;
+ r1 = r2;
+ c1 = c2;
+ s1 = s2;
+ } while( !( ( r2 == row_source ) && ( c2 == col_source ) ) );
+
+ AddNewTrace( pcbframe, DC );
+ return 1;
+}
+
+
+/* This function is used by Retrace and read the autorouting matrix data cells to create
+ * the real track on the physical board
+ */
+static void OrCell_Trace( BOARD* pcb, int col, int row,
+ int side, int orient, int current_net_code )
+{
+ if( orient == HOLE ) // placement of a via
+ {
+ VIA *newVia = new VIA( pcb );
+
+ g_CurrentTrackList.PushBack( newVia );
+
+ g_CurrentTrackSegment->SetState( TRACK_AR, true );
+ g_CurrentTrackSegment->SetLayer( F_Cu );
+
+ g_CurrentTrackSegment->SetStart(wxPoint( pcb->GetBoundingBox().GetX() +
+ ( RoutingMatrix.m_GridRouting * row ),
+ pcb->GetBoundingBox().GetY() +
+ ( RoutingMatrix.m_GridRouting * col )));
+ g_CurrentTrackSegment->SetEnd( g_CurrentTrackSegment->GetStart() );
+
+ g_CurrentTrackSegment->SetWidth( pcb->GetDesignSettings().GetCurrentViaSize() );
+ newVia->SetViaType( pcb->GetDesignSettings().m_CurrentViaType );
+
+ g_CurrentTrackSegment->SetNetCode( current_net_code );
+ }
+ else // placement of a standard segment
+ {
+ TRACK *newTrack = new TRACK( pcb );
+ int dx0, dy0, dx1, dy1;
+
+
+ g_CurrentTrackList.PushBack( newTrack );
+
+ g_CurrentTrackSegment->SetLayer( g_Route_Layer_BOTTOM );
+
+ if( side == TOP )
+ g_CurrentTrackSegment->SetLayer( g_Route_Layer_TOP );
+
+ g_CurrentTrackSegment->SetState( TRACK_AR, true );
+ g_CurrentTrackSegment->SetEnd( wxPoint( pcb->GetBoundingBox().GetX() +
+ ( RoutingMatrix.m_GridRouting * row ),
+ pcb->GetBoundingBox().GetY() +
+ ( RoutingMatrix.m_GridRouting * col )));
+ g_CurrentTrackSegment->SetNetCode( current_net_code );
+
+ if( g_CurrentTrackSegment->Back() == NULL ) // Start trace.
+ {
+ g_CurrentTrackSegment->SetStart( wxPoint( segm_fX, segm_fY ) );
+
+ // Placement on the center of the pad if outside grid.
+ dx1 = g_CurrentTrackSegment->GetEnd().x - g_CurrentTrackSegment->GetStart().x;
+ dy1 = g_CurrentTrackSegment->GetEnd().y - g_CurrentTrackSegment->GetStart().y;
+
+ dx0 = pt_cur_ch->m_PadEnd->GetPosition().x - g_CurrentTrackSegment->GetStart().x;
+ dy0 = pt_cur_ch->m_PadEnd->GetPosition().y - g_CurrentTrackSegment->GetStart().y;
+
+ // If aligned, change the origin point.
+ if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) )
+ {
+ g_CurrentTrackSegment->SetStart( pt_cur_ch->m_PadEnd->GetPosition() );
+ }
+ else // Creation of a supplemental segment
+ {
+ g_CurrentTrackSegment->SetStart( pt_cur_ch->m_PadEnd->GetPosition() );
+
+ newTrack = (TRACK*)g_CurrentTrackSegment->Clone();
+ newTrack->SetStart( g_CurrentTrackSegment->GetEnd());
+
+ g_CurrentTrackList.PushBack( newTrack );
+ }
+ }
+ else
+ {
+ if( g_CurrentTrackSegment->Back() )
+ {
+ g_CurrentTrackSegment->SetStart( g_CurrentTrackSegment->Back()->GetEnd() );
+ }
+ }
+
+ g_CurrentTrackSegment->SetWidth( pcb->GetDesignSettings().GetCurrentTrackWidth() );
+
+ if( g_CurrentTrackSegment->GetStart() != g_CurrentTrackSegment->GetEnd() )
+ {
+ // Reduce aligned segments by one.
+ TRACK* oldTrack = g_CurrentTrackSegment->Back();
+
+ if( oldTrack && oldTrack->Type() != PCB_VIA_T )
+ {
+ dx1 = g_CurrentTrackSegment->GetEnd().x - g_CurrentTrackSegment->GetStart().x;
+ dy1 = g_CurrentTrackSegment->GetEnd().y - g_CurrentTrackSegment->GetStart().y;
+
+ dx0 = oldTrack->GetEnd().x - oldTrack->GetStart().x;
+ dy0 = oldTrack->GetEnd().y - oldTrack->GetStart().y;
+
+ if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) )
+ {
+ oldTrack->SetEnd( g_CurrentTrackSegment->GetEnd() );
+
+ delete g_CurrentTrackList.PopBack();
+ }
+ }
+ }
+ }
+}
+
+
+/* Insert the new track created in the list of tracks.
+ * amend the points of beginning and end of the track so that they are
+ * connected
+ * Center on pads even if they are off grid.
+ */
+static void AddNewTrace( PCB_EDIT_FRAME* pcbframe, wxDC* DC )
+{
+ if( g_FirstTrackSegment == NULL )
+ return;
+
+ int dx0, dy0, dx1, dy1;
+ int marge, via_marge;
+ EDA_DRAW_PANEL* panel = pcbframe->GetCanvas();
+ PCB_SCREEN* screen = pcbframe->GetScreen();
+
+ marge = s_Clearance + ( pcbframe->GetDesignSettings().GetCurrentTrackWidth() / 2 );
+ via_marge = s_Clearance + ( pcbframe->GetDesignSettings().GetCurrentViaSize() / 2 );
+
+ dx1 = g_CurrentTrackSegment->GetEnd().x - g_CurrentTrackSegment->GetStart().x;
+ dy1 = g_CurrentTrackSegment->GetEnd().y - g_CurrentTrackSegment->GetStart().y;
+
+ // Place on center of pad if off grid.
+ dx0 = pt_cur_ch->m_PadStart->GetPosition().x - g_CurrentTrackSegment->GetStart().x;
+ dy0 = pt_cur_ch->m_PadStart->GetPosition().y - g_CurrentTrackSegment->GetStart().y;
+
+ // If aligned, change the origin point.
+ if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) )
+ {
+ g_CurrentTrackSegment->SetEnd( pt_cur_ch->m_PadStart->GetPosition() );
+ }
+ else
+ {
+ TRACK* newTrack = (TRACK*)g_CurrentTrackSegment->Clone();
+
+ newTrack->SetEnd( pt_cur_ch->m_PadStart->GetPosition() );
+ newTrack->SetStart( g_CurrentTrackSegment->GetEnd() );
+
+ g_CurrentTrackList.PushBack( newTrack );
+ }
+
+ g_FirstTrackSegment->start = pcbframe->GetBoard()->GetPad( g_FirstTrackSegment,
+ ENDPOINT_START );
+
+ if( g_FirstTrackSegment->start )
+ g_FirstTrackSegment->SetState( BEGIN_ONPAD, true );
+
+ g_CurrentTrackSegment->end = pcbframe->GetBoard()->GetPad( g_CurrentTrackSegment,
+ ENDPOINT_END );
+
+ if( g_CurrentTrackSegment->end )
+ g_CurrentTrackSegment->SetState( END_ONPAD, true );
+
+ // Out the new track on the matrix board
+ for( TRACK* track = g_FirstTrackSegment; track; track = track->Next() )
+ {
+ TraceSegmentPcb( track, HOLE, marge, WRITE_CELL );
+ TraceSegmentPcb( track, VIA_IMPOSSIBLE, via_marge, WRITE_OR_CELL );
+ }
+
+ // Insert new segments in real board
+ int netcode = g_FirstTrackSegment->GetNetCode();
+ TRACK* firstTrack = g_FirstTrackSegment;
+ int newCount = g_CurrentTrackList.GetCount();
+
+ // Put entire new current segment list in BOARD
+ TRACK* track;
+ TRACK* insertBeforeMe = g_CurrentTrackSegment->GetBestInsertPoint( pcbframe->GetBoard() );
+
+ while( ( track = g_CurrentTrackList.PopFront() ) != NULL )
+ {
+ ITEM_PICKER picker( track, UR_NEW );
+ s_ItemsListPicker.PushItem( picker );
+ pcbframe->GetBoard()->m_Track.Insert( track, insertBeforeMe );
+ }
+
+ DrawTraces( panel, DC, firstTrack, newCount, GR_OR );
+
+ pcbframe->TestNetConnection( DC, netcode );
+
+ screen->SetModify();
+}