1333 lines
41 KiB
C++
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, ¤t_net_code,
|
|
&row_target, &col_target, &pt_cur_ch ); // First net to route.
|
|
|
|
for( ; row_source != ILLEGAL; GetWork( &row_source, &col_source,
|
|
¤t_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();
|
|
}
|