/***************************************/ /* AUTOROUTAGE PCB : routine de calcul */ /***************************************/ /* fichier SOLVE.Cpp */ #include "fctsys.h" #include "gr_basic.h" #include "common.h" #include "class_drawpanel.h" #include "confirm.h" #include "pcbnew.h" #include "autorout.h" #include "zones.h" #include "protos.h" #include #include "cell.h" /* Routines definies ici : */ static int Autoroute_One_Track( 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 }; /* Macro d'affichage de l'activite du routeur; */ #define AFFICHE_ACTIVITE_ROUTE \ msg.Printf( wxT("Activity: Open %d Closed %d Moved %d"), OpenNodes, ClosNodes, MoveNodes); \ pcbframe->Affiche_Message(msg); /********************************************************/ 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 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 " ), GetBoard()->m_NbNoconnect ); Affiche_1_Parametre( this, 72, wxT( "NoConn" ), msg, CYAN ); /* go until no more work to do */ GetWork( &row_source, &col_source, ¤t_net_code, &row_target, &col_target, &pt_cur_ch ); // 1er chevelu a router for( ; row_source != ILLEGAL; GetWork( &row_source, &col_source, ¤t_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 = GetBoard()->FindNet( current_net_code ); if( pt_equipot ) { msg.Printf( wxT( "[%8.8s]" ), pt_equipot->GetNetname().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 = GetBoard()->m_BoundaryBox.m_Pos.x + (g_GridRoutingSize * col_source); segm_oY = GetBoard()->m_BoundaryBox.m_Pos.y + (g_GridRoutingSize * row_source); segm_fX = GetBoard()->m_BoundaryBox.m_Pos.x + (g_GridRoutingSize * col_target); segm_fY = GetBoard()->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 = 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->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 " ), GetBoard()->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; } /**********************************************************************************/ static int Autoroute_One_Track( WinEDA_PcbFrame* pcbframe, wxDC* DC, int two_sides, int row_source, int col_source, int row_target, int col_target, CHEVELU* pt_chevelu ) /**********************************************************************************/ /* 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 */ { 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; wxBusyCursor dummy_cursor; // Set an hourglass cursor while routing a track 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->GetBoard()->m_BoundaryBox.m_Pos.x; int cY = (g_GridRoutingSize * row_source) + pcbframe->GetBoard()->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->GetBoard()->m_BoundaryBox.m_Pos.x; cY = (g_GridRoutingSize * row_target) + pcbframe->GetBoard()->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->GetBoard(), pt_cur_ch->pad_start, CURRENT_PAD, marge, WRITE_OR_CELL ); Place_1_Pad_Board( pcbframe->GetBoard(), 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->GetBoard()->m_Pads[0]; i = pcbframe->GetBoard()->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->GetBoard(), *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; } /* 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; AFFICHE_ACTIVITE_ROUTE; } _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->GetBoard(), pt_cur_ch->pad_start, ~CURRENT_PAD, marge, WRITE_AND_CELL ); Place_1_Pad_Board( pcbframe->GetBoard(), pt_cur_ch->pad_end, ~CURRENT_PAD, marge, WRITE_AND_CELL ); AFFICHE_ACTIVITE_ROUTE; 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) } }; /*******************************************************************/ 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 ) /*******************************************************************/ /* 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 */ { 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->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 { 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->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) ) ); 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 = g_CurrentTrackSegment->m_End; delete g_CurrentTrackList.PopBack(); } } } } } /***********************************************************************/ static void Place_Piste_en_Buffer( WinEDA_PcbFrame* pcbframe, wxDC* DC ) /***********************************************************************/ /* 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 */ { if( g_FirstTrackSegment == NULL ) return; 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->GetBoard(), g_FirstTrackSegment, START ); if( g_FirstTrackSegment->start ) g_FirstTrackSegment->SetState( BEGIN_ONPAD, ON ); g_CurrentTrackSegment->end = Locate_Pad_Connecte( pcbframe->GetBoard(), g_CurrentTrackSegment, 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( pcbframe->GetBoard(), track, HOLE, marge, WRITE_CELL ); TraceSegmentPcb( pcbframe->GetBoard(), 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 ) { pcbframe->GetBoard()->m_Track.Insert( track, insertBeforeMe ); } Trace_Une_Piste( panel, DC, firstTrack, newCount, GR_OR ); pcbframe->test_1_net_connexion( DC, netcode ); ActiveScreen->SetModify(); }