/** * @file autoplac.cpp * @brief Routiness to automatically place MODULES on a board. */ /* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2011 Wayne Stambaugh * * Copyright (C) 1992-2012 KiCad Developers, see change_log.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GAIN 16 #define KEEP_OUT_MARGIN 500 /* Penalty for guidance given by CntRot90 and CntRot180: * graduated from 0 (rotation allowed) to 10 (rotation count null) * the count is increased. */ static const double OrientPenality[11] = { 2.0f, // CntRot = 0 rotation prohibited 1.9f, // CntRot = 1 1.8f, // CntRot = 2 1.7f, // CntRot = 3 1.6f, // CntRot = 4 1.5f, // CntRot = 5 1.4f, // CntRot = 5 1.3f, // CntRot = 7 1.2f, // CntRot = 8 1.1f, // CntRot = 9 1.0f // CntRot = 10 rotation authorized, no penalty }; // Cell states. #define OUT_OF_BOARD -2 #define OCCUPED_By_MODULE -1 static wxPoint CurrPosition; // Current position of the current module placement static bool AutoPlaceShowAll = true; double MinCout; static int TstModuleOnBoard( BOARD* Pcb, MODULE* Module, bool TstOtherSide ); static void CreateKeepOutRectangle( int ux0, int uy0, int ux1, int uy1, int marge, int aKeepOut, int aLayerMask ); static MODULE* PickModule( PCB_EDIT_FRAME* pcbframe, wxDC* DC ); static int propagate(); void PCB_EDIT_FRAME::AutoPlaceModule( MODULE* Module, int place_mode, wxDC* DC ) { MODULE* currModule = NULL; wxPoint PosOK; wxPoint memopos; int error; LAYER_NUM lay_tmp_TOP, lay_tmp_BOTTOM; // Undo: init list PICKED_ITEMS_LIST newList; newList.m_Status = UR_CHANGED; ITEM_PICKER picker( NULL, UR_CHANGED ); if( GetBoard()->m_Modules == NULL ) return; m_canvas->SetAbortRequest( false ); switch( place_mode ) { case PLACE_1_MODULE: currModule = Module; if( currModule == NULL ) return; currModule->SetIsPlaced( false ); currModule->SetNeedsPlaced( false ); 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 = g_Route_Layer_BOTTOM; lay_tmp_TOP = g_Route_Layer_TOP; RoutingMatrix.m_GridRouting = (int) GetScreen()->GetGridSize().x; // Ensure Board.m_GridRouting has a reasonable value: if( RoutingMatrix.m_GridRouting < 10*IU_PER_MILS ) RoutingMatrix.m_GridRouting = 10*IU_PER_MILS; // Min value = 1/1000 inch // Compute module parameters used in auto place if( GenPlaceBoard() == 0 ) return; int moduleCount = 0; Module = GetBoard()->m_Modules; for( ; Module != NULL; Module = Module->Next() ) { Module->SetNeedsPlaced( false ); switch( place_mode ) { case PLACE_1_MODULE: if( currModule == Module ) { // Module will be placed, add to undo. picker.SetItem( currModule ); newList.PushItem( picker ); Module->SetNeedsPlaced( true ); } break; case PLACE_OUT_OF_BOARD: Module->SetIsPlaced( false ); if( Module->IsLocked() ) break; if( !RoutingMatrix.m_BrdBox.Contains( Module->GetPosition() ) ) { // Module will be placed, add to undo. picker.SetItem( Module ); newList.PushItem( picker ); Module->SetNeedsPlaced( true ); } break; case PLACE_ALL: Module->SetIsPlaced( false ); if( Module->IsLocked() ) break; // Module will be placed, add to undo. picker.SetItem( Module ); newList.PushItem( picker ); Module->SetNeedsPlaced( true ); break; case PLACE_INCREMENTAL: if( Module->IsLocked() ) { Module->SetIsPlaced( false ); break; } if( !Module->NeedsPlaced() ) { // Module will be placed, add to undo. picker.SetItem( Module ); newList.PushItem( picker ); Module->SetNeedsPlaced( true ); } break; } if( Module->NeedsPlaced() ) // Erase from screen { moduleCount++; Module->Draw( m_canvas, DC, GR_XOR ); } else { GenModuleOnBoard( Module ); } } // Undo command: commit list if( newList.GetCount() ) SaveCopyInUndoList( newList, UR_CHANGED ); int cnt = 0; int ii; wxString msg; while( ( Module = PickModule( this, DC ) ) != NULL ) { // Display some info about activity, module placement can take a while: msg.Printf( _("Place module %d of %d"), cnt, moduleCount ); SetStatusText( msg ); // Display fill area of interest, barriers, penalties. DrawInfoPlace( DC ); error = GetOptimalModulePlacement( Module, DC ); double BestScore = MinCout; PosOK = CurrPosition; if( error == ESC ) goto end_of_tst; // Determine if the best orientation of a module is 180. ii = Module->GetPlacementCost180() & 0x0F; if( ii != 0 ) { int Angle_Rot_Module = 1800; Rotate_Module( DC, Module, Angle_Rot_Module, false ); Module->CalculateBoundingBox(); error = GetOptimalModulePlacement( Module, DC ); MinCout *= OrientPenality[ii]; if( BestScore > MinCout ) // This orientation is best. { PosOK = CurrPosition; BestScore = MinCout; } else { Angle_Rot_Module = -1800; Rotate_Module( DC, Module, Angle_Rot_Module, false ); } if( error == ESC ) goto end_of_tst; } // Determine if the best orientation of a module is 90. ii = Module->GetPlacementCost90() & 0x0F; if( ii != 0 ) { int Angle_Rot_Module = 900; Rotate_Module( DC, Module, Angle_Rot_Module, false ); error = GetOptimalModulePlacement( Module, DC ); MinCout *= OrientPenality[ii]; if( BestScore > MinCout ) // This orientation is best. { PosOK = CurrPosition; BestScore = MinCout; } else { Angle_Rot_Module = -900; Rotate_Module( DC, Module, Angle_Rot_Module, false ); } if( error == ESC ) goto end_of_tst; } // Determine if the best orientation of a module is 270. ii = (Module->GetPlacementCost90() >> 4 ) & 0x0F; if( ii != 0 ) { int Angle_Rot_Module = 2700; Rotate_Module( DC, Module, Angle_Rot_Module, false ); error = GetOptimalModulePlacement( Module, DC ); MinCout *= OrientPenality[ii]; if( BestScore > MinCout ) // This orientation is best. { 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; // Place module. CurrPosition = GetCrossHairPosition(); SetCrossHairPosition( PosOK ); PlaceModule( Module, DC ); SetCrossHairPosition( CurrPosition ); Module->CalculateBoundingBox(); GenModuleOnBoard( Module ); Module->SetIsPlaced( true ); Module->SetNeedsPlaced( false ); } CurrPosition = memopos; RoutingMatrix.UnInitRoutingMatrix(); g_Route_Layer_TOP = lay_tmp_TOP; g_Route_Layer_BOTTOM = lay_tmp_BOTTOM; Module = GetBoard()->m_Modules; for( ; Module != NULL; Module = Module->Next() ) { Module->CalculateBoundingBox(); } GetBoard()->m_Status_Pcb = 0; Compile_Ratsnest( DC, true ); m_canvas->ReDraw( DC, true ); } void PCB_EDIT_FRAME::DrawInfoPlace( wxDC* DC ) { int ii, jj; EDA_COLOR_T color; int ox, oy; MATRIX_CELL top_state, bottom_state; GRSetDrawMode( DC, GR_COPY ); for( ii = 0; ii < RoutingMatrix.m_Nrows; ii++ ) { oy = RoutingMatrix.m_BrdBox.GetY() + ( ii * RoutingMatrix.m_GridRouting ); for( jj = 0; jj < RoutingMatrix.m_Ncols; jj++ ) { ox = RoutingMatrix.m_BrdBox.GetX() + (jj * RoutingMatrix.m_GridRouting); color = BLACK; top_state = RoutingMatrix.GetCell( ii, jj, TOP ); bottom_state = RoutingMatrix.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 // Display the filling and keep out regions. { if( RoutingMatrix.GetDist( ii, jj, TOP ) || RoutingMatrix.GetDist( ii, jj, BOTTOM ) ) color = DARKGRAY; } GRPutPixel( m_canvas->GetClipBox(), DC, ox, oy, color ); } } } int PCB_EDIT_FRAME::GenPlaceBoard() { wxString msg; RoutingMatrix.UnInitRoutingMatrix(); EDA_RECT bbox = GetBoard()->ComputeBoundingBox( true ); if( bbox.GetWidth() == 0 || bbox.GetHeight() == 0 ) { DisplayError( this, _( "No PCB edge found, unknown board size!" ) ); return 0; } RoutingMatrix.ComputeMatrixSize( GetBoard(), true ); int nbCells = RoutingMatrix.m_Ncols * RoutingMatrix.m_Nrows; m_messagePanel->EraseMsgBox(); msg.Printf( wxT( "%d" ), RoutingMatrix.m_Ncols ); m_messagePanel->SetMessage( 1, _( "Cols" ), msg, GREEN ); msg.Printf( wxT( "%d" ), RoutingMatrix.m_Nrows ); m_messagePanel->SetMessage( 7, _( "Lines" ), msg, GREEN ); msg.Printf( wxT( "%d" ), nbCells ); m_messagePanel->SetMessage( 14, _( "Cells." ), msg, YELLOW ); // Choose the number of board sides. RoutingMatrix.m_RoutingLayersCount = 2; RoutingMatrix.InitRoutingMatrix(); // Display memory usage. msg.Printf( wxT( "%d" ), RoutingMatrix.m_MemSize / 1024 ); m_messagePanel->SetMessage( 24, wxT( "Mem(Kb)" ), msg, CYAN ); g_Route_Layer_BOTTOM = LAYER_N_FRONT; if( RoutingMatrix.m_RoutingLayersCount > 1 ) g_Route_Layer_BOTTOM = LAYER_N_BACK; g_Route_Layer_TOP = LAYER_N_FRONT; // Place the edge layer segments TRACK TmpSegm( NULL ); TmpSegm.SetLayer( UNDEFINED_LAYER ); TmpSegm.SetNet( -1 ); TmpSegm.SetWidth( RoutingMatrix.m_GridRouting / 2 ); EDA_ITEM* PtStruct = GetBoard()->m_Drawings; for( ; PtStruct != NULL; PtStruct = PtStruct->Next() ) { DRAWSEGMENT* DrawSegm; switch( PtStruct->Type() ) { case PCB_LINE_T: DrawSegm = (DRAWSEGMENT*) PtStruct; if( DrawSegm->GetLayer() != EDGE_N ) break; TmpSegm.SetStart( DrawSegm->GetStart() ); TmpSegm.SetEnd( DrawSegm->GetEnd() ); TmpSegm.SetShape( DrawSegm->GetShape() ); TmpSegm.m_Param = DrawSegm->GetAngle(); TraceSegmentPcb( &TmpSegm, HOLE | CELL_is_EDGE, RoutingMatrix.m_GridRouting, WRITE_CELL ); break; case PCB_TEXT_T: default: break; } } // Mark cells of the routing matrix to CELL_is_ZONE // (i.e. availlable cell to place a module ) // Init a starting point of attachment to the area. RoutingMatrix.OrCell( RoutingMatrix.m_Nrows / 2, RoutingMatrix.m_Ncols / 2, BOTTOM, CELL_is_ZONE ); // find and mark all other availlable cells: for( int ii = 1; ii != 0; ) ii = propagate(); // Initialize top layer. to the same value as the bottom layer if( RoutingMatrix.m_BoardSide[TOP] ) memcpy( RoutingMatrix.m_BoardSide[TOP], RoutingMatrix.m_BoardSide[BOTTOM], nbCells * sizeof(MATRIX_CELL) ); return 1; } /* Place module on board. */ void PCB_EDIT_FRAME::GenModuleOnBoard( MODULE* Module ) { int ox, oy, fx, fy; int marge = RoutingMatrix.m_GridRouting / 2; int layerMask; D_PAD* Pad; ox = Module->GetBoundingBox().GetX() - marge; fx = Module->GetBoundingBox().GetRight() + marge; oy = Module->GetBoundingBox().GetY() - marge; fy = Module->GetBoundingBox().GetBottom() + marge; if( ox < RoutingMatrix.m_BrdBox.GetX() ) ox = RoutingMatrix.m_BrdBox.GetX(); if( ox > RoutingMatrix.m_BrdBox.GetRight() ) ox = RoutingMatrix.m_BrdBox.GetRight(); if( fx < RoutingMatrix.m_BrdBox.GetX() ) fx = RoutingMatrix.m_BrdBox.GetX(); if( fx > RoutingMatrix.m_BrdBox.GetRight() ) fx = RoutingMatrix.m_BrdBox.GetRight(); if( oy < RoutingMatrix.m_BrdBox.GetY() ) oy = RoutingMatrix.m_BrdBox.GetY(); if( oy > RoutingMatrix.m_BrdBox.GetBottom() ) oy = RoutingMatrix.m_BrdBox.GetBottom(); if( fy < RoutingMatrix.m_BrdBox.GetY() ) fy = RoutingMatrix.m_BrdBox.GetY(); if( fy > RoutingMatrix.m_BrdBox.GetBottom() ) fy = RoutingMatrix.m_BrdBox.GetBottom(); layerMask = 0; if( Module->GetLayer() == LAYER_N_FRONT ) layerMask = LAYER_FRONT; if( Module->GetLayer() == LAYER_N_BACK ) layerMask = LAYER_BACK; TraceFilledRectangle( ox, oy, fx, fy, layerMask, CELL_is_MODULE, WRITE_OR_CELL ); int trackWidth = GetBoard()->m_NetClasses.GetDefault()->GetTrackWidth(); int clearance = GetBoard()->m_NetClasses.GetDefault()->GetClearance(); // Trace pads and surface safely. marge = trackWidth + clearance; for( Pad = Module->Pads(); Pad != NULL; Pad = Pad->Next() ) { ::PlacePad( Pad, CELL_is_MODULE, marge, WRITE_OR_CELL ); } // Trace clearance. marge = ( RoutingMatrix.m_GridRouting * Module->GetPadCount() ) / GAIN; CreateKeepOutRectangle( ox, oy, fx, fy, marge, KEEP_OUT_MARGIN, layerMask ); } int PCB_EDIT_FRAME::GetOptimalModulePlacement( MODULE* aModule, wxDC* aDC ) { int cx, cy; int ox, oy, fx, fy; // occupying part of the module focuses on the cursor int error = 1; int showRat = 0; wxPoint LastPosOK; double mincout, cout, Score; int keepOut; bool TstOtherSide; bool showRats = g_Show_Module_Ratsnest; g_Show_Module_Ratsnest = false; SetMsgPanel( aModule ); LastPosOK.x = RoutingMatrix.m_BrdBox.GetX(); LastPosOK.y = RoutingMatrix.m_BrdBox.GetY(); cx = aModule->GetPosition().x; cy = aModule->GetPosition().y; ox = aModule->GetBoundingBox().GetX() - cx; fx = aModule->GetBoundingBox().GetWidth() + ox; oy = aModule->GetBoundingBox().GetY() - cy; fy = aModule->GetBoundingBox().GetHeight() + oy; CurrPosition.x = RoutingMatrix.m_BrdBox.GetX() - ox; CurrPosition.y = RoutingMatrix.m_BrdBox.GetY() - oy; // Module placement on grid. CurrPosition.x -= CurrPosition.x % RoutingMatrix.m_GridRouting; CurrPosition.y -= CurrPosition.y % RoutingMatrix.m_GridRouting; g_Offset_Module.x = cx - CurrPosition.x; g_Offset_Module.y = cy - CurrPosition.y; GetBoard()->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK; /* Test pads, a printed circuit with components of the 2 dimensions * can become a component on opposite side if there is at least 1 patch * appearing on the other side. */ TstOtherSide = false; if( RoutingMatrix.m_RoutingLayersCount > 1 ) { D_PAD* Pad; int otherLayerMask = LAYER_BACK; if( aModule->GetLayer() == LAYER_N_BACK ) otherLayerMask = LAYER_FRONT; for( Pad = aModule->Pads(); Pad != NULL; Pad = Pad->Next() ) { if( ( Pad->GetLayerMask() & otherLayerMask ) == 0 ) continue; TstOtherSide = true; break; } } DrawModuleOutlines( m_canvas, aDC, aModule ); mincout = -1.0; SetStatusText( wxT( "Score ??, pos ??" ) ); for( ; CurrPosition.x < RoutingMatrix.m_BrdBox.GetRight() - fx; CurrPosition.x += RoutingMatrix.m_GridRouting ) { wxYield(); if( m_canvas->GetAbortRequest() ) { if( IsOK( this, _( "OK to abort?" ) ) ) return ESC; else m_canvas->SetAbortRequest( false ); } cx = aModule->GetPosition().x; cy = aModule->GetPosition().y; aModule->GetBoundingBox().SetX( ox + CurrPosition.x ); aModule->GetBoundingBox().SetY( oy + CurrPosition.y ); DrawModuleOutlines( m_canvas, aDC, aModule ); g_Offset_Module.x = cx - CurrPosition.x; CurrPosition.y = RoutingMatrix.m_BrdBox.GetY() - oy; // Placement on grid. CurrPosition.y -= CurrPosition.y % RoutingMatrix.m_GridRouting; DrawModuleOutlines( m_canvas, aDC, aModule ); for( ; CurrPosition.y < RoutingMatrix.m_BrdBox.GetBottom() - fy; CurrPosition.y += RoutingMatrix.m_GridRouting ) { #ifndef USE_WX_OVERLAY // Erase traces. DrawModuleOutlines( m_canvas, aDC, aModule ); if( showRat ) Compute_Ratsnest_PlaceModule( aDC ); #endif showRat = 0; aModule->GetBoundingBox().SetX( ox + CurrPosition.x ); aModule->GetBoundingBox().SetY( oy + CurrPosition.y ); g_Offset_Module.y = cy - CurrPosition.y; #ifndef USE_WX_OVERLAY DrawModuleOutlines( m_canvas, aDC, aModule ); #endif keepOut = TstModuleOnBoard( GetBoard(), aModule, TstOtherSide ); if( keepOut >= 0 ) // i.e. if the module can be put here { error = 0; build_ratsnest_module( aModule ); cout = Compute_Ratsnest_PlaceModule( aDC ); showRat = 1; Score = cout + keepOut; if( (mincout >= Score ) || (mincout < 0 ) ) { LastPosOK = CurrPosition; mincout = Score; wxString msg; msg.Printf( wxT( "Score %g, pos %3.4g, %3.4g" ), mincout, (double) LastPosOK.x / 10000, (double) LastPosOK.y / 10000 ); SetStatusText( msg ); } } if( showRat ) Compute_Ratsnest_PlaceModule( aDC ); showRat = 0; } } DrawModuleOutlines( m_canvas, aDC, aModule ); // erasing the last traces g_Show_Module_Ratsnest = showRats; if( showRat ) Compute_Ratsnest_PlaceModule( aDC ); // Regeneration of the modified variable. aModule->GetBoundingBox().SetX( ox + cx ); aModule->GetBoundingBox().SetY( oy + cy ); CurrPosition = LastPosOK; GetBoard()->m_Status_Pcb &= ~( RATSNEST_ITEM_LOCAL_OK | LISTE_PAD_OK ); MinCout = mincout; return error; } /* Test if the rectangular area (ux, ux .. y0, y1): * - is a free zone (except OCCUPED_By_MODULE returns) * - is on the working surface of the board (otherwise returns OUT_OF_BOARD) * * Returns 0 if OK */ int TstRectangle( BOARD* Pcb, int ux0, int uy0, int ux1, int uy1, int side ) { int row, col; int row_min, row_max, col_min, col_max; unsigned int data; ux0 -= Pcb->GetBoundingBox().GetX(); uy0 -= Pcb->GetBoundingBox().GetY(); ux1 -= Pcb->GetBoundingBox().GetX(); uy1 -= Pcb->GetBoundingBox().GetY(); row_max = uy1 / RoutingMatrix.m_GridRouting; col_max = ux1 / RoutingMatrix.m_GridRouting; row_min = uy0 / RoutingMatrix.m_GridRouting; if( uy0 > row_min * RoutingMatrix.m_GridRouting ) row_min++; col_min = ux0 / RoutingMatrix.m_GridRouting; if( ux0 > col_min * RoutingMatrix.m_GridRouting ) col_min++; if( row_min < 0 ) row_min = 0; if( row_max >= ( RoutingMatrix.m_Nrows - 1 ) ) row_max = RoutingMatrix.m_Nrows - 1; if( col_min < 0 ) col_min = 0; if( col_max >= ( RoutingMatrix.m_Ncols - 1 ) ) col_max = RoutingMatrix.m_Ncols - 1; for( row = row_min; row <= row_max; row++ ) { for( col = col_min; col <= col_max; col++ ) { data = RoutingMatrix.GetCell( row, col, side ); if( ( data & CELL_is_ZONE ) == 0 ) return OUT_OF_BOARD; if( data & CELL_is_MODULE ) return OCCUPED_By_MODULE; } } return 0; } /* Calculates and returns the clearance area of the rectangular surface * (ux, ux .. y0, y1): * (Sum of cells in terms of distance) */ unsigned int CalculateKeepOutArea( int ux0, int uy0, int ux1, int uy1, int side ) { int row, col; int row_min, row_max, col_min, col_max; unsigned int keepOut; ux0 -= RoutingMatrix.m_BrdBox.GetX(); uy0 -= RoutingMatrix.m_BrdBox.GetY(); ux1 -= RoutingMatrix.m_BrdBox.GetX(); uy1 -= RoutingMatrix.m_BrdBox.GetY(); row_max = uy1 / RoutingMatrix.m_GridRouting; col_max = ux1 / RoutingMatrix.m_GridRouting; row_min = uy0 / RoutingMatrix.m_GridRouting; if( uy0 > row_min * RoutingMatrix.m_GridRouting ) row_min++; col_min = ux0 / RoutingMatrix.m_GridRouting; if( ux0 > col_min * RoutingMatrix.m_GridRouting ) col_min++; if( row_min < 0 ) row_min = 0; if( row_max >= ( RoutingMatrix.m_Nrows - 1 ) ) row_max = RoutingMatrix.m_Nrows - 1; if( col_min < 0 ) col_min = 0; if( col_max >= ( RoutingMatrix.m_Ncols - 1 ) ) col_max = RoutingMatrix.m_Ncols - 1; keepOut = 0; for( row = row_min; row <= row_max; row++ ) { for( col = col_min; col <= col_max; col++ ) { keepOut += RoutingMatrix.GetDist( row, col, side ); } } return keepOut; } /* Test if the module can be placed on the board. * Returns the value TstRectangle(). * Module is known by its rectangle */ int TstModuleOnBoard( BOARD* Pcb, MODULE* Module, bool TstOtherSide ) { int ox, oy, fx, fy; int error, marge, side, otherside; side = TOP; otherside = BOTTOM; if( Module->GetLayer() == LAYER_N_BACK ) { side = BOTTOM; otherside = TOP; } ox = Module->GetBoundingBox().GetX(); fx = Module->GetBoundingBox().GetRight(); oy = Module->GetBoundingBox().GetY(); fy = Module->GetBoundingBox().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 = ( RoutingMatrix.m_GridRouting * Module->GetPadCount() ) / GAIN; return CalculateKeepOutArea( ox - marge, oy - marge, fx + marge, fy + marge, side ); } double PCB_EDIT_FRAME::Compute_Ratsnest_PlaceModule( wxDC* DC ) { double cout, icout; wxPoint start; // start point of a ratsnest wxPoint end; // end point of a ratsnest int dx, dy; if( ( GetBoard()->m_Status_Pcb & RATSNEST_ITEM_LOCAL_OK ) == 0 ) return -1; cout = 0; EDA_COLOR_T color = g_ColorsSettings.GetItemColor(RATSNEST_VISIBLE); if( AutoPlaceShowAll ) GRSetDrawMode( DC, GR_XOR ); for( unsigned ii = 0; ii < GetBoard()->m_LocalRatsnest.size(); ii++ ) { RATSNEST_ITEM* pt_local_rats_nest = &GetBoard()->m_LocalRatsnest[ii]; if( ( pt_local_rats_nest->m_Status & LOCAL_RATSNEST_ITEM ) ) continue; // Skip ratsnest between 2 pads of the current module // Skip modules not inside the board area MODULE * module = pt_local_rats_nest->m_PadEnd->GetParent(); if( !RoutingMatrix.m_BrdBox.Contains( module->GetPosition() ) ) continue; start = pt_local_rats_nest->m_PadStart->GetPosition() - g_Offset_Module; end = pt_local_rats_nest->m_PadEnd->GetPosition(); #ifndef USE_WX_OVERLAY if( AutoPlaceShowAll ) { GRLine( m_canvas->GetClipBox(), DC, start, end, 0, color ); } #endif // Cost of the ratsnest. dx = end.x - start.x; dy = end.y - start.y; dx = abs( dx ); dy = abs( dy ); // ttry to have always dx >= dy to calculate the cost of the rastsnet if( dx < dy ) EXCHG( dx, dy ); // Cost of the connection = lenght + penalty due to the slope // dx is the biggest lenght relative to the X or Y axis // the penalty is max for 45 degrees ratsnests, // and 0 for horizontal or vertical ratsnests. // For Horizontal and Vertical ratsnests, dy = 0; icout = hypot( dx, dy * 2.0 ); cout += icout; // Total cost = sum of costs of each connection } return cout; } /** * Function CreateKeepOutRectangle * builds the cost map: * Cells ( in Dist map ) inside the rect x0,y0 a x1,y1 are * incremented by value aKeepOut * Cell outside this rectangle, but inside the rectangle * x0,y0 -marge to x1,y1 + marge are incremented by a decreasing value * (aKeepOut ... 0). The decreasing value de pends on the distance to the first rectangle * Therefore the cost is high in rect x0,y0 a x1,y1, and decrease outside this rectangle */ void CreateKeepOutRectangle( int ux0, int uy0, int ux1, int uy1, int marge, int aKeepOut, int aLayerMask ) { int row, col; int row_min, row_max, col_min, col_max, pmarge; int trace = 0; DIST_CELL data, LocalKeepOut; int lgain, cgain; if( aLayerMask & GetLayerMask( g_Route_Layer_BOTTOM ) ) trace = 1; // Trace on bottom layer. if( ( aLayerMask & GetLayerMask( g_Route_Layer_TOP ) ) && RoutingMatrix.m_RoutingLayersCount ) trace |= 2; // Trace on top layer. if( trace == 0 ) return; ux0 -= RoutingMatrix.m_BrdBox.GetX(); uy0 -= RoutingMatrix.m_BrdBox.GetY(); ux1 -= RoutingMatrix.m_BrdBox.GetX(); uy1 -= RoutingMatrix.m_BrdBox.GetY(); ux0 -= marge; ux1 += marge; uy0 -= marge; uy1 += marge; pmarge = marge / RoutingMatrix.m_GridRouting; if( pmarge < 1 ) pmarge = 1; // Calculate the coordinate limits of the rectangle. row_max = uy1 / RoutingMatrix.m_GridRouting; col_max = ux1 / RoutingMatrix.m_GridRouting; row_min = uy0 / RoutingMatrix.m_GridRouting; if( uy0 > row_min * RoutingMatrix.m_GridRouting ) row_min++; col_min = ux0 / RoutingMatrix.m_GridRouting; if( ux0 > col_min * RoutingMatrix.m_GridRouting ) col_min++; if( row_min < 0 ) row_min = 0; if( row_max >= (RoutingMatrix.m_Nrows - 1) ) row_max = RoutingMatrix.m_Nrows - 1; if( col_min < 0 ) col_min = 0; if( col_max >= (RoutingMatrix.m_Ncols - 1) ) col_max = RoutingMatrix.m_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; LocalKeepOut = aKeepOut; 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 ) LocalKeepOut = ( LocalKeepOut * cgain ) / 256; if( trace & 1 ) { data = RoutingMatrix.GetDist( row, col, BOTTOM ) + LocalKeepOut; RoutingMatrix.SetDist( row, col, BOTTOM, data ); } if( trace & 2 ) { data = RoutingMatrix.GetDist( row, col, TOP ); data = std::max( data, LocalKeepOut ); RoutingMatrix.SetDist( row, col, TOP, data ); } } } } // Sort routines static bool Tri_PlaceModules( MODULE* ref, MODULE* compare ) { double ff1, ff2; ff1 = ref->GetArea() * ref->GetPadCount(); ff2 = compare->GetArea() * compare->GetPadCount(); return ff2 < ff1; } static bool Tri_RatsModules( MODULE* ref, MODULE* compare ) { double ff1, ff2; ff1 = ref->GetArea() * ref->GetFlag(); ff2 = compare->GetArea() * compare->GetFlag(); return ff2 < ff1; } /** * Function PickModule * find the "best" module place * The criteria are: * - Maximum ratsnest with modules already placed * - Max size, and number of pads max */ static MODULE* PickModule( PCB_EDIT_FRAME* pcbframe, wxDC* DC ) { MODULE* Module; std::vector moduleList; // Build sorted footprints list (sort by decreasing size ) Module = pcbframe->GetBoard()->m_Modules; for( ; Module != NULL; Module = Module->Next() ) { Module->CalculateBoundingBox(); moduleList.push_back(Module); } sort( moduleList.begin(), moduleList.end(), Tri_PlaceModules ); for( unsigned ii = 0; ii < moduleList.size(); ii++ ) { Module = moduleList[ii]; Module->SetFlag( 0 ); if( !Module->NeedsPlaced() ) continue; pcbframe->GetBoard()->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK; pcbframe->SetMsgPanel( Module ); pcbframe->build_ratsnest_module( Module ); // Calculate external ratsnest. for( unsigned ii = 0; ii < pcbframe->GetBoard()->m_LocalRatsnest.size(); ii++ ) { if( ( pcbframe->GetBoard()->m_LocalRatsnest[ii].m_Status & LOCAL_RATSNEST_ITEM ) == 0 ) Module->IncrementFlag(); } } pcbframe->GetBoard()->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK; sort( moduleList.begin(), moduleList.end(), Tri_RatsModules ); // Search for "best" module. MODULE* bestModule = NULL; MODULE* altModule = NULL; for( unsigned ii = 0; ii < moduleList.size(); ii++ ) { Module = moduleList[ii]; if( !Module->NeedsPlaced() ) continue; altModule = Module; if( Module->GetFlag() == 0 ) continue; bestModule = Module; break; } if( bestModule ) return bestModule; else return altModule; } /** * Function propagate * Used only in autoplace calculations * Uses the routing matrix to fill the cells within the zone * Search and mark cells within the zone, and agree with DRC options. * Requirements: * Start from an initial point, to fill zone * The zone must have no "copper island" * Algorithm: * If the current cell has a neighbor flagged as "cell in the zone", it * become a cell in the zone * The first point in the zone is the starting point * 4 searches within the matrix are made: * 1 - Left to right and top to bottom * 2 - Right to left and top to bottom * 3 - bottom to top and Right to left * 4 - bottom to top and Left to right * Given the current cell, for each search, we consider the 2 neighbor cells * the previous cell on the same line and the previous cell on the same column. * * This function can request some iterations * Iterations are made until no cell is added to the zone. * @return added cells count (i.e. which the attribute CELL_is_ZONE is set) */ int propagate() { int row, col; long current_cell, old_cell_H; std::vector< long > pt_cell_V; int nbpoints = 0; #define NO_CELL_ZONE (HOLE | CELL_is_EDGE | CELL_is_ZONE) pt_cell_V.reserve( std::max( RoutingMatrix.m_Nrows, RoutingMatrix.m_Ncols ) ); fill( pt_cell_V.begin(), pt_cell_V.end(), 0 ); // Search from left to right and top to bottom. for( row = 0; row < RoutingMatrix.m_Nrows; row++ ) { old_cell_H = 0; for( col = 0; col < RoutingMatrix.m_Ncols; col++ ) { current_cell = RoutingMatrix.GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; if( current_cell == 0 ) // a free cell is found { if( (old_cell_H & CELL_is_ZONE) || (pt_cell_V[col] & CELL_is_ZONE) ) { RoutingMatrix.OrCell( row, col, BOTTOM, CELL_is_ZONE ); current_cell = CELL_is_ZONE; nbpoints++; } } pt_cell_V[col] = old_cell_H = current_cell; } } // Search from right to left and top to bottom/ fill( pt_cell_V.begin(), pt_cell_V.end(), 0 ); for( row = 0; row < RoutingMatrix.m_Nrows; row++ ) { old_cell_H = 0; for( col = RoutingMatrix.m_Ncols - 1; col >= 0; col-- ) { current_cell = RoutingMatrix.GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; if( current_cell == 0 ) // a free cell is found { if( (old_cell_H & CELL_is_ZONE) || (pt_cell_V[col] & CELL_is_ZONE) ) { RoutingMatrix.OrCell( row, col, BOTTOM, CELL_is_ZONE ); current_cell = CELL_is_ZONE; nbpoints++; } } pt_cell_V[col] = old_cell_H = current_cell; } } // Search from bottom to top and right to left. fill( pt_cell_V.begin(), pt_cell_V.end(), 0 ); for( col = RoutingMatrix.m_Ncols - 1; col >= 0; col-- ) { old_cell_H = 0; for( row = RoutingMatrix.m_Nrows - 1; row >= 0; row-- ) { current_cell = RoutingMatrix.GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; if( current_cell == 0 ) // a free cell is found { if( (old_cell_H & CELL_is_ZONE) || (pt_cell_V[row] & CELL_is_ZONE) ) { RoutingMatrix.OrCell( row, col, BOTTOM, CELL_is_ZONE ); current_cell = CELL_is_ZONE; nbpoints++; } } pt_cell_V[row] = old_cell_H = current_cell; } } // Search from bottom to top and left to right. fill( pt_cell_V.begin(), pt_cell_V.end(), 0 ); for( col = 0; col < RoutingMatrix.m_Ncols; col++ ) { old_cell_H = 0; for( row = RoutingMatrix.m_Nrows - 1; row >= 0; row-- ) { current_cell = RoutingMatrix.GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; if( current_cell == 0 ) // a free cell is found { if( (old_cell_H & CELL_is_ZONE) || (pt_cell_V[row] & CELL_is_ZONE) ) { RoutingMatrix.OrCell( row, col, BOTTOM, CELL_is_ZONE ); current_cell = CELL_is_ZONE; nbpoints++; } } pt_cell_V[row] = old_cell_H = current_cell; } } return nbpoints; }