kicad/pcbnew/ratsnest.cpp

1254 lines
39 KiB
C++

/************************************/
/**** editeur de PCB ****/
/* traitement du Chevelu (Rastnest) */
/************************************/
/* Fichier RATSNEST.CPP */
#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "pcbnew.h"
#include "autorout.h"
#include "protos.h"
/* variables locales */
CHEVELU * g_pt_chevelu ;
CHEVELU * local_liste_chevelu; // adresse de base du buffer des chevelus locaux
int nb_local_chevelu; // nbr de links du module en deplacement
int nb_pads_ref; // nbr de nodes du module en deplacement
int nb_pads_externes; // nbr de pads connectes au module en deplacement
bool DisplayRastnestInProgress; // autorise affichage chevelu en cours de calcul
// de celui-ci
/******************************************************************************/
void WinEDA_BasePcbFrame::Compile_Ratsnest( wxDC * DC, bool display_status_pcb )
/******************************************************************************/
/*
Génère le chevelu complet de la carte.
Doit etre appelé APRES le calcul de connectivité
Doit etre appelé apres changement de structure de la carte (modif
de pads, de nets, de modules).
Si display_status_pcb : affichage des résultats en bas d'ecran
*/
{
wxString msg;
DisplayRastnestInProgress = TRUE;
/* construction de la liste des coordonnées des pastilles */
m_Pcb->m_Status_Pcb = 0; /* réinit total du calcul */
build_liste_pads() ;
MsgPanel->EraseMsgBox() ; /* effacement du bas d'ecran */
msg.Printf( wxT(" %d"),m_Pcb->m_NbPads) ;
Affiche_1_Parametre(this, 1, wxT("pads"), msg,RED) ;
msg.Printf( wxT(" %d"),m_Pcb->m_NbNets) ;
Affiche_1_Parametre(this, 8, wxT("Nets"), msg,CYAN) ;
reattribution_reference_piste(display_status_pcb);
Build_Board_Ratsnest(DC ); /* calcul du chevelu general */
test_connexions(DC); /* determine les blocks de pads connectés par
les pistes existantes */
Tst_Ratsnest(DC, 0 ); /* calcul du chevelu actif */
// Reaffichage des chevelus actifs
if( g_Show_Ratsnest ) DrawGeneralRatsnest( DC, 0 );
if ( display_status_pcb ) Affiche_Infos_Status_Pcb(this);
}
/*****************************************************************/
static int tri_par_net(LISTE_PAD * pt_ref, LISTE_PAD * pt_compare)
/****************************************************************/
/* routine utilisee par la foncion QSORT */
{
return( (*pt_ref)->m_NetCode - (*pt_compare)->m_NetCode );
}
/********************************************************/
static int sort_by_length(CHEVELU * ref, CHEVELU * compare)
/********************************************************/
/* routine de tri par longueur des chevelus utilisee par la foncion QSORT */
{
return( ref->dist - compare->dist );
}
/*****************************************************************************/
static int gen_rats_block_to_block(WinEDA_DrawPanel * DrawPanel, wxDC * DC,
LISTE_PAD * pt_liste_pad, LISTE_PAD * pt_limite, int * nblinks)
/*****************************************************************************/
/*
Routine utilisee par Build_Board_Ratsnest()
Routine generant le chevelu entre 2 blocks ( supposes du meme net )
la recherche est faite entre les pads du block 1 et les autres blocks
le block n ( n > 1 ) est alors connecte au block 1 par leur 2 pads
les plus proches.
Entree :
pt_chain_pad = adresse de debut de recherche
pt_limite = adresse de fin de recherche (borne non comprise)
Sortie:
liste des chevelus ( structures)
mise a jour de g_pt_chevelu a la 1ere case libre
Retourne:
nombre de blocks non connectes entre eux
*/
{
int dist_min, current_dist ;
int current_num_block = 1 ;
LISTE_PAD * pt_liste_pad_tmp,
* pt_liste_pad_aux,
* pt_liste_pad_block1 = NULL,
* pt_start_liste ;
pt_liste_pad_tmp = NULL ; dist_min = 0x7FFFFFFF;
pt_start_liste = pt_liste_pad ;
if ( DC ) GRSetDrawMode(DC, GR_XOR);
/* Recherche du pad le plus proche du block 1 */
for ( ; pt_liste_pad < pt_limite ; pt_liste_pad++ )
{
D_PAD * ref_pad = *pt_liste_pad;
if(ref_pad->m_logical_connexion != 1) continue ;
for ( pt_liste_pad_aux = pt_start_liste ; ;pt_liste_pad_aux++)
{
D_PAD * curr_pad = *pt_liste_pad_aux;
if(pt_liste_pad_aux >= pt_limite ) break ;
if(curr_pad->m_logical_connexion == 1) continue ;
/* Comparaison des distances des pastilles (calcul simplifie) */
current_dist =
abs( curr_pad->m_Pos.x - ref_pad->m_Pos.x)
+ abs( curr_pad->m_Pos.y - ref_pad->m_Pos.y) ;
if(dist_min > current_dist)
{
current_num_block = curr_pad->m_logical_connexion ;
dist_min = current_dist;
pt_liste_pad_tmp = pt_liste_pad_aux;
pt_liste_pad_block1 = pt_liste_pad;
}
}
}
if (current_num_block > 1) /* le block n a ete connecte au bloc 1 */
{
/* le block n est fondu avec le bloc 1 : */
for( pt_liste_pad = pt_start_liste; pt_liste_pad < pt_limite; pt_liste_pad++ )
{
if( (*pt_liste_pad)->m_logical_connexion == current_num_block)
(*pt_liste_pad)->m_logical_connexion = 1;
}
pt_liste_pad = pt_liste_pad_block1 ;
(*nblinks)++;
g_pt_chevelu->m_NetCode = (*pt_liste_pad)->m_NetCode;
g_pt_chevelu->status = CH_ACTIF|CH_VISIBLE;
g_pt_chevelu->dist = dist_min;
g_pt_chevelu->pad_start = *pt_liste_pad;
g_pt_chevelu->pad_end = *pt_liste_pad_tmp;
if( DisplayRastnestInProgress && DC )
GRLine(&DrawPanel->m_ClipBox, DC, g_pt_chevelu->pad_start->m_Pos.x,
g_pt_chevelu->pad_start->m_Pos.y,
g_pt_chevelu->pad_end->m_Pos.x,
g_pt_chevelu->pad_end->m_Pos.y,
g_DesignSettings.m_RatsnestColor) ;
g_pt_chevelu++ ;
}
return(current_num_block) ;
}
/*****************************************************************************/
static int gen_rats_pad_to_pad(WinEDA_DrawPanel * DrawPanel, wxDC * DC,
LISTE_PAD * pt_liste_pad,
LISTE_PAD * pt_limite,int current_num_block, int * nblinks)
/*****************************************************************************/
/*
Routine utilisee par Build_Board_Ratsnest()
Routine generant le chevelu entre 2 pads ( supposes du meme net )
la routine connecte 1 pad non deja connecte a un autre et crée donc un certqins nombre
de blocks de pads liées par un chevelu
Ces blocks sont donc constitués de 2 pads.
Entree :
pt_chain_pad = adresse de debut de recherche
pt_limite = adresse de fin de recherche (borne non comprise)
current_num_block = numero du dernier block de pads (constitué par les connexions
de pistes existantes
Sortie:
liste des chevelus ( structures)
mise a jour de g_pt_chevelu a la 1ere case libre
Retourne:
nombre de blocks crees (paquets de pads)
*/
{
int dist_min, current_dist ;
LISTE_PAD * pt_liste_pad_tmp;
LISTE_PAD * pt_liste_pad_aux ;
LISTE_PAD * pt_start_liste;
D_PAD * ref_pad, * pad;
pt_start_liste = pt_liste_pad ;
if ( DC ) GRSetDrawMode(DC, GR_XOR);
for ( ; pt_liste_pad < pt_limite ; pt_liste_pad++ )
{
ref_pad = *pt_liste_pad;
if(ref_pad->m_logical_connexion) continue; // Pad deja connecte
pt_liste_pad_tmp = NULL ; dist_min = 0x7FFFFFFF;
for ( pt_liste_pad_aux = pt_start_liste ; ;pt_liste_pad_aux++)
{
if (pt_liste_pad_aux >= pt_limite ) break ;
if(pt_liste_pad_aux == pt_liste_pad) continue ;
pad = *pt_liste_pad_aux;
/* Comparaison des distances des pastilles (calcul simplifie) */
current_dist =
abs( pad->m_Pos.x - ref_pad->m_Pos.x)
+ abs( pad->m_Pos.y - ref_pad->m_Pos.y);
if(dist_min > current_dist)
{
dist_min = current_dist;
pt_liste_pad_tmp = pt_liste_pad_aux;
}
}
if (pt_liste_pad_tmp != NULL)
{
pad = *pt_liste_pad_tmp;
/* Mise a jour du numero de block ( ou de sous graphe ) */
/* si aucun des 2 pads n'est deja connecte : creation d'un nouveau block */
if ( (pad->m_logical_connexion == 0) && (ref_pad->m_logical_connexion == 0) )
{
current_num_block++ ;
pad->m_logical_connexion = current_num_block ;
ref_pad->m_logical_connexion = current_num_block ;
}
/* si 1 des 2 pads est deja connecte : mise a jour pour l'autre */
else
{
ref_pad->m_logical_connexion = pad->m_logical_connexion;
}
(*nblinks)++;
g_pt_chevelu->m_NetCode = ref_pad->m_NetCode;
g_pt_chevelu->status = CH_ACTIF|CH_VISIBLE;
g_pt_chevelu->dist = dist_min;
g_pt_chevelu->pad_start = ref_pad;
g_pt_chevelu->pad_end = pad;
if(DisplayRastnestInProgress)
{
GRLine(&DrawPanel->m_ClipBox, DC, g_pt_chevelu->pad_start->m_Pos.x,
g_pt_chevelu->pad_start->m_Pos.y,
g_pt_chevelu->pad_end->m_Pos.x,
g_pt_chevelu->pad_end->m_Pos.y,
g_DesignSettings.m_RatsnestColor);
}
g_pt_chevelu++ ;
}
}
return(current_num_block) ;
}
/***********************************************************/
void WinEDA_BasePcbFrame::Build_Board_Ratsnest( wxDC * DC )
/***********************************************************/
/* Routine de calcul du chevelu complet du circuit (algorithme de LEE )
les connexions physiques (pistes) ne sont pas ici prises en compte
Il s'agit donc du chevelu de base qui ne depend que de la disposition des pads.
- etablit la liste complete des pads si nécessaire
les pads utiles (cad appartenant a un net ) sont appeles
nodes (noeuds)
et la trie par blocs de pads homogenes ( i.e. appartenant au meme net )
- puis calcule le ratsnest selon l'algorithme de LEE, consistant a
a - etablir le ratsnest entre 1 pad non "connecte" et son plus proche
voisin. Ceci cree des "blocks" ou sous graphes non connectes entre
eux
b - "connecter" (par un chevelu) entre eux ces blocks en prenant le 1er block et
en le connectant a son plus proche voisin par les 2 pads les plus
proches (Iteration jusqu'a ce qu'il n'y ait plus qu'un seul block).
les chevelus calculés montrent les connexions "logiques"
Entree = adr du buffer de classement (usuellement buf_work)
met a jour :
nb_nodes = nombre de pads connectes a un net ( pads "utiles")
nb_links = nombre min de liens :
il y a n-1 liens par equipotentielle comportant n pads.
*/
{
LISTE_PAD * pt_liste_pad, * pt_start_liste, * pt_end_liste, * pt_liste_pad_limite;
D_PAD * pad;
int ii, num_block, nbpads;
CHEVELU * pt_deb_liste_ch;
int current_net_code, noconn;
EQUIPOT * equipot;
m_Pcb->m_NbNoconnect = 0;
m_Pcb->m_NbLinks = 0 ;
if( m_Pcb->m_NbPads == 0 ) return;
/* Etablissement de la liste des pads et leur net_codes si necessaire */
if( (m_Pcb->m_Status_Pcb & NET_CODES_OK) == 0 ) recalcule_pad_net_code();
pt_liste_pad = m_Pcb->m_Pads;
for( ii = m_Pcb->m_NbPads ; ii > 0 ; pt_liste_pad++, ii-- )
{
pad = *pt_liste_pad;
pad->m_logical_connexion = 0;
}
/* classement des pointeurs sur pads par nets */
qsort(m_Pcb->m_Pads,m_Pcb->m_NbPads,sizeof(LISTE_PAD),
(int(*)(const void *, const void *))tri_par_net) ;
/* Allocation memoire du buffer des chevelus: il y a nb_nodes - 1 chevelu
au maximum ( 1 node = 1 pad connecte ).
on alloue donc un buffer pour nb_nodes chevelus... (+ une petite marge)
le nombre reel de chevelus est nb_links
*/
if( m_Pcb->m_Ratsnest ) MyFree(m_Pcb->m_Ratsnest);
m_Pcb->m_Ratsnest = NULL;
if( m_Pcb->m_NbNodes == 0 ) return; /* pas de connexions utiles */
m_Pcb->m_Ratsnest = (CHEVELU*)MyZMalloc( (m_Pcb->m_NbNodes + 10 ) * sizeof(CHEVELU) );
if(m_Pcb->m_Ratsnest == NULL) return;
/* calcul du chevelu */
DisplayRastnestInProgress = TRUE;
g_pt_chevelu = m_Pcb->m_Ratsnest ;
pt_liste_pad = pt_start_liste = m_Pcb->m_Pads;
pt_liste_pad_limite = pt_start_liste + m_Pcb->m_NbPads;
current_net_code = 1; // 1er net_code a analyser (net_code = 0 -> no connect)
equipot = m_Pcb->m_Equipots;
noconn = 0;
for ( ; pt_liste_pad < pt_liste_pad_limite ; )
{
pt_deb_liste_ch = g_pt_chevelu;
pad = *pt_liste_pad;
/* saut des pads non connectes */
if(pad->m_NetCode == 0 )
{
pt_liste_pad++; pt_start_liste = pt_liste_pad;
continue;
}
/* Recherche de la fin de la liste des pads du net courant */
num_block = pad->m_logical_connexion;
nbpads = 0;
for( pt_end_liste = pt_liste_pad + 1 ; ; pt_end_liste++)
{
if (pt_end_liste >= pt_liste_pad_limite ) break ;
pad = *pt_end_liste;
if (pad->m_NetCode != current_net_code ) break ;
nbpads++;
if( num_block < pad->m_logical_connexion )
num_block = pad->m_logical_connexion;
}
m_Pcb->m_NbLinks += nbpads;
/* fin de liste trouvee: calcul du chevelu du net "net_code" */
equipot = GetEquipot(m_Pcb, current_net_code);
if(equipot == NULL)
DisplayError(this, wxT("Gen ratsnest err: NULL equipot") );
else
{
equipot->m_NbNodes = nbpads;
equipot->m_NbLink = nbpads+1;
equipot->m_PadzoneStart = pt_start_liste;
equipot->m_PadzoneEnd = pt_end_liste;
equipot->m_RatsnestStart = g_pt_chevelu;
}
/* a - connexion des pads entre eux */
ii = gen_rats_pad_to_pad(DrawPanel, DC, pt_start_liste,
pt_end_liste, num_block, &noconn);
/* b - connexion des blocks formes precedemment (Iteration) */
while (ii > 1 )
{
ii = gen_rats_block_to_block(DrawPanel, DC, pt_liste_pad,
pt_end_liste, &noconn);
}
if( equipot )
{
equipot->m_RatsnestEnd = g_pt_chevelu;
/* classement des chevelus par longueur croissante */
qsort(equipot->m_RatsnestStart,
equipot->m_RatsnestEnd - equipot->m_RatsnestStart,
sizeof(CHEVELU),
(int(*)(const void *, const void *))sort_by_length) ;
}
pt_liste_pad = pt_start_liste = pt_end_liste ;
pt_deb_liste_ch = g_pt_chevelu;
if(pt_start_liste < pt_liste_pad_limite)
current_net_code = (*pt_start_liste)->m_NetCode;
}
m_Pcb->m_NbNoconnect = noconn;
m_Pcb->m_Status_Pcb |= LISTE_CHEVELU_OK ;
adr_lowmem = buf_work;
// Effacement du chevelu calcule
CHEVELU * Chevelu = (CHEVELU*)m_Pcb->m_Ratsnest;
GRSetDrawMode(DC, GR_XOR);
for( ii = m_Pcb->GetNumRatsnests(); ii > 0; ii--, Chevelu++ )
{
if( ! g_Show_Ratsnest ) Chevelu->status &= ~CH_VISIBLE;
if ( DC ) GRLine(&DrawPanel->m_ClipBox, DC,
Chevelu->pad_start->m_Pos.x, Chevelu->pad_start->m_Pos.y,
Chevelu->pad_end->m_Pos.x, Chevelu->pad_end->m_Pos.y,
g_DesignSettings.m_RatsnestColor);
}
}
/**********************************************************************/
void WinEDA_BasePcbFrame::ReCompile_Ratsnest_After_Changes(wxDC * DC )
/**********************************************************************/
/* recompile rastnest afet am module move, delete, ..
*/
{
if ( g_Show_Ratsnest && DC ) Compile_Ratsnest( DC, TRUE );
}
/*********************************************************************/
void WinEDA_BasePcbFrame::DrawGeneralRatsnest(wxDC * DC, int net_code)
/*********************************************************************/
/*
Affiche le chevelu general du circuit
Affiche les chevelus dont le bit CH_VISIBLE du status du chevelu est a 1
Si net_code > 0, affichage des seuls chevelus de net_code correspondant
*/
{
int ii;
CHEVELU * Chevelu;
if( (m_Pcb->m_Status_Pcb & LISTE_CHEVELU_OK) == 0 ) return;
if( (m_Pcb->m_Status_Pcb & DO_NOT_SHOW_GENERAL_RASTNEST) ) return;
if ( DC == NULL ) return;
Chevelu = m_Pcb->m_Ratsnest;
if( Chevelu == NULL ) return;
GRSetDrawMode(DC, GR_XOR);
for( ii = m_Pcb->GetNumRatsnests() ;ii > 0; Chevelu++, ii--)
{
if( (Chevelu->status & (CH_VISIBLE|CH_ACTIF)) != (CH_VISIBLE|CH_ACTIF) )
continue;
if( (net_code <= 0) || (net_code == Chevelu->m_NetCode) )
{
GRLine(&DrawPanel->m_ClipBox, DC,
Chevelu->pad_start->m_Pos.x, Chevelu->pad_start->m_Pos.y,
Chevelu->pad_end->m_Pos.x, Chevelu->pad_end->m_Pos.y,
g_DesignSettings.m_RatsnestColor);
}
}
}
/*****************************************************************************/
static int tst_rats_block_to_block(WinEDA_DrawPanel * DrawPanel, wxDC * DC,
LISTE_PAD * pt_liste_pad_start, LISTE_PAD * pt_liste_pad_end,
CHEVELU* start_rat_list, CHEVELU* end_rat_list)
/*****************************************************************************/
/*
Routine utilisee par Tst_Ratsnest()
Routine tres proche de gen_rats_block_to_block(..)
Routine testant le chevelu entre 2 blocks ( supposes du meme net )
la recherche est faite entre les pads du block 1 et les autres blocks
le block n ( n > 1 ) est alors connecte au block 1 par le chevelu le plus court
A la différence de gen_rats_block_to_block(..),
l'analyse n'est pas faite pads a pads mais a travers la liste générale des chevelus.
La routine active alors le chevelu le plus court reliant le block 1 au block n
(etablissement d'une connexion "logique")
Entree :
pt_chain_pad = adresse de debut de zone pad utile
pt_limite = adresse de fin de zone (borne non comprise)
Sortie:
Membre .state du chevelu sélectionné
Retourne:
nombre de blocks non connectes entre eux
*/
{
int current_num_block, min_block ;
LISTE_PAD * pt_liste_pad;
CHEVELU * chevelu, * min_chevelu;
/* Recherche du chevelu le plus court d'un block a un autre block */
min_chevelu = NULL;
for ( chevelu = start_rat_list; chevelu < end_rat_list ; chevelu++ )
{
if( chevelu->pad_start->m_logical_connexion == chevelu->pad_end->m_logical_connexion )
continue ;
if( min_chevelu == NULL ) min_chevelu = chevelu;
else if(min_chevelu->dist > chevelu->dist) min_chevelu = chevelu;
}
if( min_chevelu == NULL ) return 1;
min_chevelu->status |= CH_ACTIF;
current_num_block = min_chevelu->pad_start->m_logical_connexion;
min_block = min_chevelu->pad_end->m_logical_connexion;
if (min_block > current_num_block ) EXCHG(min_block, current_num_block);
/* les 2 blocks vont etre fondus */
for( pt_liste_pad = pt_liste_pad_start; pt_liste_pad < pt_liste_pad_end; pt_liste_pad++ )
{
if( (*pt_liste_pad)->m_logical_connexion == current_num_block)
{
(*pt_liste_pad)->m_logical_connexion = min_block;
}
}
return(current_num_block) ;
}
/*********************************************************************/
static int tst_rats_pad_to_pad(WinEDA_DrawPanel * DrawPanel, wxDC * DC,
int current_num_block,
CHEVELU* start_rat_list, CHEVELU* end_rat_list)
/**********************************************************************/
/*
Routine utilisee par Tst_Ratsnest_general()
Routine Activant le chevelu entre 2 pads ( supposes du meme net )
la routine connecte 1 pad non deja connecte a un autre et active donc
un certain nombre de blocks de pads liées par un chevelu
Ces blocks sont donc constitués de 2 pads.
Entree :
pt_chain_pad = adresse de debut de zone pad
pt_limite = adresse de fin de recherche (borne non comprise)
current_num_block = numero du dernier block de pads (constitué par les connexions
de pistes existantes
Sortie:
liste des chevelus ( structures)
mise a jour du membre .state du chevelu activé
Retourne:
nombre de blocks crees (paquets de pads)
*/
{
D_PAD * pad_start, * pad_end;
CHEVELU * chevelu;
for ( chevelu = start_rat_list; chevelu < end_rat_list; chevelu++ )
{
pad_start = chevelu->pad_start; pad_end = chevelu->pad_end;
/* Mise a jour du numero de block ( ou de sous graphe ) */
/* si aucun des 2 pads n'est deja connecte : creation d'un nouveau block */
if ( (pad_start->m_logical_connexion == 0) && (pad_end->m_logical_connexion == 0) )
{
current_num_block++ ;
pad_start->m_logical_connexion = current_num_block ;
pad_end->m_logical_connexion = current_num_block;
chevelu->status |= CH_ACTIF;
}
/* si 1 des 2 pads est deja connecte : mise a jour pour l'autre */
else if ( pad_start->m_logical_connexion == 0)
{
pad_start->m_logical_connexion = pad_end->m_logical_connexion;
chevelu->status |= CH_ACTIF;
}
else if ( pad_end->m_logical_connexion == 0)
{
pad_end->m_logical_connexion = pad_start->m_logical_connexion;
chevelu->status |= CH_ACTIF;
}
}
return(current_num_block) ;
}
/******************************************************************/
void WinEDA_BasePcbFrame::Tst_Ratsnest(wxDC * DC, int ref_netcode )
/*******************************************************************/
/* calcul du chevelu actif
Le chevelu général doit etre calculé
Determite les chevelus ACTIFS dans la liste générale des chevelus
*/
{
LISTE_PAD * pt_liste_pad;
CHEVELU * chevelu;
D_PAD * pad;
int ii, num_block;
int net_code;
EQUIPOT * equipot;
if( m_Pcb->m_NbPads == 0 ) return;
for ( net_code = 1; ; net_code++)
{
equipot = GetEquipot(m_Pcb, net_code);
if(equipot == NULL ) break;
if( ref_netcode && (net_code != ref_netcode) ) continue;
num_block = 0;
pt_liste_pad = equipot->m_PadzoneStart;
for( ;pt_liste_pad < equipot->m_PadzoneEnd; pt_liste_pad++)
{
pad = *pt_liste_pad;
pad->m_logical_connexion = pad->m_physical_connexion;
num_block = max (num_block, pad->m_logical_connexion);
}
for ( chevelu = equipot->m_RatsnestStart; chevelu < equipot->m_RatsnestEnd; chevelu++ )
{
chevelu->status &= ~CH_ACTIF;
}
/* a - tst connexion des pads entre eux */
ii = tst_rats_pad_to_pad(DrawPanel, DC, num_block,
equipot->m_RatsnestStart, equipot->m_RatsnestEnd);
/* b - connexion des blocks formes precedemment (Iteration) */
while (ii > 1 )
{
ii = tst_rats_block_to_block(DrawPanel, DC,
equipot->m_PadzoneStart, equipot->m_PadzoneEnd,
equipot->m_RatsnestStart, equipot->m_RatsnestEnd);
}
}
m_Pcb->m_NbNoconnect = 0;
CHEVELU * Chevelu = m_Pcb->m_Ratsnest;
for( ii = m_Pcb->GetNumRatsnests(); ii > 0; ii--, Chevelu++ )
{
if( Chevelu->status & CH_ACTIF )
m_Pcb->m_NbNoconnect++;
}
}
/**************************************************************************/
int WinEDA_BasePcbFrame::Test_1_Net_Ratsnest(wxDC * DC, int ref_netcode)
/**************************************************************************/
/* Calcule le chevelu du net net_code */
{
DisplayRastnestInProgress = FALSE;
DrawGeneralRatsnest(DC, ref_netcode);
Tst_Ratsnest(DC, ref_netcode );
DrawGeneralRatsnest(DC, ref_netcode);
return m_Pcb->GetNumRatsnests();
}
/*****************************************************/
void WinEDA_BasePcbFrame::recalcule_pad_net_code(void)
/*****************************************************/
/*
Calcule et met a jour les net_codes des PADS et des equipotentielles
met a jour le buffer des equipotentielles
A utiliser apres edition de nets sur un pad ou lecture d'une netliste
positionne a 1 le bit NET_CODE_OK du status_pcb;
*/
{
LISTE_PAD * pad_ref, *pad_courant;
int ii, jj;
EQUIPOT * pt_equipot;
EDA_BaseStruct * PtStruct;
EQUIPOT ** BufPtEquipot;
/* construction de la liste des adr des PADS */
build_liste_pads();
/* calcul des net_codes des pads */
ii = m_Pcb->m_NbPads;
m_Pcb->m_NbNodes = 0;
m_Pcb->m_NbNets = 0;
pad_courant = m_Pcb->m_Pads;
for( ; ii > 0 ; pad_courant++, ii-- )
{
if( (*pad_courant)->m_Netname.IsEmpty()) // pad non connecte
{
(*pad_courant)->m_NetCode = 0 ; continue ;
}
m_Pcb->m_NbNodes++;
/* si le netname a deja ete rencontre: mise a jour , sinon nouveau net_code */
pad_ref = m_Pcb->m_Pads ;
while (pad_ref < pad_courant )
{
if( (*pad_ref)->m_Netname == (*pad_courant)->m_Netname )
break; // sont du meme met
pad_ref++ ;
}
/* si pad_ref = pad_courant: nouveau net sinon, deja net deja traite */
if ( pad_ref == pad_courant )
{
m_Pcb->m_NbNets++ ; (*pad_courant)->m_NetCode = m_Pcb->m_NbNets;
}
else (*pad_courant)->m_NetCode = (*pad_ref)->m_NetCode ;
}
/* Construction ou correction de la liste des equipotentielles,
et construction d'un tableau d'adressage des equipots*/
BufPtEquipot = (EQUIPOT**)MyMalloc(sizeof(EQUIPOT*) * (m_Pcb->m_NbNets+1));
pt_equipot = m_Pcb->m_Equipots;
PtStruct = (EDA_BaseStruct *) m_Pcb;
for( ii = 0 ; ii <= m_Pcb->m_NbNets ;ii++ )
{
if(pt_equipot == NULL ) /* Creation d'une nouvelle equipot */
{
pt_equipot = new EQUIPOT(m_Pcb);
if( ii == 0 )
{
m_Pcb->m_Equipots = pt_equipot;
pt_equipot->Pback = m_Pcb;
}
else
{
PtStruct->Pnext = pt_equipot;
pt_equipot->Pback = PtStruct;
}
pt_equipot->Pnext = NULL;
}
pt_equipot->m_NetCode = ii; // Mise a jour du numero d'equipot
pt_equipot->m_NbNodes = 0;
pt_equipot->m_Netname.Empty();
BufPtEquipot[ii] = pt_equipot;
PtStruct = (EDA_BaseStruct *) pt_equipot;
pt_equipot = (EQUIPOT *) pt_equipot->Pnext;
}
/* Effacement des equipots inutiles */
while ( pt_equipot )
{
PtStruct = pt_equipot->Pnext;
DeleteStructure(pt_equipot);
pt_equipot = (EQUIPOT*) PtStruct;
}
pad_courant = m_Pcb->m_Pads;
pt_equipot = m_Pcb->m_Equipots;
/* Placement des noms de net en structure EQUIPOT */
for( ii = m_Pcb->m_NbPads ; ii > 0 ; pad_courant++, ii-- )
{
jj = (*pad_courant)->m_NetCode;
pt_equipot = BufPtEquipot[jj];
pt_equipot->m_NbNodes ++;
if(pt_equipot->m_Netname.IsEmpty())
{
pt_equipot->m_Netname = (*pad_courant)->m_Netname;
}
}
MyFree(BufPtEquipot);
m_Pcb->m_Status_Pcb |= NET_CODES_OK;
}
/***********************************************/
void WinEDA_BasePcbFrame::build_liste_pads(void)
/***********************************************/
/*
construction de la liste ( sous forme d'une liste de stucture )
des caract utiles des pads du PCB pour autoroutage,DRC .. )
parametres:
adresse du buffer de classement = buf_work
retourne:
1ere adresse disponible si OK
NULL si trop de pastilles
Parametres de routage calcules et mis a jour
- parametre net_code:
numero de code interne de chaque net du PCB.
permet d'accelerer les calculs de chevelu et de connexions
- parametre .link est mis a jour
pour chaque pastille, il indique le nombre d'autres pastilles du meme net
appartenant au meme module.
Variables globales mise a jour:
pointeur base_adr_liste_pad (adr de classement de la liste des pads)
nb_pads = nombre total de pastilles du PCB
nb_nets = nombre de nets differents
status_pcb |= LISTE_PAD_OK (flag permettant d'eviter la reexecution inutile
de cette routine)
*/
{
LISTE_PAD* pt_liste_pad;
MODULE * Module;
D_PAD * PtPad;
if( m_Pcb->m_Status_Pcb & LISTE_PAD_OK ) return;
/* construction de la liste des pointeurs sur les structures D_PAD */
if( m_Pcb->m_Pads ) MyFree(m_Pcb->m_Pads);
m_Pcb->m_Pads = NULL;
/* Calcul du nombre de pads */
Module = m_Pcb->m_Modules; m_Pcb->m_NbPads = 0;
for( ; Module != NULL ; Module = (MODULE*) Module->Pnext)
{
PtPad = (D_PAD*) Module->m_Pads;
for(; PtPad != NULL; PtPad = (D_PAD*) PtPad->Pnext ) m_Pcb->m_NbPads++;
}
if( m_Pcb->m_NbPads == 0 ) return;
/* Allocation memoire du buffer */
pt_liste_pad = m_Pcb->m_Pads
= (D_PAD**) MyZMalloc( (m_Pcb->m_NbPads+1) * sizeof(D_PAD*) );
m_Pcb->m_NbNodes = 0;
/* Initialisation du buffer et des variables de travail */
Module = m_Pcb->m_Modules;
for( ; Module != NULL ; Module = (MODULE*) Module->Pnext)
{
PtPad = (D_PAD*) Module->m_Pads;
for(; PtPad != NULL; PtPad = (D_PAD*) PtPad->Pnext )
{
*pt_liste_pad = PtPad;
PtPad->m_logical_connexion = 0 ;
PtPad->m_Parent = Module;
if(PtPad->m_NetCode) m_Pcb->m_NbNodes++;
pt_liste_pad++;
}
}
*pt_liste_pad = NULL; // fin de liste
adr_lowmem = buf_work;
if( m_Pcb->m_Ratsnest ) MyFree(m_Pcb->m_Ratsnest);
m_Pcb->m_Ratsnest = NULL;
m_Pcb->m_Status_Pcb = LISTE_PAD_OK;
}
/*****************************************************************************/
char * WinEDA_BasePcbFrame::build_ratsnest_module(wxDC * DC, MODULE * Module)
/*****************************************************************************/
/*
construction de la liste en mode de calcul rapide pour affichage
en temps reel lors des deplacements du chevelu d'un module.
parametres d'appel:
Module = pointeur sur module dont le ratsnest est a calculer
retourne: adresse memoire disponible
Le chevelu calcule comporte 2 parties
- un chevelu interne relatif aux pads du module appartenant a un
meme net. Il est calcule 1 seule fois
- le chevelu externe reliant un pad interne a un pad externe au module
Ce chevelu est recalcule a chaque deplacement
*/
{
LISTE_PAD * pt_liste_pad,
* pt_liste_ref,
* pt_liste_generale;
D_PAD * pad_ref,
* pad_externe;
LISTE_PAD * pt_liste_pad_limite,
* pt_start_liste,
* pt_end_liste;
int ii, jj;
CHEVELU * local_chevelu;
static CHEVELU * pt_fin_int_chevelu; // pointeur sur la fin de la liste
// des chevelus internes au module
static int nb_int_chevelu; // nombre e chevelus internes
int current_net_code;
int increment, distance; // variables de calcul de ratsnest
int pad_pos_X, pad_pos_Y; // position reelle des pads du module en mouvement
if((m_Pcb->m_Status_Pcb & LISTE_PAD_OK) == 0 ) build_liste_pads();
/* construction de la liste des pads du module si necessaire */
if((m_Pcb->m_Status_Pcb & CHEVELU_LOCAL_OK) != 0) goto calcul_chevelu_ext;
/* calcul du chevelu "interne", c.a.d. liant les seuls pads du module */
pt_liste_pad = (LISTE_PAD*) adr_lowmem; nb_pads_ref = 0;
pad_ref = Module->m_Pads;
for( ; pad_ref != NULL ; pad_ref = (D_PAD*)pad_ref->Pnext)
{
if(pad_ref->m_NetCode == 0) continue;
*pt_liste_pad = pad_ref;
pad_ref->m_logical_connexion = 0;
pad_ref->m_physical_connexion = 0;
pt_liste_pad++; nb_pads_ref++;
}
if(nb_pads_ref == 0) return( (char*)pt_liste_pad); /* pas de connexions! */
qsort(adr_lowmem,nb_pads_ref,sizeof(D_PAD*),
(int(*)(const void *, const void *))tri_par_net) ;
/* construction de la liste des pads connectes aux pads de ce module */
DisplayRastnestInProgress = FALSE;
pt_liste_ref = (LISTE_PAD*) adr_lowmem;
nb_pads_externes = 0; current_net_code = 0;
for(ii = 0 ; ii < nb_pads_ref ; ii++)
{
pad_ref = pt_liste_ref[ii];
if(pad_ref->m_NetCode == current_net_code) continue;
current_net_code = pad_ref->m_NetCode;
pt_liste_generale = m_Pcb->m_Pads ;
for(jj = m_Pcb->m_NbPads ; jj > 0; jj--)
{
pad_externe = *pt_liste_generale ; pt_liste_generale++;
if(pad_externe->m_NetCode != current_net_code) continue;
if(pad_externe->m_Parent == Module) continue;
pad_externe->m_logical_connexion = 0;
pad_externe->m_physical_connexion = 0;
*pt_liste_pad = pad_externe ; pt_liste_pad++;
nb_pads_externes++;
}
}
/* tri par net_codes croissants de la liste des pads externes */
qsort(pt_liste_ref+nb_pads_ref,nb_pads_externes,sizeof(D_PAD*),
(int(*)(const void *, const void *))tri_par_net) ;
/* calcul du chevelu interne au module:
Ce calcul est identique au calcul du chevelu general, mais il est
restreint aux seuls pads du module courant */
local_liste_chevelu = (CHEVELU*)(pt_liste_pad); // buffer chevelu a la suite de la liste des pads
nb_local_chevelu = 0;
pt_liste_ref = (LISTE_PAD*) adr_lowmem;
g_pt_chevelu = local_liste_chevelu ;
pt_liste_pad = pt_start_liste = (LISTE_PAD *) adr_lowmem ;
pt_liste_pad_limite = pt_liste_pad + nb_pads_ref;
current_net_code = (*pt_liste_pad)->m_NetCode ;
for ( ; pt_liste_pad < pt_liste_pad_limite ; )
{
/* Recherche de la fin de la liste des pads du net courant */
for( pt_end_liste = pt_liste_pad + 1 ; ; pt_end_liste++)
{
if (pt_end_liste >= pt_liste_pad_limite ) break ;
if ((*pt_end_liste)->m_NetCode != current_net_code ) break ;
}
/* fin de liste trouvee : */
/* a - connexion des pads entre eux */
ii = gen_rats_pad_to_pad(DrawPanel, DC, pt_start_liste,pt_end_liste,
0, &nb_local_chevelu ) ;
/* b - connexion des blocks formes precedemment (Iteration) */
while (ii > 1 )
{
ii = gen_rats_block_to_block(DrawPanel, DC, pt_liste_pad,
pt_end_liste, &nb_local_chevelu ) ;
}
pt_liste_pad = pt_start_liste = pt_end_liste ;
if(pt_start_liste < pt_liste_pad_limite)
current_net_code = (*pt_start_liste)->m_NetCode ;
}
pt_fin_int_chevelu = local_chevelu = g_pt_chevelu;
nb_int_chevelu = nb_local_chevelu;
/* Mise a 1 du flag LOCAL */
g_pt_chevelu = local_liste_chevelu;
while(g_pt_chevelu < pt_fin_int_chevelu)
{
g_pt_chevelu->status = LOCAL_CHEVELU; g_pt_chevelu++;
}
m_Pcb->m_Status_Pcb |= CHEVELU_LOCAL_OK;
/////////////////////////////////////////
// calcul du chevelu externe au module //
/////////////////////////////////////////
calcul_chevelu_ext:
/* Cette partie est executee a chaque deplacement du module: on calcule
pour chaque pad du module courant la + courte distance a un pad externe.
Pour chaque groupe de pad du module courant appartenant a un meme net,
on ne garde qu'un seul chevelu: le plus court.
*/
local_chevelu = pt_fin_int_chevelu;
nb_local_chevelu = nb_int_chevelu;
pt_liste_ref = (LISTE_PAD*) adr_lowmem;
pad_ref = *pt_liste_ref;
current_net_code = pad_ref->m_NetCode;
local_chevelu->dist = 0x7FFFFFFF;
local_chevelu->status = 0;
increment = 0;
for(ii = 0 ; ii < nb_pads_ref ; ii++)
{
pad_ref = *(pt_liste_ref+ii);
if(pad_ref->m_NetCode != current_net_code)
{ /* un nouveau chevelu est cree (si necessaire) pour
chaque nouveau net */
if(increment)
{
nb_local_chevelu++; local_chevelu++;
}
increment = 0;
current_net_code = pad_ref->m_NetCode;
local_chevelu->dist = 0x7FFFFFFF;
}
pad_pos_X = pad_ref->m_Pos.x - g_Offset_Module.x;
pad_pos_Y = pad_ref->m_Pos.y - g_Offset_Module.y;
pt_liste_generale = pt_liste_ref + nb_pads_ref ;
for(jj = nb_pads_externes ; jj > 0; jj--)
{
pad_externe = *pt_liste_generale ; pt_liste_generale++;
/* les netcodes doivent etre identiques */
if(pad_externe->m_NetCode < pad_ref->m_NetCode) continue;
if(pad_externe->m_NetCode > pad_ref->m_NetCode) break;
distance = abs(pad_externe->m_Pos.x - pad_pos_X) +
abs(pad_externe->m_Pos.y - pad_pos_Y);
if(distance < local_chevelu->dist)
{
local_chevelu->pad_start = pad_ref;
local_chevelu->pad_end = pad_externe;
local_chevelu->m_NetCode = pad_ref->m_NetCode;
local_chevelu->dist = distance;
local_chevelu->status = 0;
increment = 1;
}
}
}
if(increment) // fin de balayage : le ratsnest courant doit etre memorise
{
nb_local_chevelu++; local_chevelu++;
}
/* Retourne l'adr de la zone disponible */
adr_max = max(adr_max, (char*)(local_chevelu+1));
return( (char*) (local_chevelu+1) ) ; /* la struct pointee par
local_chevelu est utilisee
pour des calculs temporaires */
}
/***********************************************************/
void WinEDA_BasePcbFrame::trace_ratsnest_module(wxDC * DC)
/**********************************************************/
/*
affiche le chevelu d'un module calcule en mode rapide.
retourne: rien
*/
{
CHEVELU* local_chevelu;
int ii;
if((m_Pcb->m_Status_Pcb & CHEVELU_LOCAL_OK) == 0) return ;
local_chevelu = local_liste_chevelu;
ii = nb_local_chevelu;
GRSetDrawMode(DC, GR_XOR);
while( ii-- > 0 )
{
if(local_chevelu->status & LOCAL_CHEVELU)
{
GRLine(&DrawPanel->m_ClipBox, DC,
local_chevelu->pad_start->m_Pos.x - g_Offset_Module.x,
local_chevelu->pad_start->m_Pos.y - g_Offset_Module.y,
local_chevelu->pad_end->m_Pos.x - g_Offset_Module.x,
local_chevelu->pad_end->m_Pos.y - g_Offset_Module.y,
YELLOW);
}
else
{
GRLine(&DrawPanel->m_ClipBox, DC,
local_chevelu->pad_start->m_Pos.x - g_Offset_Module.x,
local_chevelu->pad_start->m_Pos.y - g_Offset_Module.y,
local_chevelu->pad_end->m_Pos.x,
local_chevelu->pad_end->m_Pos.y,
g_DesignSettings.m_RatsnestColor);
}
local_chevelu++;
}
}
/*********************************************************************************************/
/* int * WinEDA_BasePcbFrame::build_ratsnest_pad(D_PAD * pad_ref, const wxPoint & refpos) */
/*********************************************************************************************/
/*
construction de la liste en mode de calcul rapide pour affichage
en temps reel du chevelu d'un pad lors des tracés d'une piste démarrant
sur ce pad.
parametres d'appel:
pad_ref ( si null : mise a 0 du nombre de chevelus )
ox, oy = coord de l'extremite de la piste en trace
init (flag)
= 0 : mise a jour des chevelu
<> 0: creation de la liste
retourne: adresse memoire disponible
*/
/* routine locale de tri par longueur de links utilisee par la fonction QSORT */
static int sort_by_localnetlength(int * ref, int * compare)
{
int * org = (int*)adr_lowmem;
int ox = *org++;
int oy = *org++;
int lengthref, lengthcmp;
lengthref = abs( *ref - ox);
ref++;
lengthref += abs( *ref - oy); // = longueur entre point origine et pad ref
lengthcmp = abs( *compare - ox);
compare++;
lengthcmp += abs( *compare - oy); // = longueur entre point origine et pad comparé
return( lengthref - lengthcmp );
}
/****************************************************************************************/
int * WinEDA_BasePcbFrame::build_ratsnest_pad(EDA_BaseStruct * ref,
const wxPoint & refpos, bool init)
/****************************************************************************************/
{
int ii;
int * pt_coord, * base_data;
int current_net_code = 0, conn_number = 0;
LISTE_PAD * padlist;
D_PAD * pad_ref = NULL;
if( ((m_Pcb->m_Status_Pcb & LISTE_CHEVELU_OK) == 0 ) ||
((m_Pcb->m_Status_Pcb & LISTE_PAD_OK) == 0) )
{
nb_local_chevelu = 0;
return(NULL);
}
base_data = pt_coord = (int *) adr_lowmem;
local_liste_chevelu = (CHEVELU *) pt_coord;
if (init)
{
nb_local_chevelu = 0;
if(ref == NULL) return(NULL);
switch ( ref->m_StructType )
{
case TYPEPAD:
pad_ref = (D_PAD*) ref;
current_net_code = pad_ref->m_NetCode;
conn_number = pad_ref->m_physical_connexion;
break;
case TYPETRACK:
case TYPEVIA:
{
TRACK *track_ref = (TRACK*) ref;
current_net_code = track_ref->m_NetCode;
conn_number = track_ref->m_Sous_Netcode;
break;
}
}
if ( current_net_code <= 0 ) return NULL;
*pt_coord = refpos.x; pt_coord++;
*pt_coord = refpos.y; pt_coord++;
if( m_Pcb->m_Ratsnest == NULL ) return(NULL);
padlist = m_Pcb->m_Pads;
for( ii = 0 ;ii < m_Pcb->m_NbPads; padlist++, ii++)
{
D_PAD * pad = *padlist;
if( pad->m_NetCode != current_net_code ) continue;
if( pad == pad_ref ) continue;
if( !pad->m_physical_connexion || (pad->m_physical_connexion != conn_number) )
{
*pt_coord = pad->m_Pos.x; pt_coord++;
*pt_coord = pad->m_Pos.y; pt_coord++;
nb_local_chevelu++;
}
}
} /* Fin Init */
else if( nb_local_chevelu )
{
*pt_coord = refpos.x; *(pt_coord+1) = refpos.y;
}
qsort(base_data + 2,nb_local_chevelu,2*sizeof(int),
(int(*)(const void *, const void *))sort_by_localnetlength) ;
return pt_coord;
}
/*******************************************************/
void WinEDA_BasePcbFrame::trace_ratsnest_pad(wxDC * DC)
/*******************************************************/
/*
affiche le "chevelu" d'un pad lors des trace de segments de piste
*/
{
int * pt_coord;
int ii;
int refX, refY;
if((m_Pcb->m_Status_Pcb & LISTE_CHEVELU_OK) == 0) return;
if( nb_local_chevelu == 0 ) return;
if ( local_liste_chevelu == NULL ) return;
pt_coord = (int*) local_liste_chevelu;
refX = *pt_coord; pt_coord++;
refY = *pt_coord; pt_coord++;
GRSetDrawMode(DC, GR_XOR);
for( ii = 0; ii < nb_local_chevelu; ii++)
{
if ( ii >= g_MaxLinksShowed ) break;
GRLine(&DrawPanel->m_ClipBox, DC, refX, refY, *pt_coord, *(pt_coord+1), YELLOW);
pt_coord += 2;
}
}