1344 lines
42 KiB
C++
1344 lines
42 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 <pcbcommon.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
|
|
* Return:
|
|
* 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()->m_NetClasses.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 = 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 );
|
|
}
|
|
|
|
pt_cur_ch = pt_cur_ch;
|
|
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;
|
|
int padLayerMaskStart; /* Mask layers belonging to the starting pad. */
|
|
int padLayerMaskEnd; /* Mask layers belonging to the ending pad. */
|
|
int topLayerMask = GetLayerMask( Route_Layer_TOP );
|
|
int bottomLayerMask = GetLayerMask( Route_Layer_BOTTOM );
|
|
int routeLayerMask; /* Mask two layers for routing. */
|
|
int tab_mask[2]; /* Enables the calculation of the mask layer being
|
|
* tested. (side = TOP or BOTTOM) */
|
|
int start_mask_layer = 0;
|
|
wxString msg;
|
|
|
|
wxBusyCursor dummy_cursor; // Set an hourglass cursor while routing a
|
|
// track
|
|
|
|
result = NOSUCCESS;
|
|
|
|
marge = s_Clearance + ( pcbframe->GetBoard()->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->GetLayerMask();
|
|
|
|
padLayerMaskEnd = pt_cur_ch->m_PadEnd->GetLayerMask();
|
|
|
|
|
|
/* 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 )
|
|
EXCHG( 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 )
|
|
EXCHG( 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 &
|
|
g_TabAllCopperLayerMask[pcbframe->GetBoard()->GetCopperLayerCount() - 1] ) )
|
|
{
|
|
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 )
|
|
{
|
|
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 )
|
|
{
|
|
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 )
|
|
{
|
|
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 )
|
|
{
|
|
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 )
|
|
{
|
|
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) )
|
|
{
|
|
/* 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 )
|
|
{
|
|
int dx0, dy0, dx1, dy1;
|
|
TRACK* newTrack;
|
|
|
|
if( orient == HOLE ) // placement of a via
|
|
{
|
|
newTrack = new SEGVIA( pcb );
|
|
|
|
g_CurrentTrackList.PushBack( newTrack );
|
|
|
|
g_CurrentTrackSegment->SetState( TRACK_AR, ON );
|
|
g_CurrentTrackSegment->SetLayer( 0x0F );
|
|
|
|
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->GetCurrentViaSize() );
|
|
g_CurrentTrackSegment->SetShape( pcb->GetDesignSettings().m_CurrentViaType );
|
|
|
|
g_CurrentTrackSegment->SetNet( current_net_code );
|
|
}
|
|
else // placement of a standard segment
|
|
{
|
|
newTrack = new TRACK( pcb );
|
|
|
|
g_CurrentTrackList.PushBack( newTrack );
|
|
|
|
g_CurrentTrackSegment->SetLayer( Route_Layer_BOTTOM );
|
|
|
|
if( side == TOP )
|
|
g_CurrentTrackSegment->SetLayer( Route_Layer_TOP );
|
|
|
|
g_CurrentTrackSegment->SetState( TRACK_AR, ON );
|
|
g_CurrentTrackSegment->SetEnd( wxPoint( pcb->GetBoundingBox().GetX() +
|
|
( RoutingMatrix.m_GridRouting * row ),
|
|
pcb->GetBoundingBox().GetY() +
|
|
( RoutingMatrix.m_GridRouting * col )));
|
|
g_CurrentTrackSegment->SetNet( 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->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->GetBoard()->GetCurrentTrackWidth() / 2 );
|
|
via_marge = s_Clearance + ( pcbframe->GetBoard()->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, FLG_START );
|
|
|
|
if( g_FirstTrackSegment->start )
|
|
g_FirstTrackSegment->SetState( BEGIN_ONPAD, ON );
|
|
|
|
g_CurrentTrackSegment->end = pcbframe->GetBoard()->GetPad( g_CurrentTrackSegment, FLG_END );
|
|
|
|
if( g_CurrentTrackSegment->end )
|
|
g_CurrentTrackSegment->SetState( END_ONPAD, ON );
|
|
|
|
/* 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->GetNet();
|
|
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();
|
|
}
|