kicad/pcbnew/solve.cpp

1074 lines
37 KiB
C++

/***************************************/
/* AUTOROUTAGE PCB : routine de calcul */
/***************************************/
/* fichier SOLVE.Cpp */
#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "pcbnew.h"
#include "autorout.h"
#include "zones.h"
#include "protos.h"
#include <fcntl.h>
#include "cell.h"
/* Routines definies ici : */
static int Route_1_Trace( WinEDA_PcbFrame* pcbframe, wxDC* DC, int two_sides, int row_source,
int col_source,
int row_target, int col_target, CHEVELU* pt_chevelu );
static int Retrace( WinEDA_PcbFrame* 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 Place_Piste_en_Buffer( WinEDA_PcbFrame* pcbframe, wxDC* DC );
/* Variables locales : */
static int segm_oX, segm_oY;
static int segm_fX, segm_fY; /* Origine et fin de la piste en cours de trace */
static CHEVELU* pt_cur_ch;
static int Ncurrent; /* measures of progress */
#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 des coord
* [][0] = row, []{1] = col a ajouter aux coord du point central pour
* obtenir les coord des 8 points voisins */
static 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 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
};
/********************************************************/
/* int WinEDA_PcbFrame::Solve(wxDC * DC, int two_sides) */
/********************************************************/
/* route all traces
* Return: 1 si OK
* -1 si Escape (arret en cours de routage) demande
* -2 si defaut alloc memoire
*/
int WinEDA_PcbFrame::Solve( wxDC* DC, int two_sides )
{
int current_net_code;
int row_source, col_source, row_target, col_target;
int success, nbsucces = 0, nbunsucces = 0;
EQUIPOT* pt_equipot;
bool stop = FALSE;
wxString msg;
DrawPanel->m_AbortRequest = FALSE;
DrawPanel->m_AbortEnable = TRUE;
Ncurrent = 0;
MsgPanel->EraseMsgBox();
msg.Printf( wxT( "%d " ), m_Pcb->m_NbNoconnect );
Affiche_1_Parametre( this, 72, wxT( "NoConn" ), msg, CYAN );
/* go until no more work to do */
GetWork( &row_source, &col_source, &current_net_code,
&row_target, &col_target, &pt_cur_ch ); // 1er chevelu a router
for( ; row_source != ILLEGAL; GetWork( &row_source, &col_source,
&current_net_code, &row_target, &col_target,
&pt_cur_ch ) )
{
/* Tst demande d'arret de routage ( key ESCAPE actionnee ) */
wxYield();
if( DrawPanel->m_AbortRequest )
{
if( IsOK( this, _( "Abort routing?" ) ) )
{
success = STOP_FROM_ESC;
stop = TRUE;
break;
}
else
DrawPanel->m_AbortRequest = 0;
}
Ncurrent++;
pt_equipot = m_Pcb->FindNet( current_net_code );
if( pt_equipot )
{
msg.Printf( wxT( "[%8.8s]" ), pt_equipot->m_Netname.GetData() );
Affiche_1_Parametre( this, 1, wxT( "Net route" ), msg, BROWN );
msg.Printf( wxT( "%d / %d" ), Ncurrent, Ntotal );
Affiche_1_Parametre( this, 12, wxT( "Activity" ), msg, BROWN );
}
pt_cur_ch = pt_cur_ch;
segm_oX = m_Pcb->m_BoundaryBox.m_Pos.x + (g_GridRoutingSize * col_source);
segm_oY = m_Pcb->m_BoundaryBox.m_Pos.y + (g_GridRoutingSize * row_source);
segm_fX = m_Pcb->m_BoundaryBox.m_Pos.x + (g_GridRoutingSize * col_target);
segm_fY = m_Pcb->m_BoundaryBox.m_Pos.y + (g_GridRoutingSize * row_target);
/* Affiche Liaison */
GRLine( &DrawPanel->m_ClipBox, DC, segm_oX, segm_oY, segm_fX, segm_fY, 0, WHITE | GR_XOR );
pt_cur_ch->pad_start->Draw( DrawPanel, DC, GR_OR | GR_SURBRILL );
pt_cur_ch->pad_end->Draw( DrawPanel, DC, GR_OR | GR_SURBRILL );
success = Route_1_Trace( this, DC, two_sides, row_source, col_source,
row_target, col_target, pt_cur_ch );
switch( success )
{
case NOSUCCESS:
pt_cur_ch->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 );
Affiche_1_Parametre( this, 22, wxT( "Ok" ), msg, GREEN );
msg.Printf( wxT( "%d " ), nbunsucces );
Affiche_1_Parametre( this, 30, wxT( "Fail" ), msg, RED );
msg.Printf( wxT( "%d " ), m_Pcb->m_NbNoconnect );
Affiche_1_Parametre( this, 38, wxT( "NoConn" ), msg, CYAN );
/* Effacement des affichages de routage sur l'ecran */
pt_cur_ch->pad_start->Draw( DrawPanel, DC, GR_AND );
pt_cur_ch->pad_end->Draw( DrawPanel, DC, GR_AND );
if( stop )
break;
}
DrawPanel->m_AbortEnable = FALSE;
return SUCCESS;
}
/**************************/
/* int Route_1_Trace(xxx) */
/**************************/
/* Route une piste du BOARD.
* Parametres:
* 1 face / 2 faces ( 0 / 1)
* coord source (row,col)
* coord destination (row,col)
* net_code
* pointeur sur le chevelu de reference
*
* Retourne :
* SUCCESS si route trouvee
* TRIVIAL_SUCCESS si pads connectes par superposition ( pas de piste a tirer)
* NOSUCCESS si echec
* STOP_FROM_ESC si Escape demande
* ERR_MEMORY defaut alloc RAM
*/
static int Route_1_Trace( WinEDA_PcbFrame* pcbframe, wxDC* DC,
int two_sides, int row_source, int col_source,
int row_target, int col_target, CHEVELU* pt_chevelu )
{
int r, c, side, d, apx_dist, nr, nc;
int result, skip;
int i;
LISTE_PAD* ptr;
long curcell, newcell, buddy, lastopen, lastclos, lastmove;
int newdist, olddir, _self;
int current_net_code;
int marge, via_marge;
int pad_masque_layer_s; /* Masque des couches appartenant au pad de depart */
int pad_masque_layer_e; /* Masque des couches appartenant au pad d'arrivee */
int masque_layer_TOP = g_TabOneLayerMask[Route_Layer_TOP];
int masque_layer_BOTTOM = g_TabOneLayerMask[Route_Layer_BOTTOM];
int masque_layers; /* Masque des 2 couches de routage */
int tab_mask[2]; /* permet le calcul du Masque de la couche en cours
* de tst (side = TOP ou BOTTOM)*/
int start_mask_layer = 0;
wxString msg;
result = NOSUCCESS;
marge = g_DesignSettings.m_TrackClearence + (g_DesignSettings.m_CurrentTrackWidth / 2);
via_marge = g_DesignSettings.m_TrackClearence + (g_DesignSettings.m_CurrentViaSize / 2);
/* clear direction flags */
i = Nrows * Ncols * sizeof(char);
memset( Board.m_DirSide[TOP], FROM_NOWHERE, i );
memset( Board.m_DirSide[BOTTOM], FROM_NOWHERE, i );
lastopen = lastclos = lastmove = 0;
/* Init tab_masque[side] pour tests de fin de routage */
tab_mask[TOP] = masque_layer_TOP;
tab_mask[BOTTOM] = masque_layer_BOTTOM;
/* Init masque des couches actives */
masque_layers = masque_layer_TOP | masque_layer_BOTTOM;
pt_cur_ch = pt_chevelu;
current_net_code = pt_chevelu->GetNet();
pad_masque_layer_s = pt_cur_ch->pad_start->m_Masque_Layer;
pad_masque_layer_e = pt_cur_ch->pad_end->m_Masque_Layer;
/* Test 1 Si routage possible c.a.d si les pads sont accessibles
* sur les couches de routage */
if( (masque_layers & pad_masque_layer_s) == 0 )
goto end_of_route;
if( (masque_layers & pad_masque_layer_e) == 0 )
goto end_of_route;
/* Test 2 Si routage possible c.a.d si les pads sont accessibles
* sur la grille de routage ( 1 point de grille doit etre dans le pad)*/
{
int cX = (g_GridRoutingSize * col_source) + pcbframe->m_Pcb->m_BoundaryBox.m_Pos.x;
int cY = (g_GridRoutingSize * row_source) + pcbframe->m_Pcb->m_BoundaryBox.m_Pos.y;
int dx = pt_cur_ch->pad_start->m_Size.x / 2;
int dy = pt_cur_ch->pad_start->m_Size.y / 2;
int px = pt_cur_ch->pad_start->GetPosition().x;
int py = pt_cur_ch->pad_start->GetPosition().y;
if( ( (pt_cur_ch->pad_start->m_Orient / 900) & 1 ) != 0 )
EXCHG( dx, dy );
if( (abs( cX - px ) > dx ) || (abs( cY - py ) > dy) )
goto end_of_route;
cX = (g_GridRoutingSize * col_target) + pcbframe->m_Pcb->m_BoundaryBox.m_Pos.x;
cY = (g_GridRoutingSize * row_target) + pcbframe->m_Pcb->m_BoundaryBox.m_Pos.y;
dx = pt_cur_ch->pad_end->m_Size.x / 2;
dy = pt_cur_ch->pad_end->m_Size.y / 2;
px = pt_cur_ch->pad_end->GetPosition().x;
py = pt_cur_ch->pad_end->GetPosition().y;
if( ( (pt_cur_ch->pad_end->m_Orient / 900) & 1 ) != 0 )
EXCHG( dx, dy );
if( (abs( cX - px ) > dx ) || (abs( cY - py ) > dy) )
goto end_of_route;
}
/* Test du cas trivial: connection directe par superposition des pads */
if( (row_source == row_target) && (col_source == col_target)
&& ( pad_masque_layer_e & pad_masque_layer_s &
g_TabAllCopperLayerMask[g_DesignSettings.m_CopperLayerCount - 1]) )
{
result = TRIVIAL_SUCCESS;
goto end_of_route;
}
/* Placement du bit de suppression d'obstacle relative aux 2 pads a relier */
pcbframe->Affiche_Message( wxT( "Gen Cells" ) );
Place_1_Pad_Board( pcbframe->m_Pcb, pt_cur_ch->pad_start, CURRENT_PAD, marge, WRITE_OR_CELL );
Place_1_Pad_Board( pcbframe->m_Pcb, pt_cur_ch->pad_end, CURRENT_PAD, marge, WRITE_OR_CELL );
/* Regenere les barrieres restantes (qui peuvent empieter sur le placement
* des bits precedents) */
ptr = (LISTE_PAD*) &pcbframe->m_Pcb->m_Pads[0];
i = pcbframe->m_Pcb->m_Pads.size();
for( ; i > 0; i--, ptr++ )
{
if( (pt_cur_ch->pad_start != *ptr) && (pt_cur_ch->pad_end != *ptr) )
{
Place_1_Pad_Board( pcbframe->m_Pcb, *ptr, ~CURRENT_PAD, marge, WRITE_AND_CELL );
}
}
InitQueue(); /* initialize the search queue */
apx_dist = GetApxDist( row_source, col_source, row_target, col_target );
/* Init 1ere recherche */
if( two_sides ) /* orientation preferentielle */
{
if( abs( row_target - row_source ) > abs( col_target - col_source ) )
{
if( pad_masque_layer_s & masque_layer_TOP )
{
start_mask_layer = 2;
if( SetQueue( row_source, col_source, TOP, 0, apx_dist,
row_target, col_target ) == 0 )
{
return ERR_MEMORY;
}
}
if( pad_masque_layer_s & masque_layer_BOTTOM )
{
start_mask_layer |= 1;
if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist,
row_target, col_target ) == 0 )
{
return ERR_MEMORY;
}
}
}
else
{
if( pad_masque_layer_s & masque_layer_BOTTOM )
{
start_mask_layer = 1;
if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist,
row_target, col_target ) == 0 )
{
return ERR_MEMORY;
}
}
if( pad_masque_layer_s & masque_layer_TOP )
{
start_mask_layer |= 2;
if( SetQueue( row_source, col_source, TOP, 0, apx_dist,
row_target, col_target ) == 0 )
{
return ERR_MEMORY;
}
}
}
}
else if( pad_masque_layer_s & masque_layer_BOTTOM )
{
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 = GetCell( r, c, side );
if( curcell & CURRENT_PAD )
curcell &= ~HOLE;
if( (r == row_target) && (c == col_target) /* success si layer OK */
&& ( tab_mask[side] & pad_masque_layer_e) )
{
/* Efface Liaison */
GRSetDrawMode( DC, GR_XOR );
GRLine( &pcbframe->DrawPanel->m_ClipBox,
DC,
segm_oX,
segm_oY,
segm_fX,
segm_fY,
0,
WHITE );
/* Generation de la trace */
if( Retrace( pcbframe, DC, row_source, col_source,
row_target, col_target, side, current_net_code ) )
{
result = SUCCESS; /* Success : Route OK */
}
break; /* Fin du routage */
}
if( pcbframe->DrawPanel->m_AbortRequest )
{
result = STOP_FROM_ESC; break;
}
_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 >= Nrows || nc < 0 || nc >= Ncols )
continue; /* off the edge */
if( _self == 5 && selfok2[i].present )
continue;
newcell = 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 = 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 = 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 = GetDir( r, c, side );
newdist = d + CalcDist( ndir[i], olddir,
(olddir == FROM_OTHERSIDE) ? 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( !GetDir( nr, nc, side ) )
{
SetDir( nr, nc, side, ndir[i] );
SetDist( nr, nc, side, newdist );
if( SetQueue( nr, nc, side, newdist,
GetApxDist( nr, nc, row_target, col_target ),
row_target, col_target ) == 0 )
{
return ERR_MEMORY;
}
}
else if( newdist < GetDist( nr, nc, side ) )
{
SetDir( nr, nc, side, ndir[i] );
SetDist( nr, nc, side, newdist );
ReSetQueue( nr, nc, side, newdist,
GetApxDist( nr, nc, row_target, col_target ),
row_target, col_target );
}
}
/** etude de l'autre couche **/
if( (two_sides) && !g_No_Via_Route )
{
olddir = 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 = 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 >= Nrows || nc < 0 || nc >= Ncols )
continue; /* off the edge !! */
if( GetCell( nr, nc, side ) /* & blocking2[i]*/ )
{
skip = 1; /* can't drill via here */
break;
}
if( 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 + CalcDist( FROM_OTHERSIDE, olddir, 0, side );
/* if (a) not visited yet,
* or (b) we have found a better path,
* add it to queue */
if( !GetDir( r, c, 1 - side ) )
{
SetDir( r, c, 1 - side, FROM_OTHERSIDE );
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 < GetDist( r, c, 1 - side ) )
{
SetDir( r, c, 1 - side, FROM_OTHERSIDE );
SetDist( r, c, 1 - side, newdist );
ReSetQueue( r, c, 1 - side, newdist, apx_dist, row_target, col_target );
}
} /* Fin de l'exploration de l'autre couche */
}
end_of_route:
Place_1_Pad_Board( pcbframe->m_Pcb, pt_cur_ch->pad_start, ~CURRENT_PAD, marge, WRITE_AND_CELL );
Place_1_Pad_Board( pcbframe->m_Pcb, pt_cur_ch->pad_end, ~CURRENT_PAD, marge, WRITE_AND_CELL );
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) }
};
/*****************************************************************/
/* int Retrace (COMMAND * Cmd, int row_source, int col_source */
/* int row_target, int col_target, int target_side, */
/* int current_net_code ) */
/*****************************************************************/
/* work from target back to source, actually laying the traces
* Parametres:
* start on side target_side, aux coordonnees row_target, col_target.
* arrivee sur side masque_layer_start, coord row_source, col_source
* La recherche se fait en sens inverse du routage,
* c.a.d du point d'arrivee (target) vers le point de depart (source)
* du routeur.
*
* target_side = cote (TOP / BOTTOM) de depart
* mask_layer_source = masque des couches d'arrivee
*
* Retourne:
* 0 si erreur
* > 0 si Ok
*/
static int Retrace( WinEDA_PcbFrame* 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, side d'ou on vient */
int r2, c2, s2; /* row, col, side ou on va */
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 = 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:
DisplayError( pcbframe, wxT( "Retrace: internal error: no way back" ) );
return 0;
}
if( r0 != ILLEGAL )
y = 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->m_Pcb, 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->m_Pcb, r1, c1, s1, b, current_net_code );
if( b & HOLE )
OrCell_Trace( pcbframe->m_Pcb, r2, c2, s2, HOLE, current_net_code );
}
else
{
DisplayError( pcbframe, 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:
DisplayError( pcbframe, wxT( "Retrace: error 3" ) );
return 0;
}
OrCell_Trace( pcbframe->m_Pcb, 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) ) );
Place_Piste_en_Buffer( pcbframe, DC );
return 1;
}
/*****************************************************************************/
static void OrCell_Trace( BOARD* pcb, int col, int row,
int side, int orient, int current_net_code )
/*****************************************************************************/
/* appelle la routine OrCell et place la piste reelle sur le pcb */
{
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( SEGM_AR, ON );
g_CurrentTrackSegment->SetLayer( 0x0F );
g_CurrentTrackSegment->m_Start.x =
g_CurrentTrackSegment->m_End.x = pcb->m_BoundaryBox.m_Pos.x + (g_GridRoutingSize * row);
g_CurrentTrackSegment->m_Start.y =
g_CurrentTrackSegment->m_End.y = pcb->m_BoundaryBox.m_Pos.y + (g_GridRoutingSize * col);
g_CurrentTrackSegment->m_Width = g_DesignSettings.m_CurrentViaSize;
g_CurrentTrackSegment->m_Shape = g_DesignSettings.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( SEGM_AR, ON );
g_CurrentTrackSegment->m_End.x = pcb->m_BoundaryBox.m_Pos.x + (g_GridRoutingSize * row);
g_CurrentTrackSegment->m_End.y = pcb->m_BoundaryBox.m_Pos.y + (g_GridRoutingSize * col);
g_CurrentTrackSegment->SetNet( current_net_code );
if( g_CurrentTrackSegment->Back() == NULL ) /* Start Piste */
{
g_CurrentTrackSegment->m_Start.x = segm_fX;
g_CurrentTrackSegment->m_Start.y = segm_fY;
/* Replacement sur le centre du pad si hors grille */
dx1 = g_CurrentTrackSegment->m_End.x - g_CurrentTrackSegment->m_Start.x;
dy1 = g_CurrentTrackSegment->m_End.y - g_CurrentTrackSegment->m_Start.y;
dx0 = pt_cur_ch->pad_end->GetPosition().x - g_CurrentTrackSegment->m_Start.x;
dy0 = pt_cur_ch->pad_end->GetPosition().y - g_CurrentTrackSegment->m_Start.y;
/* si aligne: modif du point origine */
if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) ) /* Alignes ! */
{
g_CurrentTrackSegment->m_Start = pt_cur_ch->pad_end->GetPosition();
}
else // Creation of a supplemental segment
{
g_CurrentTrackSegment->m_Start = pt_cur_ch->pad_end->GetPosition();
newTrack = g_CurrentTrackSegment->Copy();
newTrack->m_Start = g_CurrentTrackSegment->m_End;
g_CurrentTrackList.PushBack( newTrack );
}
}
else
{
if( g_CurrentTrackSegment->Back() )
{
g_CurrentTrackSegment->m_Start = g_CurrentTrackSegment->Back()->m_End;
}
}
g_CurrentTrackSegment->m_Width = g_DesignSettings.m_CurrentTrackWidth;
if( g_CurrentTrackSegment->m_Start != g_CurrentTrackSegment->m_End )
{
/* Reduction des segments alignes a 1 seul */
TRACK* oldTrack = g_CurrentTrackSegment->Back();
if( oldTrack && oldTrack->Type() != TYPE_VIA )
{
dx1 = g_CurrentTrackSegment->m_End.x - g_CurrentTrackSegment->m_Start.x;
dy1 = g_CurrentTrackSegment->m_End.y - g_CurrentTrackSegment->m_Start.y;
dx0 = oldTrack->m_End.x - oldTrack->m_Start.x;
dy0 = oldTrack->m_End.y - oldTrack->m_Start.y;
if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) ) /* le dernier segment est en ligne*/
{
oldTrack->m_End.x = g_CurrentTrackSegment->m_End.x;
oldTrack->m_End.y = g_CurrentTrackSegment->m_End.y;
delete g_CurrentTrackList.PopBack();
}
}
}
}
}
/*******************************************/
/* static void Place_Piste_en_Buffer() */
/*******************************************/
/* Insere la nouvelle piste creee dans la liste standard des pistes.
* Modifie les points de debut et fin de piste pour qu'ils soient relies
* au centre des pads corresponadants, meme hors grille
*/
static void Place_Piste_en_Buffer( WinEDA_PcbFrame* pcbframe, wxDC* DC )
{
int dx0, dy0, dx1, dy1;
int marge, via_marge;
WinEDA_DrawPanel* panel = pcbframe->DrawPanel;
marge = g_DesignSettings.m_TrackClearence + (g_DesignSettings.m_CurrentTrackWidth / 2);
via_marge = g_DesignSettings.m_TrackClearence + (g_DesignSettings.m_CurrentViaSize / 2);
/* tst point d'arrivee : doit etre sur pad start */
dx1 = g_CurrentTrackSegment->m_End.x - g_CurrentTrackSegment->m_Start.x;
dy1 = g_CurrentTrackSegment->m_End.y - g_CurrentTrackSegment->m_Start.y;
/* Replacement sur le centre du pad si hors grille */
dx0 = pt_cur_ch->pad_start->GetPosition().x - g_CurrentTrackSegment->m_Start.x;
dy0 = pt_cur_ch->pad_start->GetPosition().y - g_CurrentTrackSegment->m_Start.y;
/* si aligne: modif du point origine */
if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) ) /* Alignes ! */
{
g_CurrentTrackSegment->m_End = pt_cur_ch->pad_start->GetPosition();
}
else /* Creation d'un segment suppl raccord */
{
TRACK* newTrack = g_CurrentTrackSegment->Copy();
newTrack->m_End = pt_cur_ch->pad_start->GetPosition();
newTrack->m_Start = g_CurrentTrackSegment->m_End;
g_CurrentTrackList.PushBack( newTrack );
}
g_FirstTrackSegment->start = Locate_Pad_Connecte( pcbframe->m_Pcb, g_FirstTrackSegment, START );
if( g_FirstTrackSegment->start )
g_FirstTrackSegment->SetState( BEGIN_ONPAD, ON );
g_CurrentTrackSegment->end = Locate_Pad_Connecte( pcbframe->m_Pcb, g_CurrentTrackSegment, END );
if( g_CurrentTrackSegment->end )
g_CurrentTrackSegment->SetState( END_ONPAD, ON );
/* recherche de la zone de rangement et insertion de la nouvelle piste */
pcbframe->m_Pcb->Add( g_FirstTrackSegment );
Trace_Une_Piste( panel, DC, g_FirstTrackSegment, g_CurrentTrackList.GetCount(), GR_OR );
pcbframe->test_1_net_connexion( DC, g_FirstTrackSegment->GetNet() );
/* Trace de la forme exacte de la piste en BOARD */
for( TRACK* track = g_FirstTrackSegment; track; track = track->Next() )
{
TraceSegmentPcb( pcbframe->m_Pcb, track, HOLE, marge, WRITE_CELL );
TraceSegmentPcb( pcbframe->m_Pcb, track, VIA_IMPOSSIBLE, via_marge, WRITE_OR_CELL );
}
ActiveScreen->SetModify();
}