kicad/pcbnew/autoplac.cpp

1159 lines
33 KiB
C++
Raw Blame History

/*************************************************/
/* Routines de placement automatique des MODULES */
/*************************************************/
/* Fichier autoplac.cpp */
#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "pcbnew.h"
#include "autorout.h"
#include "cell.h"
#include "protos.h"
/************************************************************/
/* Menu et Routines de placement automatique des composants */
/************************************************************/
#define GAIN 16
#define PENALITE 500
/* Penalite pour orientation donnee par CntRot90 et CntRot180:
gradue de 0 ( rotation interdite ) a 10 ( rotation a cout null )
Le cout est ici donne en majoration
*/
static float OrientPenality[11] = {
2.0, /* CntRot = 0 en fait rotation interdite */
1.9, /* CntRot = 1 */
1.8, /* CntRot = 2 */
1.7, /* CntRot = 3 */
1.6, /* CntRot = 4 */
1.5, /* CntRot = 5 */
1.4, /* CntRot = 5 */
1.3, /* CntRot = 7 */
1.2, /* CntRot = 8 */
1.1, /* CntRot = 9 */
1.0}; /* CntRot = 10 rotation autorisee, penalite nulle */
/* Etat d'une cellule */
#define OUT_OF_BOARD -2
#define OCCUPED_By_MODULE -1
/* variables locales */
static wxPoint CurrPosition; // position courante du module en cours de placement
static bool AutoPlaceShowAll = TRUE;
float MinCout;
/* Fonctions locales */
static int TstModuleOnBoard(BOARD * Pcb, MODULE * Module, bool TstOtherSide);
static void Build_PlacedPads_List(BOARD * Pcb);
static int Tri_PlaceModules(MODULE ** pt_ref, MODULE ** pt_compare);
static void TracePenaliteRectangle(BOARD * Pcb, int ux0, int uy0, int ux1, int uy1,
int marge, int Penalite, int masque_layer);
static MODULE * PickModule( WinEDA_PcbFrame * pcbframe, wxDC * DC );
/* variables importees */
extern CHEVELU * local_liste_chevelu; // adresse de base du buffer des chevelus locaux
extern int nb_local_chevelu; // nbr de links du module en deplacement
/********************************************************************************/
void WinEDA_PcbFrame::AutoPlaceModule(MODULE * Module, int place_mode, wxDC * DC)
/********************************************************************************/
/* Routine de Placement Automatique des composants dans le contour du PCB
Les composants ayant le status FIXE ne sont pas bouges
Si le menu appelant est le placement de 1 module, il sera replace
*/
{
int ii, activ;
MODULE * ThisModule = NULL;
MODULE ** BaseListeModules;
wxPoint PosOK;
wxPoint memopos;
int error;
int NbModules = 0;
int NbTotalModules = 0;
float Pas;
int lay_tmp_TOP, lay_tmp_BOTTOM, OldPasRoute;
if( m_Pcb->m_Modules == NULL ) return;
DrawPanel->m_AbortRequest = FALSE;
DrawPanel->m_AbortEnable = TRUE;
switch( place_mode )
{
case PLACE_1_MODULE:
ThisModule = Module;
if(ThisModule == NULL) return;
ThisModule->m_ModuleStatus &= ~(MODULE_is_PLACED|MODULE_to_PLACE);
break;
case PLACE_OUT_OF_BOARD:
break;
case PLACE_ALL:
if( ! IsOK(this, _("Footprints NOT LOCKED will be moved") ))
return;
break;
case PLACE_INCREMENTAL:
if( ! IsOK(this, _("Footprints NOT PLACED will be moved")) )
return;
break;
}
memopos = CurrPosition;
lay_tmp_BOTTOM = Route_Layer_BOTTOM;
lay_tmp_TOP = Route_Layer_TOP;
OldPasRoute = g_GridRoutingSize;
g_GridRoutingSize = m_CurrentScreen->GetGrid().x;
// Ensure g_GridRoutingSize has a reasonnable value:
if ( g_GridRoutingSize < 10 ) g_GridRoutingSize = 10; // Min value = 1/1000 inch
/* Compute module parmeters used in auto place */
Module = m_Pcb->m_Modules;
for( ; Module != NULL; Module = (MODULE*)Module->Pnext) // remise a jour du rect d'encadrement
{
Module->Set_Rectangle_Encadrement();
Module->SetRectangleExinscrit();
}
/* Generation du plan de placement */
if( GenPlaceBoard() == 0) return;
/* Mise a jour des parametres modules utiles au placement */
BaseListeModules = GenListeModules(m_Pcb, &NbTotalModules );
MyFree(BaseListeModules);
/* Placement des modules fixes sur le plan de placement */
Module = m_Pcb->m_Modules;
for( ; Module != NULL; Module = (MODULE*)Module->Pnext )
{
Module->m_ModuleStatus &= ~MODULE_to_PLACE;
switch( place_mode )
{
case PLACE_1_MODULE:
if( ThisModule == Module )
Module->m_ModuleStatus |= MODULE_to_PLACE;
break;
case PLACE_OUT_OF_BOARD:
Module->m_ModuleStatus &= ~MODULE_is_PLACED;
if( Module->m_ModuleStatus & MODULE_is_LOCKED ) break;
if( ! m_Pcb->m_BoundaryBox.Inside(Module->m_Pos) )
Module->m_ModuleStatus |= MODULE_to_PLACE;
break;
case PLACE_ALL:
Module->m_ModuleStatus &= ~MODULE_is_PLACED;
if( Module->m_ModuleStatus & MODULE_is_LOCKED ) break;
Module->m_ModuleStatus |= MODULE_to_PLACE;
break;
case PLACE_INCREMENTAL:
if( Module->m_ModuleStatus & MODULE_is_LOCKED )
{
Module->m_ModuleStatus &= ~MODULE_is_PLACED; break;
}
if( ! (Module->m_ModuleStatus & MODULE_is_PLACED) )
Module->m_ModuleStatus |= MODULE_to_PLACE;
break;
}
if( Module->m_ModuleStatus & MODULE_to_PLACE ) // Erase from screen
{
NbModules++;
Module->Draw(DrawPanel, DC, wxPoint(0,0), GR_XOR);
}
else
{
GenModuleOnBoard(Module);
}
}
/* Placement des modules */
activ = 0; Pas = 100.0;
if( NbModules ) Pas = 100.0 / (float)NbModules;
while( (Module = PickModule(this, DC) ) != NULL )
{
float BestScore;
DisplayActivity((int)(activ * Pas) , wxEmptyString ); activ++;
/* Affichage du remplissage: surface de placement, obstacles, penalites */
DrawInfoPlace(DC);
/* Recherche du placement: orientation 0 */
error = RecherchePlacementModule(Module, DC);
BestScore = MinCout;
PosOK = CurrPosition;
if ( error == ESC ) goto end_of_tst;
/* Recherche du placement: orientation 180 */
ii = Module->m_CntRot180 & 0x0F;
if( ii != 0 )
{
int Angle_Rot_Module = 1800;
Rotate_Module(DC, Module, Angle_Rot_Module, FALSE);
Module->SetRectangleExinscrit();
error = RecherchePlacementModule(Module, DC );
MinCout *= OrientPenality[ii];
if( BestScore > MinCout ) /* Cette orientation est meilleure */
{
PosOK = CurrPosition;
BestScore = MinCout;
}
else
{
Angle_Rot_Module = -1800;
Rotate_Module(DC, Module, Angle_Rot_Module, FALSE);
}
if ( error == ESC ) goto end_of_tst;
}
/* Recherche du placement: orientation 90 */
ii = Module->m_CntRot90 & 0x0F;
if( ii != 0 )
{
int Angle_Rot_Module = 900;
Rotate_Module(DC, Module, Angle_Rot_Module, FALSE);
error = RecherchePlacementModule(Module, DC );
MinCout *= OrientPenality[ii];
if( BestScore > MinCout ) /* Cette orientation est meilleure */
{
PosOK = CurrPosition;
BestScore = MinCout;
}
else
{
Angle_Rot_Module = -900;
Rotate_Module(DC, Module, Angle_Rot_Module, FALSE);
}
if ( error == ESC ) goto end_of_tst;
}
/* Recherche du placement: orientation -90 (ou 270 degres) */
ii = (Module->m_CntRot90 >> 4 ) & 0x0F;
if( ii != 0 )
{
int Angle_Rot_Module = 2700;
Rotate_Module(DC, Module, Angle_Rot_Module, FALSE);
error = RecherchePlacementModule(Module, DC );
MinCout *= OrientPenality[ii];
if( BestScore > MinCout ) /* Cette orientation est meilleure */
{
PosOK = CurrPosition;
BestScore = MinCout;
}
else
{
Angle_Rot_Module = -2700;
Rotate_Module(DC, Module, Angle_Rot_Module, FALSE);
}
if ( error == ESC ) goto end_of_tst;
}
end_of_tst:
if ( error == ESC ) break;
/* placement du module */
CurrPosition = m_CurrentScreen->m_Curseur;
m_CurrentScreen->m_Curseur = PosOK;
Place_Module(Module, DC);
m_CurrentScreen->m_Curseur = CurrPosition;
Module->Set_Rectangle_Encadrement();
Module->SetRectangleExinscrit();
GenModuleOnBoard(Module);
Module->m_ModuleStatus |= MODULE_is_PLACED;
Module->m_ModuleStatus &= ~MODULE_to_PLACE;
}
CurrPosition = memopos;
/* Liberation de la memoire */
Board.UnInitBoard();
Route_Layer_TOP = lay_tmp_TOP;
Route_Layer_BOTTOM = lay_tmp_BOTTOM;
g_GridRoutingSize = OldPasRoute;
Module = m_Pcb->m_Modules;
for( ; Module != NULL; Module = (MODULE*)Module->Pnext )
{
Module->Set_Rectangle_Encadrement();
}
/* Recalcul de la liste des pads, detruite par les calculs precedents */
m_Pcb->m_Status_Pcb = 0; build_liste_pads();
DrawPanel->ReDraw(DC, TRUE);
DrawPanel->m_AbortEnable = FALSE;
}
/**********************************************/
void WinEDA_PcbFrame::DrawInfoPlace(wxDC * DC)
/**********************************************/
/* Affiche a l'ecran les infos de placement
*/
{
int color, ii, jj;
int ox, oy, top_state, bottom_state;
GRSetDrawMode(DC, GR_COPY);
for ( ii = 0; ii < Nrows; ii ++ )
{
oy = m_Pcb->m_BoundaryBox.m_Pos.y + (ii * g_GridRoutingSize);
for (jj = 0; jj < Ncols ; jj ++ )
{
ox = m_Pcb->m_BoundaryBox.m_Pos.x + (jj * g_GridRoutingSize);
/* surface de placement : */
color = BLACK;
top_state = GetCell(ii, jj , TOP);
bottom_state = GetCell(ii, jj , BOTTOM);
if( (top_state & CELL_is_ZONE) ) color = BLUE;
/* obstacles */
if( (top_state & CELL_is_EDGE) || (bottom_state & CELL_is_EDGE)) color = WHITE;
else if( top_state & (HOLE|CELL_is_MODULE) ) color = LIGHTRED;
else if( bottom_state & (HOLE|CELL_is_MODULE) ) color = LIGHTGREEN;
else /* Affichage du remplissage: Penalites */
{
if( GetDist(ii, jj , TOP ) || GetDist(ii, jj , BOTTOM ) )
color = DARKGRAY;
}
GRPutPixel(&DrawPanel->m_ClipBox, DC, ox, oy, color);
}
}
}
/***************************************/
int WinEDA_PcbFrame::GenPlaceBoard(void)
/***************************************/
/* Routine de generation du board ( cote composant + cote cuivre ) :
Alloue la memoire necessaire pour representer en "bitmap" sur la grille
courante:
- la surface de placement des composant ( le board )
- le bitmap des penalites
et initialise les cellules du board a
- HOLE pour les cellules occupees par un segment EDGE
- CELL_is_ZONE pour les cellules internes au contour EDGE (s'il est ferme)
la surface de placement (board) donne les cellules internes au contour
du pcb, et parmi celle-ci les cellules libres et les cellules deja occupees
le bitmap des penalites donnent les cellules occupes par les modules,
augmentes d'une surface de penalite liee au nombre de pads du module
le bitmap des penalites est mis a 0
l'occupation des cellules est laisse a 0
*/
{
int jj, ii;
int NbCells;
EDA_BaseStruct * PtStruct;
wxString msg;
Board.UnInitBoard();
if( ! SetBoardBoundaryBoxFromEdgesOnly() )
{
DisplayError(this, _("No edge PCB, Unknown board size!"), 30);
return(0);
}
/* The boundary box must have its start point on placing grid: */
m_Pcb->m_BoundaryBox.m_Pos.x -= m_Pcb->m_BoundaryBox.m_Pos.x % g_GridRoutingSize;
m_Pcb->m_BoundaryBox.m_Pos.y -= m_Pcb->m_BoundaryBox.m_Pos.y % g_GridRoutingSize;
/* The boundary box must have its end point on placing grid: */
wxPoint end = m_Pcb->m_BoundaryBox.GetEnd();
end.x -= end.x % g_GridRoutingSize; end.x += g_GridRoutingSize;
end.y -= end.y % g_GridRoutingSize; end.y += g_GridRoutingSize;
m_Pcb->m_BoundaryBox.SetEnd(end);
Nrows = m_Pcb->m_BoundaryBox.GetHeight() / g_GridRoutingSize;
Ncols = m_Pcb->m_BoundaryBox.GetWidth() / g_GridRoutingSize;
/* get a small margin for memory allocation: */
Ncols += 2; Nrows += 2;
NbCells = Ncols * Nrows;
MsgPanel->EraseMsgBox();
msg.Printf( wxT("%d"),Ncols);
Affiche_1_Parametre(this, 1, _("Cols"),msg,GREEN);
msg.Printf( wxT("%d"),Nrows);
Affiche_1_Parametre(this, 7, _("Lines"),msg,GREEN);
msg.Printf( wxT("%d"),NbCells);
Affiche_1_Parametre(this, 14, _("Cells."),msg,YELLOW);
/* Choix du nombre de faces de placement */
Nb_Sides = TWO_SIDES;
Affiche_1_Parametre(this, 22, wxT("S"), ( Nb_Sides == TWO_SIDES ) ? wxT("2") : wxT("1"), WHITE);
/* Creation du mapping du board */
Board.InitBoard();
/* Affichage de la memoire utilisee */
msg.Printf( wxT("%d"), Board.m_MemSize / 1024 );
Affiche_1_Parametre(this, 24, wxT("Mem(Ko)"),msg,CYAN);
Route_Layer_BOTTOM = CMP_N;
if( Nb_Sides == TWO_SIDES ) Route_Layer_BOTTOM = CUIVRE_N;
Route_Layer_TOP = CMP_N;
/* Place the edge layer segments */
PtStruct = m_Pcb->m_Drawings;
TRACK TmpSegm(NULL);
TmpSegm.m_Layer = -1;
TmpSegm.m_NetCode = -1;
TmpSegm.m_Width = g_GridRoutingSize/2;
for( ; PtStruct != NULL; PtStruct = PtStruct->Pnext )
{
DRAWSEGMENT * DrawSegm;
switch( PtStruct->m_StructType)
{
case TYPEDRAWSEGMENT:
DrawSegm = (DRAWSEGMENT *) PtStruct;
if(DrawSegm->m_Layer != EDGE_N) break;
TmpSegm.m_Start = DrawSegm->m_Start;
TmpSegm.m_End = DrawSegm->m_End;
TmpSegm.m_Shape = DrawSegm->m_Shape;
TmpSegm.m_Param = DrawSegm->m_Angle;
TraceSegmentPcb(m_Pcb, &TmpSegm, HOLE|CELL_is_EDGE, g_GridRoutingSize, WRITE_CELL );
break;
case TYPETEXTE:
default: break;
}
}
/* Init du point d'accrochage de la zone */
OrCell(Nrows/2, Ncols/2, BOTTOM, CELL_is_ZONE);
/* Remplissage des cellules de la couche BOTTOM */
ii = 1; jj = 1;
while( ii )
{
msg.Printf( wxT("%d"), jj++ );
Affiche_1_Parametre(this, 50, _("Loop"),msg,CYAN);
ii = Propagation(this);
}
/* Init de la couche TOP */
if( Board.m_BoardSide[TOP] )
memcpy( Board.m_BoardSide[TOP], Board.m_BoardSide[BOTTOM],NbCells * sizeof(BoardCell));
return(1);
}
/******************************************************/
void WinEDA_PcbFrame::GenModuleOnBoard(MODULE * Module)
/******************************************************/
/* initialise sur le board de placement les cellules correspondantes au
module Module
*/
{
int ox, oy, fx, fy, Penalite;
int marge = g_GridRoutingSize / 2;
int masque_layer;
D_PAD * Pad;
ox = Module->m_RealBoundaryBox.m_Pos.x - marge;
fx = Module->m_RealBoundaryBox.GetRight() + marge;
oy = Module->m_RealBoundaryBox.m_Pos.y - marge;
fy = Module->m_RealBoundaryBox.GetBottom() + marge;
if( ox < m_Pcb->m_BoundaryBox.m_Pos.x)
ox = m_Pcb->m_BoundaryBox.m_Pos.x;
if( ox > m_Pcb->m_BoundaryBox.GetRight())
ox = m_Pcb->m_BoundaryBox.GetRight();
if( fx < m_Pcb->m_BoundaryBox.m_Pos.x) fx = m_Pcb->m_BoundaryBox.m_Pos.x;
if( fx > m_Pcb->m_BoundaryBox.GetRight())
fx = m_Pcb->m_BoundaryBox.GetRight();
if( oy < m_Pcb->m_BoundaryBox.m_Pos.y) oy = m_Pcb->m_BoundaryBox.m_Pos.y;
if( oy > m_Pcb->m_BoundaryBox.GetBottom())
oy = m_Pcb->m_BoundaryBox.GetBottom();
if( fy < m_Pcb->m_BoundaryBox.m_Pos.y) fy = m_Pcb->m_BoundaryBox.m_Pos.y;
if( fy > m_Pcb->m_BoundaryBox.GetBottom())
fy = m_Pcb->m_BoundaryBox.GetBottom();
masque_layer = 0;
if( Module->m_Layer == CMP_N ) masque_layer = CMP_LAYER;
if( Module->m_Layer == CUIVRE_N ) masque_layer = CUIVRE_LAYER;
TraceFilledRectangle(m_Pcb, ox, oy, fx, fy, masque_layer,
CELL_is_MODULE, WRITE_OR_CELL);
/* Trace des pads et leur surface de securite */
marge = g_DesignSettings.m_TrackClearence + g_DesignSettings.m_CurrentTrackWidth;
for(Pad = Module->m_Pads ; Pad != NULL; Pad = (D_PAD *) Pad->Pnext )
{
Place_1_Pad_Board( m_Pcb, Pad, CELL_is_MODULE, marge, WRITE_OR_CELL);
}
/* Trace de la penalite */
marge = (g_GridRoutingSize * Module->m_PadNum ) / GAIN;
Penalite = PENALITE;
TracePenaliteRectangle(m_Pcb, ox, oy, fx, fy, marge, Penalite,
masque_layer);
}
/************************************************************************/
int WinEDA_PcbFrame::RecherchePlacementModule(MODULE * Module, wxDC * DC)
/************************************************************************/
/*
Routine Principale de recherche de la position optimale du module
Entree:
Module pointe la struct MODULE du module a placer.
Retourne:
1 si placement impossible, 0 si OK
et MinCout = variable externe = cout du meilleur placement
*/
{
int cx, cy;
int ox, oy, fx, fy; /* cadre d'occupation du module centre sur le curseur */
int error = 1;
int DisplayChevelu = 0;
wxPoint LastPosOK;
float mincout, cout, Score;
int Penalite;
bool TstOtherSide;
Module->Display_Infos(this);
Build_PlacedPads_List(m_Pcb);
LastPosOK.x = m_Pcb->m_BoundaryBox.m_Pos.x;
LastPosOK.y = m_Pcb->m_BoundaryBox.m_Pos.y;
cx = Module->m_Pos.x; cy = Module->m_Pos.y;
ox = Module->m_RealBoundaryBox.m_Pos.x - cx;
fx = Module->m_RealBoundaryBox.m_Size.x + ox;
oy = Module->m_RealBoundaryBox.m_Pos.y - cy;
fy = Module->m_RealBoundaryBox.m_Size.y + oy;
CurrPosition.x = m_Pcb->m_BoundaryBox.m_Pos.x - ox;
CurrPosition.y = m_Pcb->m_BoundaryBox.m_Pos.y - oy;
/* remise sur la grille de placement: */
CurrPosition.x -= CurrPosition.x % g_GridRoutingSize;
CurrPosition.y -= CurrPosition.y % g_GridRoutingSize;
g_Offset_Module.x = cx - CurrPosition.x;
g_Offset_Module.y = cy - CurrPosition.y;
m_Pcb->m_Status_Pcb &= ~CHEVELU_LOCAL_OK;
/* tst des pastilles traversantes, qui pour un circuit imprime ayant des
composants des 2 cotes, peuvent tomber sur un composant de cote oppose:
s'il y a au moins 1 pastille apparaissant sur l'autre cote, ce cote
est teste */
TstOtherSide = FALSE;
if( Nb_Sides == TWO_SIDES )
{
D_PAD * Pad; int masque_otherlayer;
masque_otherlayer = CUIVRE_LAYER;
if ( Module->m_Layer == CUIVRE_N ) masque_otherlayer = CMP_LAYER;
for(Pad = Module->m_Pads ; Pad != NULL; Pad = (D_PAD *) Pad->Pnext )
{
if( (Pad->m_Masque_Layer & masque_otherlayer) == 0 ) continue;
TstOtherSide = TRUE;
break;
}
}
DrawModuleOutlines(DrawPanel, DC, Module);
mincout = -1.0;
Affiche_Message(wxT("Score ??, pos ??") );
for ( ; CurrPosition.x < m_Pcb->m_BoundaryBox.GetRight() - fx;
CurrPosition.x += g_GridRoutingSize )
{
wxYield();
if ( DrawPanel->m_AbortRequest )
{
if ( IsOK(this, _("Ok to abort ?")) ) return ESC;
else DrawPanel->m_AbortRequest = FALSE;
}
cx = Module->m_Pos.x; cy = Module->m_Pos.y;
Module->m_RealBoundaryBox.m_Pos.x = ox + CurrPosition.x;
Module->m_RealBoundaryBox.m_Pos.y = oy + CurrPosition.y;
DrawModuleOutlines(DrawPanel, DC, Module);
g_Offset_Module.x = cx - CurrPosition.x;
CurrPosition.y = m_Pcb->m_BoundaryBox.m_Pos.y - oy;
/* remise sur la grille de placement: */
CurrPosition.y -= CurrPosition.y % g_GridRoutingSize;
DrawModuleOutlines(DrawPanel, DC, Module);
for ( ; CurrPosition.y < m_Pcb->m_BoundaryBox.GetBottom() - fy;
CurrPosition.y += g_GridRoutingSize )
{
/* effacement des traces */
DrawModuleOutlines(DrawPanel, DC, Module);
if (DisplayChevelu) Compute_Ratsnest_PlaceModule(DC);
DisplayChevelu = 0;
Module->m_RealBoundaryBox.m_Pos.x = ox + CurrPosition.x;
Module->m_RealBoundaryBox.m_Pos.y = oy + CurrPosition.y;
g_Offset_Module.y = cy - CurrPosition.y;
DrawModuleOutlines(DrawPanel, DC, Module);
Penalite = TstModuleOnBoard(m_Pcb, Module, TstOtherSide );
if( Penalite >= 0 ) /* c a d si le module peut etre place */
{
error = 0;
build_ratsnest_module(DC, Module);
cout = Compute_Ratsnest_PlaceModule(DC);
DisplayChevelu = 1;
Score = cout + (float) Penalite;
if( (mincout >= Score ) || (mincout < 0 ) )
{
LastPosOK = CurrPosition;
mincout = Score;
wxString msg;
msg.Printf( wxT("Score %d, pos %3.4f, %3.4f"),
(int)mincout,
(float)LastPosOK.x /10000, (float)LastPosOK.y /10000);
Affiche_Message(msg);
}
}
if( DisplayChevelu ) Compute_Ratsnest_PlaceModule(DC);
DisplayChevelu = 0;
}
}
DrawModuleOutlines(DrawPanel, DC, Module); /* effacement du dernier trace */
if (DisplayChevelu) Compute_Ratsnest_PlaceModule(DC);
/* Regeneration des variables modifiees */
Module->m_RealBoundaryBox.m_Pos.x = ox + cx;
Module->m_RealBoundaryBox.m_Pos.y = oy + cy;
CurrPosition = LastPosOK;
m_Pcb->m_Status_Pcb &= ~(CHEVELU_LOCAL_OK | LISTE_PAD_OK );
MinCout = mincout;
return(error);
}
/**************************************************************************/
int TstRectangle(BOARD * Pcb, int ux0, int uy0, int ux1, int uy1, int side)
/**************************************************************************/
/* tst si la surface rectangulaire (ux,y0 .. ux,y1):
- est sur une zone libre ( retourne OCCUPED_By_MODULE sinon)
- est sur la surface utile du board ( retourne OUT_OF_BOARD sinon)
retourne 0 si OK
*/
{
int row, col;
int row_min, row_max, col_min, col_max;
unsigned int data;
ux0 -= Pcb->m_BoundaryBox.m_Pos.x; uy0 -= Pcb->m_BoundaryBox.m_Pos.y;
ux1 -= Pcb->m_BoundaryBox.m_Pos.x; uy1 -= Pcb->m_BoundaryBox.m_Pos.y;
/* Calcul des coord limites des cellules appartenant au rectangle */
row_max = uy1 / g_GridRoutingSize;
col_max = ux1 / g_GridRoutingSize;
row_min = uy0 / g_GridRoutingSize; if (uy0 > row_min*g_GridRoutingSize ) row_min++;
col_min = ux0 / g_GridRoutingSize; if (ux0 > col_min*g_GridRoutingSize ) col_min++;
if( row_min < 0 ) row_min = 0;
if( row_max >= (Nrows-1)) row_max = Nrows-1;
if( col_min < 0 ) col_min = 0;
if( col_max >= (Ncols-1)) col_max = Ncols-1;
for( row = row_min ;row <= row_max ; row++)
{
for(col = col_min;col <= col_max; col++)
{
data = GetCell(row,col,side);
if( (data & CELL_is_ZONE) == 0 ) /* Cellule non autorisee */
return(OUT_OF_BOARD);
if( data & CELL_is_MODULE ) /* Deja utilisee */
return(OCCUPED_By_MODULE);
}
}
return( 0 );
}
/******************************************************************************/
unsigned int CalculePenaliteRectangle(BOARD * Pcb, int ux0, int uy0,
int ux1, int uy1, int side)
/******************************************************************************/
/* calcule et retourne la penalite de la surface rectangulaire (ux,y0 .. ux,y1):
( somme des valeurs des cellules du plan des Distances )
*/
{
int row, col;
int row_min, row_max, col_min, col_max;
unsigned int Penalite;
ux0 -= Pcb->m_BoundaryBox.m_Pos.x; uy0 -= Pcb->m_BoundaryBox.m_Pos.y;
ux1 -= Pcb->m_BoundaryBox.m_Pos.x; uy1 -= Pcb->m_BoundaryBox.m_Pos.y;
/* Calcul des coord limites des cellules appartenant au rectangle */
row_max = uy1 / g_GridRoutingSize;
col_max = ux1 / g_GridRoutingSize;
row_min = uy0 / g_GridRoutingSize; if (uy0 > row_min*g_GridRoutingSize ) row_min++;
col_min = ux0 / g_GridRoutingSize; if (ux0 > col_min*g_GridRoutingSize ) col_min++;
if( row_min < 0 ) row_min = 0;
if( row_max >= (Nrows-1)) row_max = Nrows-1;
if( col_min < 0 ) col_min = 0;
if( col_max >= (Ncols-1)) col_max = Ncols-1;
Penalite = 0;
for( row = row_min ;row <= row_max ; row++)
{
for(col = col_min;col <= col_max; col++)
{
Penalite += (int) GetDist(row, col, side );
}
}
return( Penalite );
}
/**********************************************************************/
int TstModuleOnBoard(BOARD * Pcb, MODULE * Module, bool TstOtherSide)
/**********************************************************************/
/* Teste si le module peut etre place sur le board.
retourne de diagnostic de TstRectangle().
le module est connu par son rectangle d'encadrement
*/
{
int ox, oy, fx, fy;
int error, Penalite, marge, side, otherside;
side = TOP; otherside = BOTTOM;
if ( Module->m_Layer == CUIVRE_N )
{
side = BOTTOM; otherside = TOP;
}
ox = Module->m_RealBoundaryBox.m_Pos.x;
fx = Module->m_RealBoundaryBox.GetRight();
oy = Module->m_RealBoundaryBox.m_Pos.y;
fy = Module->m_RealBoundaryBox.GetBottom();
error = TstRectangle(Pcb, ox, oy, fx, fy, side );
if( error < 0 ) return (error);
if( TstOtherSide )
{
error = TstRectangle(Pcb, ox, oy, fx, fy, otherside );
if( error < 0 ) return (error);
}
marge = (g_GridRoutingSize * Module->m_PadNum ) / GAIN;
Penalite = CalculePenaliteRectangle(Pcb, ox - marge, oy - marge,
fx + marge, fy + marge, side );
return( Penalite );
}
/************************************************************/
float WinEDA_PcbFrame::Compute_Ratsnest_PlaceModule(wxDC * DC)
/************************************************************/
/* Routine affichant le chevelu du module en cours de deplacement, et
evaluant le "cout" de la position.
Le cout est la longueur des chevelus en distance de manhattan, avec
penalite pour les inclinaisons se rapprochant de 45 degre
*/
{
CHEVELU* pt_local_chevelu ;
int ii;
float cout, icout;
int ox, oy, fx, fy, dx , dy;
if((m_Pcb->m_Status_Pcb & CHEVELU_LOCAL_OK) == 0)
return(-1);
pt_local_chevelu = local_liste_chevelu;
ii = nb_local_chevelu; cout = 0;
while( ii-- > 0 )
{
if(pt_local_chevelu->status & LOCAL_CHEVELU)
{ // Non Affich<63>
}
else
{
ox = pt_local_chevelu->pad_start->m_Pos.x - g_Offset_Module.x;
oy = pt_local_chevelu->pad_start->m_Pos.y - g_Offset_Module.y;
fx = pt_local_chevelu->pad_end->m_Pos.x;
fy = pt_local_chevelu->pad_end->m_Pos.y;
if ( AutoPlaceShowAll )
{
GRLine(&DrawPanel->m_ClipBox, DC, ox, oy, fx, fy,
0, g_DesignSettings.m_RatsnestColor|GR_XOR) ;
}
/* Evaluation du cout du chevelu: */
dx = fx - ox; dy = fy - oy;
dx = abs(dx); dy = abs( dy );
if( dx < dy ) EXCHG(dx,dy); /* dx >= dy */
/* cout de la distance: */
icout = (float) dx * dx;
/* cout de l'inclinaison */
icout += 3 * (float) dy * dy;
icout = sqrt(icout);
cout += icout; /* cout total = somme des couts de chaque chevelu */
}
pt_local_chevelu++;
}
return (cout);
}
/********************************************/
void Build_PlacedPads_List(BOARD * Pcb)
/********************************************/
/*
construction de la liste ( sous forme d'une liste de stucture )
des caract utiles des pads du PCB pour Placement Automatique )
Cette liste est restreinte a la liste des pads des modules deja places sur
la carte.
parametres:
adresse du buffer de classement = Pcb->ptr_pads;
Variables globales mise a jour:
pointeur ptr_pads (adr de classement de la liste des pads)
nb_pads = nombre utile de pastilles classes
m_Status_Pcb |= LISTE_PAD_OK
*/
{
LISTE_PAD* pt_liste_pad;
MODULE * Module;
D_PAD * PtPad;
if( Pcb->m_Pads ) MyFree( Pcb->m_Pads );
pt_liste_pad = Pcb->m_Pads = NULL;
Pcb->m_NbPads = Pcb->m_NbNodes = 0;
/* Calcul du nombre de pads utiles */
Module = Pcb->m_Modules;
for( ; Module != NULL ; Module = (MODULE*) Module->Pnext)
{
if( Module->m_ModuleStatus & MODULE_to_PLACE ) continue;
PtPad = (D_PAD*) Module->m_Pads;
for(; PtPad != NULL; PtPad = (D_PAD*) PtPad->Pnext )
{
Pcb->m_NbPads++;
}
}
/* Allocation memoire du buffer */
if ( Pcb->m_NbPads > 0)
{
pt_liste_pad = Pcb->m_Pads
= (D_PAD**) MyMalloc(Pcb->m_NbPads * sizeof(D_PAD*) );
}
/* Initialisation du buffer et des variables de travail */
Module = Pcb->m_Modules;
for( ;(Module != NULL) && (pt_liste_pad != NULL); Module = (MODULE*) Module->Pnext)
{
if( Module->m_ModuleStatus & MODULE_to_PLACE ) continue;
PtPad = (D_PAD*) Module->m_Pads;
for(; PtPad != NULL; PtPad = (D_PAD*) PtPad->Pnext )
{
*pt_liste_pad = PtPad;
PtPad->m_physical_connexion = 0;
PtPad->m_logical_connexion = 0;
PtPad->m_Parent = Module;
if(PtPad->m_NetCode) Pcb->m_NbNodes++;
pt_liste_pad++;
}
}
Pcb->m_Status_Pcb |= LISTE_PAD_OK;
Pcb->m_Status_Pcb &= ~(LISTE_CHEVELU_OK | CHEVELU_LOCAL_OK);
adr_lowmem = buf_work;
}
/*****************************************************************/
/* Construction de la zone de penalite ( rectangle ) d'un module */
/*****************************************************************/
/* les cellules ( du plan des Distances ) du rectangle x0,y0 a x1,y1 sont
incrementees de la valeur Penalite
celles qui sont externes au rectangle, mais internes au rectangle
x0,y0 -marge a x1,y1 + marge sont incrementees d'une valeur
(Penalite ... 0) decroissante en fonction de leur eloignement
*/
static void TracePenaliteRectangle(BOARD * Pcb, int ux0, int uy0, int ux1, int uy1 ,
int marge, int Penalite, int masque_layer)
{
int row, col;
int row_min, row_max, col_min, col_max, pmarge;
int trace = 0;
DistCell data, LocalPenalite;
int lgain, cgain;
if(masque_layer & g_TabOneLayerMask[Route_Layer_BOTTOM])
trace = 1; /* Trace sur BOTTOM */
if( (masque_layer & g_TabOneLayerMask[Route_Layer_TOP] ) && Nb_Sides)
trace |= 2; /* Trace sur TOP */
if(trace == 0) return;
ux0 -= Pcb->m_BoundaryBox.m_Pos.x; uy0 -= Pcb->m_BoundaryBox.m_Pos.y;
ux1 -= Pcb->m_BoundaryBox.m_Pos.x; uy1 -= Pcb->m_BoundaryBox.m_Pos.y;
ux0 -= marge; ux1 += marge;
uy0 -= marge; uy1 += marge;
pmarge = marge / g_GridRoutingSize; if (pmarge < 1 ) pmarge = 1;
/* Calcul des coord limites des cellules appartenant au rectangle */
row_max = uy1 / g_GridRoutingSize;
col_max = ux1 / g_GridRoutingSize;
row_min = uy0 / g_GridRoutingSize; if (uy0 > row_min*g_GridRoutingSize ) row_min++;
col_min = ux0 / g_GridRoutingSize; if (ux0 > col_min*g_GridRoutingSize ) col_min++;
if( row_min < 0 ) row_min = 0;
if( row_max >= (Nrows-1)) row_max = Nrows-1;
if( col_min < 0 ) col_min = 0;
if( col_max >= (Ncols-1)) col_max = Ncols-1;
for( row = row_min ;row <= row_max ; row++)
{
lgain = 256;
if ( row < pmarge )
lgain = (256 * row) / pmarge;
else if( row > row_max - pmarge )
lgain = (256 * (row_max - row) ) / pmarge;
for(col = col_min;col <= col_max; col++)
{
cgain = 256;
LocalPenalite = Penalite;
if ( col < pmarge )
cgain = (256 * col) / pmarge;
else if( col > col_max - pmarge )
cgain = (256 * (col_max - col) ) / pmarge;
cgain = (cgain * lgain) / 256;
if( cgain != 256 ) LocalPenalite = (LocalPenalite * cgain) / 256;
if(trace & 1)
{
data = GetDist(row,col,BOTTOM) + LocalPenalite;
SetDist(row,col,BOTTOM, data);
}
if(trace & 2)
{
data = GetDist(row,col,TOP);
data = max( data, LocalPenalite);
SetDist(row,col,TOP, data);
}
}
}
}
/***************************************************/
/* Routines de tri de modules, utilisee par qsort: */
/***************************************************/
static int Tri_PlaceModules(MODULE ** pt_ref, MODULE ** pt_compare)
{
float ff, ff1, ff2 ;
ff1 = (*pt_ref)->m_Surface * (*pt_ref)->m_PadNum ;
ff2 = (*pt_compare)->m_Surface * (*pt_compare)->m_PadNum ;
ff = ff1 - ff2 ;
if( ff < 0 ) return(1) ;
if( ff > 0 ) return(-1) ;
return( 0 );
}
static int Tri_RatsModules(MODULE ** pt_ref, MODULE ** pt_compare)
{
float ff, ff1, ff2 ;
ff1 = (*pt_ref)->m_Surface * (*pt_ref)->flag;
ff2 = (*pt_compare)->m_Surface * (*pt_compare)->flag;
ff = ff1 - ff2 ;
if( ff < 0 ) return(1) ;
if( ff > 0 ) return(-1) ;
return( 0 );
}
/***************************************************************/
static MODULE * PickModule(WinEDA_PcbFrame * pcbframe, wxDC * DC)
/***************************************************************/
/* Recherche le "meilleur" module a placer
les criteres de choix sont:
- maximum de chevelus avec les modules deja places
- taille max, et nombre de pads max
*/
{
MODULE ** BaseListeModules, ** pt_Dmod;
MODULE * Module = NULL, * AltModule = NULL;
CHEVELU* pt_local_chevelu;
int NbModules, ii;
BaseListeModules = GenListeModules(pcbframe->m_Pcb, &NbModules );
if( BaseListeModules == NULL ) return(NULL);
Build_PlacedPads_List(pcbframe->m_Pcb);
/* Tri par surface decroissante des modules
(on place les plus gros en 1er), surface ponderee par le nombre de pads */
qsort(BaseListeModules, NbModules, sizeof(MODULE**),
(int(*)(const void *, const void * ))Tri_PlaceModules);
for(pt_Dmod = BaseListeModules; *pt_Dmod != NULL; pt_Dmod++)
{
(*pt_Dmod)->flag = 0;
if( ! ((*pt_Dmod)->m_ModuleStatus & MODULE_to_PLACE) ) continue;
pcbframe->m_Pcb->m_Status_Pcb &= ~CHEVELU_LOCAL_OK;
adr_lowmem = buf_work;
(*pt_Dmod)->Display_Infos(pcbframe);
pcbframe->build_ratsnest_module(DC, *pt_Dmod);
/* calcul du nombre de chevelus externes */
pt_local_chevelu = local_liste_chevelu;
ii = nb_local_chevelu;
while( ii-- > 0 )
{
if( (pt_local_chevelu->status & LOCAL_CHEVELU) == 0 )
(*pt_Dmod)->flag++;
pt_local_chevelu++;
}
}
pcbframe->m_Pcb->m_Status_Pcb &= ~CHEVELU_LOCAL_OK;
qsort(BaseListeModules, NbModules, sizeof(MODULE**),
(int(*)(const void *, const void * ))Tri_RatsModules);
/* Recherche du "meilleur" module */
Module = NULL;
for(pt_Dmod = BaseListeModules; *pt_Dmod != NULL; pt_Dmod++)
{
if( ! ((*pt_Dmod)->m_ModuleStatus & MODULE_to_PLACE) ) continue;
AltModule = *pt_Dmod;
if( (*pt_Dmod)->flag == 0 ) continue;
Module = *pt_Dmod; break;
}
MyFree(BaseListeModules);
if ( Module ) return(Module);
else return(AltModule);
}
/*******************************************************/
bool WinEDA_PcbFrame::SetBoardBoundaryBoxFromEdgesOnly(void)
/*******************************************************/
/* Determine le rectangle d'encadrement du pcb, selon les contours
(couche EDGE) uniquement
Sortie:
m_Pcb->m_BoundaryBox mis a jour
Retourne FALSE si pas de contour
*/
{
int rayon, cx, cy, d;
int xmax, ymax;
EDA_BaseStruct * PtStruct;
DRAWSEGMENT* ptr;
bool succes = FALSE;
if (m_Pcb == NULL) return FALSE;
m_Pcb->m_BoundaryBox.m_Pos.x = m_Pcb->m_BoundaryBox.m_Pos.y = 0x7FFFFFFFl ;
xmax = ymax = -0x7FFFFFFFl ;
/* Analyse des Contours PCB */
PtStruct = m_Pcb->m_Drawings;
for( ; PtStruct != NULL; PtStruct = PtStruct->Pnext )
{
if( PtStruct->m_StructType != TYPEDRAWSEGMENT ) continue;
succes = TRUE;
ptr = (DRAWSEGMENT*) PtStruct;
d = (ptr->m_Width /2) + 1;
if(ptr->m_Shape == S_CIRCLE)
{
cx = ptr->m_Start.x; cy = ptr->m_Start.y;
rayon = (int)hypot((double)(ptr->m_End.x-cx),(double)(ptr->m_End.y-cy) );
rayon += d;
m_Pcb->m_BoundaryBox.m_Pos.x = min(m_Pcb->m_BoundaryBox.m_Pos.x,cx-rayon);
m_Pcb->m_BoundaryBox.m_Pos.y = min(m_Pcb->m_BoundaryBox.m_Pos.y,cy-rayon);
xmax = max(xmax,cx+rayon);
ymax = max(ymax,cy+rayon);
}
else
{
cx = min(ptr->m_Start.x, ptr->m_End.x );
cy = min(ptr->m_Start.y, ptr->m_End.y);
m_Pcb->m_BoundaryBox.m_Pos.x = min(m_Pcb->m_BoundaryBox.m_Pos.x,cx - d);
m_Pcb->m_BoundaryBox.m_Pos.y = min(m_Pcb->m_BoundaryBox.m_Pos.y,cy - d);
cx = max(ptr->m_Start.x, ptr->m_End.x );
cy = max(ptr->m_Start.y, ptr->m_End.y);
xmax = max(xmax,cx + d);
ymax = max(ymax,cy + d);
}
}
m_Pcb->m_BoundaryBox.SetWidth(xmax - m_Pcb->m_BoundaryBox.m_Pos.x);
m_Pcb->m_BoundaryBox.SetHeight(ymax - m_Pcb->m_BoundaryBox.m_Pos.y);
return(succes);
}