	/***************************************/
	/* 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 "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;
	};

static struct block blocking[8] = { /* blocking masks for diagonal traces */
	 {  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("%5.5d"),OpenNodes); \
				Affiche_1_Parametre(pcbframe, 24,wxT("Open"),msg,WHITE); \
				msg.Printf( wxT("%5.5d"),ClosNodes); \
				Affiche_1_Parametre(pcbframe, 32,wxT("Closed"),msg,WHITE);\
				msg.Printf( wxT("%5.5d"),MoveNodes); \
				Affiche_1_Parametre(pcbframe, 40,wxT("Moved"),msg,WHITE); \
				msg.Printf( wxT("%5.5d"),MaxNodes); \
				Affiche_1_Parametre(pcbframe, 48,wxT("Max"),msg,WHITE); \
				msg.Printf( wxT("%2.2d"),(ClosNodes*50)/(Nrows*Ncols) ); \
				Affiche_1_Parametre(pcbframe, 56, wxT("%"),msg,CYAN);


	/********************************************************/
	/* 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,LIGHTCYAN);


	/* 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 = GetEquipot(m_Pcb, 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,YELLOW);
			msg.Printf( wxT( "%d / %d"),Ncurrent, Ntotal);
			Affiche_1_Parametre(this, 12, wxT("Activity"), msg,YELLOW);
			}

		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, WHITE | GR_XOR);
		pt_cur_ch->pad_start->Draw(DrawPanel, DC, wxPoint(0,0), GR_OR | GR_SURBRILL);
		pt_cur_ch->pad_end->Draw(DrawPanel, DC, wxPoint(0,0), 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, 61, wxT("Ok"),msg,LIGHTGREEN);
		msg.Printf( wxT("%d  "),nbunsucces);
		Affiche_1_Parametre(this, 66, wxT("Fail"),msg,LIGHTRED);
		msg.Printf( wxT("%d  "),m_Pcb->m_NbNoconnect);
		Affiche_1_Parametre(this, 72, wxT("NoConn"),msg,LIGHTCYAN);

		/* Effacement des affichages de routage sur l'ecran */
		pt_cur_ch->pad_start->Draw(DrawPanel, DC, wxPoint(0,0), GR_AND);
		pt_cur_ch->pad_end->Draw(DrawPanel, DC,wxPoint(0,0), 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->m_NetCode;
	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->m_Pos.x;
	int py = pt_cur_ch->pad_start->m_Pos.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->m_Pos.x;
	py = pt_cur_ch->pad_end->m_Pos.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; i = pcbframe->m_Pcb->m_NbPads;
	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, 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 */
			}
		/* report every 300 new nodes or so */
		if( (OpenNodes-lastopen > 300) || (ClosNodes-lastclos > 300) || (MoveNodes - lastmove > 300))
			{
			lastopen = (OpenNodes/300)*300; lastclos = (ClosNodes/300)*300;
			lastmove = (MoveNodes/300)*300;

			if( pcbframe->DrawPanel->m_AbortRequest )
				{
				result = STOP_FROM_ESC; break;
				}
			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->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);

	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) }
	};

