kicad/pcbnew/autorouter/solve.cpp

1333 lines
41 KiB
C++

/*
* 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( AUTOROUTER_CONTEXT& ctx,
int two_sides,
int row_source,
int col_source,
int row_target,
int col_target,
RATSNEST_ITEM* pt_rat );
static int Retrace( AUTOROUTER_CONTEXT& ctx,
int,
int,
int,
int,
int,
int net_code );
static void OrCell_Trace( AUTOROUTER_CONTEXT& ctx,
int col,
int row,
int side,
int orient,
int current_net_code );
static void AddNewTrace( AUTOROUTER_CONTEXT& ctx );
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( AUTOROUTER_CONTEXT& aCtx, 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 = aCtx.board->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 = aCtx.board->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 = aCtx.bbox.GetX() + ( RoutingMatrix.m_GridRouting * col_source );
segm_oY = aCtx.bbox.GetY() + ( RoutingMatrix.m_GridRouting * row_source );
segm_fX = aCtx.bbox.GetX() + ( RoutingMatrix.m_GridRouting * col_target );
segm_fY = aCtx.bbox.GetY() + ( RoutingMatrix.m_GridRouting * row_target );
// Draw segment.
GRLine( m_canvas->GetClipBox(), aCtx.dc,
segm_oX, segm_oY, segm_fX, segm_fY,
0, WHITE );
pt_cur_ch->m_PadStart->Draw( m_canvas, aCtx.dc, GR_OR | GR_HIGHLIGHT );
pt_cur_ch->m_PadEnd->Draw( m_canvas, aCtx.dc, GR_OR | GR_HIGHLIGHT );
success = Autoroute_One_Track( aCtx, 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" ), aCtx.board->GetUnconnectedNetCount() );
AppendMsgPanel( wxT( "Not Connected" ), msg, CYAN );
// Delete routing from display.
pt_cur_ch->m_PadStart->Draw( m_canvas, aCtx.dc, GR_AND );
pt_cur_ch->m_PadEnd->Draw( m_canvas, aCtx.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( AUTOROUTER_CONTEXT& ctx,
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( ctx.board->GetCopperLayerCount() );
wxBusyCursor dummy_cursor; // Set an hourglass cursor while routing a
// track
result = NOSUCCESS;
marge = s_Clearance + ( ctx.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 ) + ctx.bbox.GetX();
int cY = ( RoutingMatrix.m_GridRouting * row_source ) + ctx.bbox.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 ) + ctx.bbox.GetX();
cY = ( RoutingMatrix.m_GridRouting * row_target ) + ctx.bbox.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.
ctx.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 = ctx.board->GetPadCount();
for( unsigned ii = 0; ii < ctx.board->GetPadCount(); ii++ )
{
D_PAD* ptr = ctx.board->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( ctx.dc, GR_XOR );
GRLine( ctx.pcbframe->GetCanvas()->GetClipBox(),
ctx.dc,
segm_oX, segm_oY,
segm_fX, segm_fY,
0,
WHITE );
// Generate trace.
if( Retrace( ctx, row_source, col_source,
row_target, col_target, side, current_net_code ) )
{
result = SUCCESS; // Success : Route OK
}
break; // Routing complete.
}
if( ctx.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 );
ctx.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 );
ctx.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( AUTOROUTER_CONTEXT& ctx,
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( ctx.pcbframe, wxT( "Retrace: error 1" ) );
return 0;
}
OrCell_Trace( ctx, 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( ctx, r1, c1, s1, b, current_net_code );
if( b & HOLE )
OrCell_Trace( ctx, 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( ctx, 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( ctx );
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( AUTOROUTER_CONTEXT& ctx, int col, int row,
int side, int orient, int current_net_code )
{
if( orient == HOLE ) // placement of a via
{
VIA* newVia = new VIA( ctx.board );
g_CurrentTrackList.PushBack( newVia );
g_CurrentTrackSegment->SetState( TRACK_AR, true );
g_CurrentTrackSegment->SetLayer( F_Cu );
g_CurrentTrackSegment->SetStart( wxPoint( ctx.bbox.GetX() + RoutingMatrix.m_GridRouting * row,
ctx.bbox.GetY() + RoutingMatrix.m_GridRouting * col ) );
g_CurrentTrackSegment->SetEnd( g_CurrentTrackSegment->GetStart() );
g_CurrentTrackSegment->SetWidth( ctx.board->GetDesignSettings().GetCurrentViaSize() );
newVia->SetViaType( ctx.board->GetDesignSettings().m_CurrentViaType );
g_CurrentTrackSegment->SetNetCode( current_net_code );
}
else // placement of a standard segment
{
TRACK* newTrack = new TRACK( ctx.board );
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( ctx.bbox.GetX() + RoutingMatrix.m_GridRouting * row,
ctx.bbox.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( ctx.board->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( AUTOROUTER_CONTEXT& ctx )
{
if( g_FirstTrackSegment == NULL )
return;
int dx0, dy0, dx1, dy1;
int marge, via_marge;
EDA_DRAW_PANEL* panel = ctx.pcbframe->GetCanvas();
PCB_SCREEN* screen = ctx.pcbframe->GetScreen();
marge = s_Clearance + ( ctx.pcbframe->GetDesignSettings().GetCurrentTrackWidth() / 2 );
via_marge = s_Clearance + ( ctx.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 = ctx.board->GetPad( g_FirstTrackSegment,
ENDPOINT_START );
if( g_FirstTrackSegment->start )
g_FirstTrackSegment->SetState( BEGIN_ONPAD, true );
g_CurrentTrackSegment->end = ctx.board->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( ctx.board );
while( ( track = g_CurrentTrackList.PopFront() ) != NULL )
{
ITEM_PICKER picker( track, UR_NEW );
s_ItemsListPicker.PushItem( picker );
ctx.board->m_Track.Insert( track, insertBeforeMe );
}
DrawTraces( panel, ctx.dc, firstTrack, newCount, GR_OR );
ctx.pcbframe->TestNetConnection( ctx.dc, netcode );
screen->SetModify();
}