/*****************************************************************/
/* 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;

	g_FirstTrackSegment = g_CurrentTrackSegment = NULL;
	g_TrackSegmentCount = 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, *OldTrack;

	if(orient == HOLE)	/* Placement d'une VIA */
		{
		NewTrack = new SEGVIA(pcb);
		g_TrackSegmentCount++;
		NewTrack->Pback = g_CurrentTrackSegment;
		if( g_CurrentTrackSegment)	g_CurrentTrackSegment->Pnext = NewTrack;
		else g_FirstTrackSegment = NewTrack;

		g_CurrentTrackSegment = NewTrack;

		g_CurrentTrackSegment->SetState(SEGM_AR, ON);
		g_CurrentTrackSegment->m_Layer = 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->m_NetCode = current_net_code;
		}

	else	/* Placement d'un segment standard */
		{
		NewTrack = new TRACK(pcb);
		g_TrackSegmentCount++;
		NewTrack->Pback = g_CurrentTrackSegment;
		if( g_CurrentTrackSegment)	g_CurrentTrackSegment->Pnext = NewTrack;
		else g_FirstTrackSegment = NewTrack;

		g_CurrentTrackSegment = NewTrack;

		g_CurrentTrackSegment->m_Layer = Route_Layer_BOTTOM;
		if (side == TOP) g_CurrentTrackSegment->m_Layer = 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->m_NetCode = current_net_code;

		if ( g_CurrentTrackSegment->Pback == 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->m_Pos.x - g_CurrentTrackSegment->m_Start.x;
			dy0 = pt_cur_ch->pad_end->m_Pos.y - g_CurrentTrackSegment->m_Start.y;

			/* si aligne: modif du point origine */
			if(abs(dx0*dy1) == abs(dx1*dy0) ) /* Alignes ! */
				{
				g_CurrentTrackSegment->m_Start.x = pt_cur_ch->pad_end->m_Pos.x;
				g_CurrentTrackSegment->m_Start.y = pt_cur_ch->pad_end->m_Pos.y;
				}

			else /* Creation d'un segment suppl raccord */
				{
				NewTrack = g_CurrentTrackSegment->Copy();
				g_TrackSegmentCount++;
				NewTrack->Insert(pcb, g_CurrentTrackSegment);

				g_CurrentTrackSegment->m_Start.x = pt_cur_ch->pad_end->m_Pos.x;
				g_CurrentTrackSegment->m_Start.y = pt_cur_ch->pad_end->m_Pos.y;
				NewTrack->m_Start.x = g_CurrentTrackSegment->m_End.x;
				NewTrack->m_Start.y = g_CurrentTrackSegment->m_End.y;

		 		g_CurrentTrackSegment = NewTrack;
				}
			}
		else
			{
			if ( g_CurrentTrackSegment->Pback )
				{
				g_CurrentTrackSegment->m_Start.x = ((TRACK*)g_CurrentTrackSegment->Pback)->m_End.x;
				g_CurrentTrackSegment->m_Start.y = ((TRACK*)g_CurrentTrackSegment->Pback)->m_End.y;
				}
			}
		g_CurrentTrackSegment->m_Width = g_DesignSettings.m_CurrentTrackWidth;

		if ( (g_CurrentTrackSegment->m_Start.x != g_CurrentTrackSegment->m_End.x) ||
			 (g_CurrentTrackSegment->m_Start.y != g_CurrentTrackSegment->m_End.y) )
			{
			/* Reduction des segments alignes a 1 seul */
			OldTrack = (TRACK*) g_CurrentTrackSegment->Pback;
			if ( OldTrack && (OldTrack->m_StructType != TYPEVIA) )
				{
				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_CurrentTrackSegment;
					g_CurrentTrackSegment = OldTrack;
					g_CurrentTrackSegment->Pnext = NULL;
					g_TrackSegmentCount--;
					}
				}
			}
		}
}

	/*******************************************/
	/* static void Place_Piste_en_Buffer(void) */
	/*******************************************/

/* 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)
{
TRACK* pt_track;
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->m_Pos.x - g_CurrentTrackSegment->m_Start.x;
	dy0 = pt_cur_ch->pad_start->m_Pos.y - g_CurrentTrackSegment->m_Start.y;

	/* si aligne: modif du point origine */
	if(abs(dx0*dy1) == abs(dx1*dy0) ) /* Alignes ! */
		{
		g_CurrentTrackSegment->m_End.x = pt_cur_ch->pad_start->m_Pos.x;
		g_CurrentTrackSegment->m_End.y = pt_cur_ch->pad_start->m_Pos.y;
		}
	else /* Creation d'un segment suppl raccord */
		{
		TRACK * NewTrack = g_CurrentTrackSegment->Copy();
		NewTrack->Insert(pcbframe->m_Pcb, g_CurrentTrackSegment);

		NewTrack->m_End.x = pt_cur_ch->pad_start->m_Pos.x;
		NewTrack->m_End.y = pt_cur_ch->pad_start->m_Pos.y;
		NewTrack->m_Start.x = g_CurrentTrackSegment->m_End.x;
		NewTrack->m_Start.y = g_CurrentTrackSegment->m_End.y;

		g_CurrentTrackSegment = NewTrack; g_TrackSegmentCount++;
		}


	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 */
	pt_track = g_FirstTrackSegment->GetBestInsertPoint(pcbframe->m_Pcb);
	g_FirstTrackSegment->Insert(pcbframe->m_Pcb, pt_track);

	Trace_Une_Piste(panel, DC, g_FirstTrackSegment,g_TrackSegmentCount,GR_OR) ;

	pcbframe->test_1_net_connexion(DC, g_FirstTrackSegment->m_NetCode );

	/* Trace de la forme exacte de la piste en BOARD */
	for ( pt_track = g_FirstTrackSegment; ; pt_track = (TRACK*)pt_track->Pnext)
		{
		TraceSegmentPcb(pcbframe->m_Pcb,pt_track,HOLE,marge,WRITE_CELL);
		TraceSegmentPcb(pcbframe->m_Pcb,pt_track,VIA_IMPOSSIBLE,via_marge,WRITE_OR_CELL);
		if(pt_track == g_CurrentTrackSegment ) break;
		}

	ActiveScreen->SetModify();
}

