diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index d9f55dd4f0..a77f8ebfdb 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -205,6 +205,9 @@ set( PCBNEW_CLASS_SRCS autorouter/rect_placement/rect_placement.cpp autorouter/spread_footprints.cpp + autorouter/ar_autoplacer.cpp + autorouter/ar_matrix.cpp + autorouter/autoplacer_tool.cpp action_plugin.cpp append_board_to_current.cpp diff --git a/pcbnew/autorouter/ar_autoplacer.cpp b/pcbnew/autorouter/ar_autoplacer.cpp index 289c9db36d..b68044f573 100644 --- a/pcbnew/autorouter/ar_autoplacer.cpp +++ b/pcbnew/autorouter/ar_autoplacer.cpp @@ -1,8 +1,3 @@ -/** - * @file auto_place_footprints.cpp - * @brief Functions to automatically place Footprints on a board. - */ - /* * This program source code file is part of KiCad, a free EDA CAD application. * @@ -39,26 +34,31 @@ #include #include -#include -#include - #include #include #include #include -#include -#include -#include +#include +#include -#define GAIN 16 -#define KEEP_OUT_MARGIN 500 +#include +#include +#include + +#include "ar_matrix.h" +#include "ar_cell.h" +#include "ar_autoplacer.h" + +#define AR_GAIN 16 +#define AR_KEEPOUT_MARGIN 500 +#define AR_ABORT_PLACEMENT -1 /* Penalty (cost) for CntRot90 and CntRot180: * CntRot90 and CntRot180 are from 0 (rotation allowed) to 10 (rotation not allowed) */ -static const double OrientPenality[11] = +static const double OrientationPenalty[11] = { 2.0, // CntRot = 0 rotation prohibited 1.9, // CntRot = 1 @@ -73,447 +73,81 @@ static const double OrientPenality[11] = 1.0 // CntRot = 10 rotation authorized, no penalty }; -// Cell states. -#define OUT_OF_BOARD -2 -#define OCCUPED_By_MODULE -1 -#define FREE_CELL 0 - -static wxPoint CurrPosition; // Current position of the current module placement -double MinCout; - - -/* generates the Routing matrix, used to fing the best placement - * of a footprint. - * Allocate a "bitmap" which is an image of the real board - * the bitmap handles: - * - The free areas - * - penalties (cell not occupied, but near occupied areas) - * - cells occupied by footprints, board cutout ... - */ -int genPlacementRoutingMatrix( BOARD* aBrd, EDA_MSG_PANEL* messagePanel ); - -/* searches for the optimal position of aModule. - * return 1 if placement impossible or 0 if OK. - */ -static int getOptimalModulePlacement( PCB_EDIT_FRAME* aFrame, - MODULE* aModule, wxDC* aDC ); - -/* - * Function compute_Ratsnest_PlaceModule - * displays the module's ratsnest during displacement, and assess the "cost" - * of the position. - * - * The cost is the longest ratsnest distance with penalty for connections - * approaching 45 degrees. - */ -static double compute_Ratsnest_PlaceModule( BOARD* aBrd ); - -/* Place a footprint on the Routing matrix. - */ -void genModuleOnRoutingMatrix( MODULE* Module ); -/* - * Displays the Placement/Routing matrix on the screen - */ -static void drawPlacementRoutingMatrix( BOARD* aBrd, wxDC* DC ); - -static int TstModuleOnBoard( BOARD* Pcb, MODULE* Module, bool TstOtherSide ); - -static void CreateKeepOutRectangle( int ux0, int uy0, int ux1, int uy1, - int marge, int aKeepOut, LSET 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 ) +AR_AUTOPLACER::AR_AUTOPLACER( BOARD* aBoard ) { - MODULE* currModule = NULL; - wxPoint PosOK; - wxPoint memopos; - int error; - PCB_LAYER_ID lay_tmp_TOP, lay_tmp_BOTTOM; + m_board = aBoard; + m_connectivity.reset( new CONNECTIVITY_DATA ); - // Undo: init list - PICKED_ITEMS_LIST newList; + for( auto mod : m_board->Modules() ) + m_connectivity->Add( mod ); - 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 < Millimeter2iu( 0.25 ) ) - RoutingMatrix.m_GridRouting = Millimeter2iu( 0.25 ); - - // Compute module parameters used in auto place - if( genPlacementRoutingMatrix( GetBoard(), m_messagePanel ) == 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 - { - genModuleOnRoutingMatrix( Module ); - } - } - - // Undo command: prepare list - if( newList.GetCount() ) - SaveCopyInUndoList( newList, UR_CHANGED ); - - int cnt = 0; - wxString msg; - - while( ( Module = PickModule( this, DC ) ) != NULL ) - { - // Display some info about activity, module placement can take a while: - msg.Printf( _( "Place footprint %d of %d" ), cnt, moduleCount ); - SetStatusText( msg ); - - double initialOrient = Module->GetOrientation(); - // Display fill area of interest, barriers, penalties. - drawPlacementRoutingMatrix( GetBoard(), DC ); - - error = getOptimalModulePlacement( this, Module, DC ); - double bestScore = MinCout; - double bestRotation = 0.0; - int rotAllowed; - PosOK = CurrPosition; - - if( error == ESC ) - goto end_of_tst; - - // Try orientations 90, 180, 270 degrees from initial orientation - rotAllowed = Module->GetPlacementCost180(); - - if( rotAllowed != 0 ) - { - Rotate_Module( DC, Module, 1800.0, true ); - error = getOptimalModulePlacement( this, Module, DC ); - MinCout *= OrientPenality[rotAllowed]; - - if( bestScore > MinCout ) // This orientation is better. - { - PosOK = CurrPosition; - bestScore = MinCout; - bestRotation = 1800.0; - } - else - { - Rotate_Module( DC, Module, initialOrient, false ); - } - - if( error == ESC ) - goto end_of_tst; - } - - // Determine if the best orientation of a module is 90. - rotAllowed = Module->GetPlacementCost90(); - - if( rotAllowed != 0 ) - { - Rotate_Module( DC, Module, 900.0, true ); - error = getOptimalModulePlacement( this, Module, DC ); - MinCout *= OrientPenality[rotAllowed]; - - if( bestScore > MinCout ) // This orientation is better. - { - PosOK = CurrPosition; - bestScore = MinCout; - bestRotation = 900.0; - } - else - { - Rotate_Module( DC, Module, initialOrient, false ); - } - - if( error == ESC ) - goto end_of_tst; - } - - // Determine if the best orientation of a module is -90. - if( rotAllowed != 0 ) - { - Rotate_Module( DC, Module, 2700.0, true ); - error = getOptimalModulePlacement( this, Module, DC ); - MinCout *= OrientPenality[rotAllowed]; - - if( bestScore > MinCout ) // This orientation is better. - { - PosOK = CurrPosition; - bestScore = MinCout; - bestRotation = 2700.0; - } - else - { - Rotate_Module( DC, Module, initialOrient, false ); - } - - if( error == ESC ) - goto end_of_tst; - } - -end_of_tst: - - if( error == ESC ) - break; - - // Place module. - CurrPosition = GetCrossHairPosition(); - SetCrossHairPosition( PosOK ); - - PlaceModule( Module, DC ); - - bestRotation += initialOrient; - - if( bestRotation != Module->GetOrientation() ) - Rotate_Module( DC, Module, bestRotation, false ); - - SetCrossHairPosition( CurrPosition ); - - Module->CalculateBoundingBox(); - - genModuleOnRoutingMatrix( 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 ); + m_gridSize = Millimeter2iu( 0.5 ); + m_progressReporter = nullptr; + m_refreshCallback = nullptr; } -void drawPlacementRoutingMatrix( BOARD* aBrd, wxDC* DC ) +void AR_AUTOPLACER::placeModule( MODULE* aModule, bool aDoNotRecreateRatsnest, const wxPoint& aPos ) { - int ii, jj; - COLOR4D color; - int ox, oy; - MATRIX_CELL top_state, bottom_state; + if( !aModule ) + return; - 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 = COLOR4D::BLACK; - - top_state = RoutingMatrix.GetCell( ii, jj, TOP ); - bottom_state = RoutingMatrix.GetCell( ii, jj, BOTTOM ); - - if( top_state & CELL_is_ZONE ) - color = COLOR4D( BLUE ); - - // obstacles - if( ( top_state & CELL_is_EDGE ) || ( bottom_state & CELL_is_EDGE ) ) - color = COLOR4D::WHITE; - else if( top_state & ( HOLE | CELL_is_MODULE ) ) - color = COLOR4D( LIGHTRED ); - else if( bottom_state & (HOLE | CELL_is_MODULE) ) - color = COLOR4D( LIGHTGREEN ); - else // Display the filling and keep out regions. - { - if( RoutingMatrix.GetDist( ii, jj, TOP ) - || RoutingMatrix.GetDist( ii, jj, BOTTOM ) ) - color = DARKGRAY; - } - - GRPutPixel( NULL, DC, ox, oy, color ); - } - } + aModule->SetPosition( aPos ); + m_connectivity->Update( aModule ); } -int genPlacementRoutingMatrix( BOARD* aBrd, EDA_MSG_PANEL* messagePanel ) +int AR_AUTOPLACER::genPlacementRoutingMatrix() { - wxString msg; + m_matrix.UnInitRoutingMatrix(); - RoutingMatrix.UnInitRoutingMatrix(); - - EDA_RECT bbox = aBrd->GetBoardEdgesBoundingBox(); + EDA_RECT bbox = m_board->GetBoardEdgesBoundingBox(); if( bbox.GetWidth() == 0 || bbox.GetHeight() == 0 ) { - DisplayError( NULL, _( "No PCB edge found, unknown board size!" ) ); + //DisplayError( NULL, _( "No PCB edge found, unknown board size!" ) ); + // fixme: no wx here return 0; } - RoutingMatrix.ComputeMatrixSize( aBrd, true ); - int nbCells = RoutingMatrix.m_Ncols * RoutingMatrix.m_Nrows; - - messagePanel->EraseMsgBox(); - msg.Printf( wxT( "%d" ), RoutingMatrix.m_Ncols ); - messagePanel->SetMessage( 1, _( "Cols" ), msg, GREEN ); - msg.Printf( wxT( "%d" ), RoutingMatrix.m_Nrows ); - messagePanel->SetMessage( 7, _( "Lines" ), msg, GREEN ); - msg.Printf( wxT( "%d" ), nbCells ); - messagePanel->SetMessage( 14, _( "Cells." ), msg, YELLOW ); + m_matrix.ComputeMatrixSize( bbox ); + int nbCells = m_matrix.m_Ncols * m_matrix.m_Nrows; // Choose the number of board sides. - RoutingMatrix.m_RoutingLayersCount = 2; + m_matrix.m_RoutingLayersCount = 2; + m_matrix.InitRoutingMatrix(); - RoutingMatrix.InitRoutingMatrix(); + m_matrix.m_routeLayerBottom = F_Cu; - // Display memory usage. - msg.Printf( wxT( "%d" ), RoutingMatrix.m_MemSize / 1024 ); - messagePanel->SetMessage( 24, wxT( "Mem(Kb)" ), msg, CYAN ); + if( m_matrix.m_RoutingLayersCount > 1 ) + m_matrix.m_routeLayerBottom = B_Cu; - g_Route_Layer_BOTTOM = F_Cu; - - if( RoutingMatrix.m_RoutingLayersCount > 1 ) - g_Route_Layer_BOTTOM = B_Cu; - - g_Route_Layer_TOP = F_Cu; + m_matrix.m_routeLayerTop = F_Cu; // Place the edge layer segments - TRACK TmpSegm( NULL ); + TRACK tmp( NULL ); - TmpSegm.SetLayer( UNDEFINED_LAYER ); - TmpSegm.SetNetCode( -1 ); - TmpSegm.SetWidth( RoutingMatrix.m_GridRouting / 2 ); + tmp.SetLayer( UNDEFINED_LAYER ); + tmp.SetNetCode( -1 ); + tmp.SetWidth( m_matrix.m_GridRouting / 2 ); - for( auto PtStruct : aBrd->Drawings() ) + for( auto drawing : m_board->Drawings() ) { DRAWSEGMENT* DrawSegm; - switch( PtStruct->Type() ) + switch( drawing->Type() ) { case PCB_LINE_T: - DrawSegm = (DRAWSEGMENT*) PtStruct; + DrawSegm = (DRAWSEGMENT*) drawing; if( DrawSegm->GetLayer() != Edge_Cuts ) break; - TraceSegmentPcb( DrawSegm, HOLE | CELL_is_EDGE, - RoutingMatrix.m_GridRouting, WRITE_CELL ); + + //printf("addSeg %p grid %d\n", DrawSegm, m_matrix.m_GridRouting ); + m_matrix.TraceSegmentPcb( DrawSegm, CELL_IS_HOLE | CELL_IS_EDGE, + m_matrix.m_GridRouting, AR_MATRIX::WRITE_CELL ); break; case PCB_TEXT_T: @@ -522,641 +156,37 @@ int genPlacementRoutingMatrix( BOARD* aBrd, EDA_MSG_PANEL* messagePanel ) } } - // Mark cells of the routing matrix to CELL_is_ZONE + // 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 ); + m_matrix.OrCell( m_matrix.m_Nrows / 2, m_matrix.m_Ncols / 2, + AR_SIDE_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) ); + if( m_matrix.m_BoardSide[AR_SIDE_TOP] ) + memcpy( m_matrix.m_BoardSide[AR_SIDE_TOP], m_matrix.m_BoardSide[AR_SIDE_BOTTOM], + nbCells * sizeof(AR_MATRIX::MATRIX_CELL) ); return 1; } -/* Place module on Routing matrix. - */ -void genModuleOnRoutingMatrix( MODULE* Module ) +void AR_AUTOPLACER::rotateModule( MODULE* module, double angle, bool incremental ) { - int ox, oy, fx, fy; - LSET layerMask; - D_PAD* Pad; - - EDA_RECT fpBBox = Module->GetBoundingBox(); - - fpBBox.Inflate( RoutingMatrix.m_GridRouting / 2 ); - ox = fpBBox.GetX(); - fx = fpBBox.GetRight(); - oy = fpBBox.GetY(); - fy = fpBBox.GetBottom(); - - 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(); - - if( Module->GetLayer() == F_Cu ) - layerMask.set( F_Cu ); - - if( Module->GetLayer() == B_Cu ) - layerMask.set( B_Cu ); - - TraceFilledRectangle( ox, oy, fx, fy, layerMask, - CELL_is_MODULE, WRITE_OR_CELL ); - - // Trace pads + clearance areas. - for( Pad = Module->Pads(); Pad != NULL; Pad = Pad->Next() ) - { - int margin = (RoutingMatrix.m_GridRouting / 2) + Pad->GetClearance(); - ::PlacePad( Pad, CELL_is_MODULE, margin, WRITE_OR_CELL ); - } - - // Trace clearance. - int margin = ( RoutingMatrix.m_GridRouting * Module->GetPadCount() ) / GAIN; - CreateKeepOutRectangle( ox, oy, fx, fy, margin, KEEP_OUT_MARGIN, layerMask ); -} - - -// A minor helper function to draw a bounding box: -inline void draw_FootprintRect( EDA_RECT* aClipBox, wxDC* aDC, EDA_RECT& fpBBox, COLOR4D aColor ) -{ -#ifndef USE_WX_OVERLAY - GRRect( aClipBox, aDC, fpBBox, 0, aColor ); -#endif -} - - -int getOptimalModulePlacement( PCB_EDIT_FRAME* aFrame, MODULE* aModule, wxDC* aDC ) -{ - int error = 1; - wxPoint LastPosOK; - double min_cost, curr_cost, Score; - bool TstOtherSide; - auto displ_opts = (PCB_DISPLAY_OPTIONS*)aFrame->GetDisplayOptions(); - BOARD* brd = aFrame->GetBoard(); - - aModule->CalculateBoundingBox(); - - bool showRats = displ_opts->m_Show_Module_Ratsnest; - displ_opts->m_Show_Module_Ratsnest = false; - - brd->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK; - aFrame->SetMsgPanel( aModule ); - - LastPosOK = RoutingMatrix.m_BrdBox.GetOrigin(); - - wxPoint mod_pos = aModule->GetPosition(); - EDA_RECT fpBBox = aModule->GetFootprintRect(); - - // Move fpBBox to have the footprint position at (0,0) - fpBBox.Move( -mod_pos ); - wxPoint fpBBoxOrg = fpBBox.GetOrigin(); - - // Calculate the limit of the footprint position, relative - // to the routing matrix area - wxPoint xylimit = RoutingMatrix.m_BrdBox.GetEnd() - fpBBox.GetEnd(); - - wxPoint initialPos = RoutingMatrix.m_BrdBox.GetOrigin() - fpBBoxOrg; - - // Stay on grid. - initialPos.x -= initialPos.x % RoutingMatrix.m_GridRouting; - initialPos.y -= initialPos.y % RoutingMatrix.m_GridRouting; - - CurrPosition = initialPos; - - // Undraw the current footprint - aModule->DrawOutlinesWhenMoving( aFrame->GetCanvas(), aDC, wxPoint( 0, 0 ) ); - - g_Offset_Module = mod_pos - CurrPosition; - - /* Examine pads, and set TstOtherSide to true if a footprint - * has at least 1 pad through. - */ - TstOtherSide = false; - - if( RoutingMatrix.m_RoutingLayersCount > 1 ) - { - LSET other( aModule->GetLayer() == B_Cu ? F_Cu : B_Cu ); - - for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) - { - if( !( pad->GetLayerSet() & other ).any() ) - continue; - - TstOtherSide = true; - break; - } - } - - // Draw the initial bounding box position - COLOR4D color = COLOR4D( BROWN ); - fpBBox.SetOrigin( fpBBoxOrg + CurrPosition ); - draw_FootprintRect(aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color); - - min_cost = -1.0; - aFrame->SetStatusText( wxT( "Score ??, pos ??" ) ); - - for( ; CurrPosition.x < xylimit.x; CurrPosition.x += RoutingMatrix.m_GridRouting ) - { - wxYield(); - - if( aFrame->GetCanvas()->GetAbortRequest() ) - { - if( IsOK( aFrame, _( "OK to abort?" ) ) ) - { - displ_opts->m_Show_Module_Ratsnest = showRats; - return ESC; - } - else - aFrame->GetCanvas()->SetAbortRequest( false ); - } - - CurrPosition.y = initialPos.y; - - for( ; CurrPosition.y < xylimit.y; CurrPosition.y += RoutingMatrix.m_GridRouting ) - { - // Erase traces. - draw_FootprintRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color ); - - fpBBox.SetOrigin( fpBBoxOrg + CurrPosition ); - g_Offset_Module = mod_pos - CurrPosition; - int keepOutCost = TstModuleOnBoard( brd, aModule, TstOtherSide ); - - // Draw at new place - color = keepOutCost >= 0 ? BROWN : RED; - draw_FootprintRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color ); - - if( keepOutCost >= 0 ) // i.e. if the module can be put here - { - error = 0; - aFrame->build_ratsnest_module( aModule ); - curr_cost = compute_Ratsnest_PlaceModule( brd ); - Score = curr_cost + keepOutCost; - - if( (min_cost >= Score ) || (min_cost < 0 ) ) - { - LastPosOK = CurrPosition; - min_cost = Score; - wxString msg; - msg.Printf( wxT( "Score %g, pos %s, %s" ), - min_cost, - GetChars( ::CoordinateToString( LastPosOK.x ) ), - GetChars( ::CoordinateToString( LastPosOK.y ) ) ); - aFrame->SetStatusText( msg ); - } - } - } - } - - // erasing the last traces - GRRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, 0, BROWN ); - - displ_opts->m_Show_Module_Ratsnest = showRats; - - // Regeneration of the modified variable. - CurrPosition = LastPosOK; - - brd->m_Status_Pcb &= ~( RATSNEST_ITEM_LOCAL_OK | LISTE_PAD_OK ); - - MinCout = min_cost; - 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 OUT_OF_BOARD, or OCCUPED_By_MODULE or FREE_CELL if OK - */ -int TstRectangle( BOARD* Pcb, const EDA_RECT& aRect, int side ) -{ - EDA_RECT rect = aRect; - - rect.Inflate( RoutingMatrix.m_GridRouting / 2 ); - - wxPoint start = rect.GetOrigin(); - wxPoint end = rect.GetEnd(); - - start -= RoutingMatrix.m_BrdBox.GetOrigin(); - end -= RoutingMatrix.m_BrdBox.GetOrigin(); - - int row_min = start.y / RoutingMatrix.m_GridRouting; - int row_max = end.y / RoutingMatrix.m_GridRouting; - int col_min = start.x / RoutingMatrix.m_GridRouting; - int col_max = end.x / RoutingMatrix.m_GridRouting; - - if( start.y > row_min * RoutingMatrix.m_GridRouting ) - row_min++; - - if( start.x > 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( int row = row_min; row <= row_max; row++ ) - { - for( int col = col_min; col <= col_max; col++ ) - { - unsigned int 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 FREE_CELL; -} - - -/* Calculates and returns the clearance area of the rectangular surface - * aRect): - * (Sum of cells in terms of distance) - */ -unsigned int CalculateKeepOutArea( const EDA_RECT& aRect, int side ) -{ - wxPoint start = aRect.GetOrigin(); - wxPoint end = aRect.GetEnd(); - - start -= RoutingMatrix.m_BrdBox.GetOrigin(); - end -= RoutingMatrix.m_BrdBox.GetOrigin(); - - int row_min = start.y / RoutingMatrix.m_GridRouting; - int row_max = end.y / RoutingMatrix.m_GridRouting; - int col_min = start.x / RoutingMatrix.m_GridRouting; - int col_max = end.x / RoutingMatrix.m_GridRouting; - - if( start.y > row_min * RoutingMatrix.m_GridRouting ) - row_min++; - - if( start.x > 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; - - unsigned int keepOutCost = 0; - - for( int row = row_min; row <= row_max; row++ ) - { - for( int col = col_min; col <= col_max; col++ ) - { - // RoutingMatrix.GetDist returns the "cost" of the cell - // at position (row, col) - // in autoplace this is the cost of the cell, if it is - // inside aRect - keepOutCost += RoutingMatrix.GetDist( row, col, side ); - } - } - - return keepOutCost; -} - - -/* Test if the module can be placed on the board. - * Returns the value TstRectangle(). - * Module is known by its bounding box - */ -int TstModuleOnBoard( BOARD* Pcb, MODULE* aModule, bool TstOtherSide ) -{ - int side = TOP; - int otherside = BOTTOM; - - if( aModule->GetLayer() == B_Cu ) - { - side = BOTTOM; otherside = TOP; - } - - EDA_RECT fpBBox = aModule->GetFootprintRect(); - fpBBox.Move( -g_Offset_Module ); - - int diag = TstRectangle( Pcb, fpBBox, side ); - - if( diag != FREE_CELL ) - return diag; - - if( TstOtherSide ) - { - diag = TstRectangle( Pcb, fpBBox, otherside ); - - if( diag != FREE_CELL ) - return diag; - } - - int marge = ( RoutingMatrix.m_GridRouting * aModule->GetPadCount() ) / GAIN; - - fpBBox.Inflate( marge ); - return CalculateKeepOutArea( fpBBox, side ); -} - - -double compute_Ratsnest_PlaceModule( BOARD* aBrd ) -{ - double curr_cost; - wxPoint start; // start point of a ratsnest - wxPoint end; // end point of a ratsnest - int dx, dy; - - if( ( aBrd->m_Status_Pcb & RATSNEST_ITEM_LOCAL_OK ) == 0 ) - return -1; - - curr_cost = 0; - - for( unsigned ii = 0; ii < aBrd->m_LocalRatsnest.size(); ii++ ) - { - RATSNEST_ITEM* pt_local_rats_nest = &aBrd->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(); - - // 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 ) - std::swap( dx, dy ); - - // Cost of the connection = length + penalty due to the slope - // dx is the biggest length 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; - double conn_cost = hypot( dx, dy * 2.0 ); - curr_cost += conn_cost; // Total cost = sum of costs of each connection - } - - return curr_cost; -} - - -/** - * 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 depends on the distance to the first rectangle - * Therefore the cost is high in rect x0,y0 to x1,y1, and decrease outside this rectangle - */ -void CreateKeepOutRectangle( int ux0, int uy0, int ux1, int uy1, - int marge, int aKeepOut, LSET 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[g_Route_Layer_BOTTOM] ) - trace = 1; // Trace on bottom layer. - - if( aLayerMask[g_Route_Layer_TOP] && RoutingMatrix.m_RoutingLayersCount ) - trace |= 2; // Trace on top layer. - - if( trace == 0 ) + if( module == NULL ) 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++ ) - { - // RoutingMatrix Dist map containt the "cost" of the cell - // at position (row, col) - // in autoplace this is the cost of the cell, when - // a footprint overlaps it, near a "master" footprint - // this cost is hight near the "master" footprint - // and decrease with the distance - 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 sortFootprintsByRatsnestSize( 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 kk = 0; kk < moduleList.size(); kk++ ) - { - Module = moduleList[kk]; - 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(), sortFootprintsByRatsnestSize ); - - // 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; + if( incremental ) + module->SetOrientation( module->GetOrientation() + angle ); else - return altModule; + module->SetOrientation( angle ); + + + m_board->GetConnectivity()->Update( module ); } @@ -1182,35 +212,36 @@ static MODULE* PickModule( PCB_EDIT_FRAME* pcbframe, wxDC* DC ) * * 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) + * @return added cells count (i.e. which the attribute CELL_IS_ZONE is set) */ -int propagate() + +int AR_AUTOPLACER::propagate() { int row, col; long current_cell, old_cell_H; - std::vector pt_cell_V; + std::vector pt_cell_V; int nbpoints = 0; -#define NO_CELL_ZONE (HOLE | CELL_is_EDGE | CELL_is_ZONE) + const uint32_t NO_CELL_ZONE = CELL_IS_HOLE | CELL_IS_EDGE | CELL_IS_ZONE; - pt_cell_V.reserve( std::max( RoutingMatrix.m_Nrows, RoutingMatrix.m_Ncols ) ); + pt_cell_V.reserve( std::max( m_matrix.m_Nrows, m_matrix.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++ ) + for( row = 0; row < m_matrix.m_Nrows; row++ ) { old_cell_H = 0; - for( col = 0; col < RoutingMatrix.m_Ncols; col++ ) + for( col = 0; col < m_matrix.m_Ncols; col++ ) { - current_cell = RoutingMatrix.GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; + current_cell = m_matrix.GetCell( row, col, AR_SIDE_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) ) + 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; + m_matrix.OrCell( row, col, AR_SIDE_BOTTOM, CELL_IS_ZONE ); + current_cell = CELL_IS_ZONE; nbpoints++; } } @@ -1222,20 +253,20 @@ int propagate() // 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++ ) + for( row = 0; row < m_matrix.m_Nrows; row++ ) { old_cell_H = 0; - for( col = RoutingMatrix.m_Ncols - 1; col >= 0; col-- ) + for( col = m_matrix.m_Ncols - 1; col >= 0; col-- ) { - current_cell = RoutingMatrix.GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; + current_cell = m_matrix.GetCell( row, col, AR_SIDE_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) ) + 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; + m_matrix.OrCell( row, col, AR_SIDE_BOTTOM, CELL_IS_ZONE ); + current_cell = CELL_IS_ZONE; nbpoints++; } } @@ -1247,20 +278,20 @@ int propagate() // 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-- ) + for( col = m_matrix.m_Ncols - 1; col >= 0; col-- ) { old_cell_H = 0; - for( row = RoutingMatrix.m_Nrows - 1; row >= 0; row-- ) + for( row = m_matrix.m_Nrows - 1; row >= 0; row-- ) { - current_cell = RoutingMatrix.GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; + current_cell = m_matrix.GetCell( row, col, AR_SIDE_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) ) + 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; + m_matrix.OrCell( row, col, AR_SIDE_BOTTOM, CELL_IS_ZONE ); + current_cell = CELL_IS_ZONE; nbpoints++; } } @@ -1272,20 +303,20 @@ int propagate() // 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++ ) + for( col = 0; col < m_matrix.m_Ncols; col++ ) { old_cell_H = 0; - for( row = RoutingMatrix.m_Nrows - 1; row >= 0; row-- ) + for( row = m_matrix.m_Nrows - 1; row >= 0; row-- ) { - current_cell = RoutingMatrix.GetCell( row, col, BOTTOM ) & NO_CELL_ZONE; + current_cell = m_matrix.GetCell( row, col, AR_SIDE_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) ) + 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; + m_matrix.OrCell( row, col, AR_SIDE_BOTTOM, CELL_IS_ZONE ); + current_cell = CELL_IS_ZONE; nbpoints++; } } @@ -1296,3 +327,756 @@ int propagate() return nbpoints; } + + +void AR_AUTOPLACER::genModuleOnRoutingMatrix( MODULE* Module ) +{ + int ox, oy, fx, fy; + LSET layerMask; + EDA_RECT fpBBox = Module->GetBoundingBox(); + + fpBBox.Inflate( m_matrix.m_GridRouting / 2 ); + ox = fpBBox.GetX(); + fx = fpBBox.GetRight(); + oy = fpBBox.GetY(); + fy = fpBBox.GetBottom(); + + if( ox < m_matrix.m_BrdBox.GetX() ) + ox = m_matrix.m_BrdBox.GetX(); + + if( ox > m_matrix.m_BrdBox.GetRight() ) + ox = m_matrix.m_BrdBox.GetRight(); + + if( fx < m_matrix.m_BrdBox.GetX() ) + fx = m_matrix.m_BrdBox.GetX(); + + if( fx > m_matrix.m_BrdBox.GetRight() ) + fx = m_matrix.m_BrdBox.GetRight(); + + if( oy < m_matrix.m_BrdBox.GetY() ) + oy = m_matrix.m_BrdBox.GetY(); + + if( oy > m_matrix.m_BrdBox.GetBottom() ) + oy = m_matrix.m_BrdBox.GetBottom(); + + if( fy < m_matrix.m_BrdBox.GetY() ) + fy = m_matrix.m_BrdBox.GetY(); + + if( fy > m_matrix.m_BrdBox.GetBottom() ) + fy = m_matrix.m_BrdBox.GetBottom(); + + if( Module->GetLayer() == F_Cu ) + layerMask.set( F_Cu ); + + if( Module->GetLayer() == B_Cu ) + layerMask.set( B_Cu ); + + m_matrix.TraceFilledRectangle( ox, oy, fx, fy, layerMask, + CELL_IS_MODULE, AR_MATRIX::WRITE_OR_CELL ); + + // Trace pads + clearance areas. + for( auto pad : Module->Pads() ) + { + int margin = (m_matrix.m_GridRouting / 2) + pad->GetClearance(); + m_matrix.PlacePad( pad, CELL_IS_MODULE, margin, AR_MATRIX::WRITE_OR_CELL ); + } + + // Trace clearance. + int margin = ( m_matrix.m_GridRouting * Module->GetPadCount() ) / AR_GAIN; + m_matrix.CreateKeepOutRectangle( ox, oy, fx, fy, margin, AR_KEEPOUT_MARGIN , layerMask ); +} + + +/* 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 OUT_OF_BOARD, or OCCUPED_By_MODULE or FREE_CELL if OK + */ +int AR_AUTOPLACER::testRectangle( const EDA_RECT& aRect, int side ) +{ + EDA_RECT rect = aRect; + + rect.Inflate( m_matrix.m_GridRouting / 2 ); + + wxPoint start = rect.GetOrigin(); + wxPoint end = rect.GetEnd(); + + start -= m_matrix.m_BrdBox.GetOrigin(); + end -= m_matrix.m_BrdBox.GetOrigin(); + + int row_min = start.y / m_matrix.m_GridRouting; + int row_max = end.y / m_matrix.m_GridRouting; + int col_min = start.x / m_matrix.m_GridRouting; + int col_max = end.x / m_matrix.m_GridRouting; + + if( start.y > row_min * m_matrix.m_GridRouting ) + row_min++; + + if( start.x > col_min * m_matrix.m_GridRouting ) + col_min++; + + if( row_min < 0 ) + row_min = 0; + + if( row_max >= ( m_matrix.m_Nrows - 1 ) ) + row_max = m_matrix.m_Nrows - 1; + + if( col_min < 0 ) + col_min = 0; + + if( col_max >= ( m_matrix.m_Ncols - 1 ) ) + col_max = m_matrix.m_Ncols - 1; + + for( int row = row_min; row <= row_max; row++ ) + { + for( int col = col_min; col <= col_max; col++ ) + { + unsigned int data = m_matrix.GetCell( row, col, side ); + + if( ( data & CELL_IS_ZONE ) == 0 ) + return AR_OUT_OF_BOARD; + + if( (data & CELL_IS_MODULE) ) + return AR_OCCUIPED_BY_MODULE; + } + } + + return AR_FREE_CELL; +} + + +/* Calculates and returns the clearance area of the rectangular surface + * aRect): + * (Sum of cells in terms of distance) + */ +unsigned int AR_AUTOPLACER::calculateKeepOutArea( const EDA_RECT& aRect, int side ) +{ + wxPoint start = aRect.GetOrigin(); + wxPoint end = aRect.GetEnd(); + + start -= m_matrix.m_BrdBox.GetOrigin(); + end -= m_matrix.m_BrdBox.GetOrigin(); + + int row_min = start.y / m_matrix.m_GridRouting; + int row_max = end.y / m_matrix.m_GridRouting; + int col_min = start.x / m_matrix.m_GridRouting; + int col_max = end.x / m_matrix.m_GridRouting; + + if( start.y > row_min * m_matrix.m_GridRouting ) + row_min++; + + if( start.x > col_min * m_matrix.m_GridRouting ) + col_min++; + + if( row_min < 0 ) + row_min = 0; + + if( row_max >= ( m_matrix.m_Nrows - 1 ) ) + row_max = m_matrix.m_Nrows - 1; + + if( col_min < 0 ) + col_min = 0; + + if( col_max >= ( m_matrix.m_Ncols - 1 ) ) + col_max = m_matrix.m_Ncols - 1; + + unsigned int keepOutCost = 0; + + for( int row = row_min; row <= row_max; row++ ) + { + for( int col = col_min; col <= col_max; col++ ) + { + // m_matrix.GetDist returns the "cost" of the cell + // at position (row, col) + // in autoplace this is the cost of the cell, if it is + // inside aRect + keepOutCost += m_matrix.GetDist( row, col, side ); + } + } + + return keepOutCost; +} + + +/* Test if the module can be placed on the board. + * Returns the value TstRectangle(). + * Module is known by its bounding box + */ +int AR_AUTOPLACER::testModuleOnBoard( MODULE* aModule, bool TstOtherSide, const wxPoint& aOffset ) +{ + int side = AR_SIDE_TOP; + int otherside = AR_SIDE_BOTTOM; + + if( aModule->GetLayer() == B_Cu ) + { + side = AR_SIDE_BOTTOM; otherside = AR_SIDE_TOP; + } + + EDA_RECT fpBBox = aModule->GetFootprintRect(); + fpBBox.Move( -aOffset ); + + int diag = testRectangle( fpBBox, side ); + + if( diag != AR_FREE_CELL ) + return diag; + + if( TstOtherSide ) + { + diag = testRectangle( fpBBox, otherside ); + + if( diag != AR_FREE_CELL ) + return diag; + } + + int marge = ( m_matrix.m_GridRouting * aModule->GetPadCount() ) / AR_GAIN; + + fpBBox.Inflate( marge ); + return calculateKeepOutArea( fpBBox, side ); +} + + +int AR_AUTOPLACER::getOptimalModulePlacement(MODULE* aModule) +{ + int error = 1; + wxPoint LastPosOK; + double min_cost, curr_cost, Score; + bool TstOtherSide; + + aModule->CalculateBoundingBox(); + + LastPosOK = m_matrix.m_BrdBox.GetOrigin(); + + wxPoint mod_pos = aModule->GetPosition(); + EDA_RECT fpBBox = aModule->GetFootprintRect(); + + // Move fpBBox to have the footprint position at (0,0) + fpBBox.Move( -mod_pos ); + wxPoint fpBBoxOrg = fpBBox.GetOrigin(); + + // Calculate the limit of the footprint position, relative + // to the routing matrix area + wxPoint xylimit = m_matrix.m_BrdBox.GetEnd() - fpBBox.GetEnd(); + + wxPoint initialPos = m_matrix.m_BrdBox.GetOrigin() - fpBBoxOrg; + + // Stay on grid. + initialPos.x -= initialPos.x % m_matrix.m_GridRouting; + initialPos.y -= initialPos.y % m_matrix.m_GridRouting; + + m_curPosition = initialPos; + auto moduleOffset = mod_pos - m_curPosition; + + /* Examine pads, and set TstOtherSide to true if a footprint + * has at least 1 pad through. + */ + TstOtherSide = false; + + if( m_matrix.m_RoutingLayersCount > 1 ) + { + LSET other( aModule->GetLayer() == B_Cu ? F_Cu : B_Cu ); + + for( auto pad : aModule->Pads() ) + { + if( !( pad->GetLayerSet() & other ).any() ) + continue; + + TstOtherSide = true; + break; + } + } + + fpBBox.SetOrigin( fpBBoxOrg + m_curPosition ); + + min_cost = -1.0; +// m_frame->SetStatusText( wxT( "Score ??, pos ??" ) ); + + + for( ; m_curPosition.x < xylimit.x; m_curPosition.x += m_matrix.m_GridRouting ) + { + if ( m_refreshCallback ) + { + if ( m_refreshCallback() == AR_ABORT_PLACEMENT ) + return AR_ABORT_PLACEMENT; + } + + m_curPosition.y = initialPos.y; + + for( ; m_curPosition.y < xylimit.y; m_curPosition.y += m_matrix.m_GridRouting ) + { + + fpBBox.SetOrigin( fpBBoxOrg + m_curPosition ); + moduleOffset = mod_pos - m_curPosition; + int keepOutCost = testModuleOnBoard( aModule, TstOtherSide, moduleOffset ); + + if( keepOutCost >= 0 ) // i.e. if the module can be put here + { + error = 0; + // m_frame->build_ratsnest_module( aModule ); // fixme + curr_cost = computePlacementRatsnestCost( aModule, moduleOffset ); + Score = curr_cost + keepOutCost; + + if( (min_cost >= Score ) || (min_cost < 0 ) ) + { + LastPosOK = m_curPosition; + min_cost = Score; + wxString msg; +/* msg.Printf( wxT( "Score %g, pos %s, %s" ), + min_cost, + GetChars( ::CoordinateToString( LastPosOK.x ) ), + GetChars( ::CoordinateToString( LastPosOK.y ) ) ); + m_frame->SetStatusText( msg );*/ + } + } + } + } + + // Regeneration of the modified variable. + m_curPosition = LastPosOK; + + m_minCost = min_cost; + return error; +} + + +const D_PAD* AR_AUTOPLACER::nearestPad( MODULE *aRefModule, D_PAD* aRefPad, const wxPoint& aOffset) +{ + const D_PAD* nearest = nullptr; + int64_t nearestDist = INT64_MAX; + + for ( auto mod : m_board->Modules() ) + { + if ( mod == aRefModule ) + continue; + + if( !m_matrix.m_BrdBox.Contains( mod->GetPosition() ) ) + continue; + + for ( auto pad: mod->Pads() ) + { + if ( pad->GetNetCode() != aRefPad->GetNetCode() || pad->GetNetCode() <= 0 ) + continue; + + auto dist = (VECTOR2I( aRefPad->GetPosition() - aOffset ) - VECTOR2I( pad->GetPosition() ) ).EuclideanNorm(); + + //printf("Dist %lld pad %p\n", dist, pad ); + + if ( dist < nearestDist ) + { + nearestDist = dist; + nearest = pad; + } + } + } + + return nearest; +} + + +double AR_AUTOPLACER::computePlacementRatsnestCost( MODULE *aModule, const wxPoint& aOffset ) +{ + double curr_cost; + VECTOR2I start; // start point of a ratsnest + VECTOR2I end; // end point of a ratsnest + int dx, dy; + + curr_cost = 0; + + for ( auto pad : aModule->Pads() ) + { + auto nearest = nearestPad( aModule, pad, aOffset ); + + if( !nearest ) + continue; + + //printf("pad %s nearest %s\n", (const char *)aModule->GetReference().c_str(), (const char *)nearest->GetParent()->GetReference().c_str()); + + start = VECTOR2I( pad->GetPosition() ) - VECTOR2I(aOffset); + end = VECTOR2I( nearest->GetPosition() ); + + //m_overlay->SetIsStroke( true ); + //m_overlay->SetStrokeColor( COLOR4D(0.0, 1.0, 0.0, 1.0) ); + //m_overlay->Line( start, end ); + + // 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 ) + std::swap( dx, dy ); + + // Cost of the connection = length + penalty due to the slope + // dx is the biggest length 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; + double conn_cost = hypot( dx, dy * 2.0 ); + curr_cost += conn_cost; // Total cost = sum of costs of each connection + } + + return curr_cost; +} + + +// Sort routines +static bool sortFootprintsByComplexity( MODULE* ref, MODULE* compare ) +{ + double ff1, ff2; + + ff1 = ref->GetArea() * ref->GetPadCount(); + ff2 = compare->GetArea() * compare->GetPadCount(); + + return ff2 < ff1; +} + + +static bool sortFootprintsByRatsnestSize( MODULE* ref, MODULE* compare ) +{ + double ff1, ff2; + + ff1 = ref->GetArea() * ref->GetFlag(); + ff2 = compare->GetArea() * compare->GetFlag(); + return ff2 < ff1; +} + + +/** + * Function Module + * find the "best" module place + * The criteria are: + * - Maximum ratsnest with modules already placed + * - Max size, and number of pads max + */ +MODULE* AR_AUTOPLACER::pickModule( ) +{ + MODULE* module; + std::vector moduleList; + + + for( auto m : m_board->Modules() ) + { + m->CalculateBoundingBox(); + moduleList.push_back( m ); + } + + sort( moduleList.begin(), moduleList.end(), sortFootprintsByComplexity ); + + for( unsigned kk = 0; kk < moduleList.size(); kk++ ) + { + module = moduleList[kk]; + module->SetFlag( 0 ); + + if( !module->NeedsPlaced() ) + continue; + + m_connectivity->Update( module ); + } + + m_connectivity->RecalculateRatsnest(); + + for( unsigned kk = 0; kk < moduleList.size(); kk++ ) + { + module = moduleList[kk]; + + auto edges = m_connectivity->GetRatsnestForComponent( module, true ); + + module->SetFlag( edges.size() ) ; + } + + sort( moduleList.begin(), moduleList.end(), sortFootprintsByRatsnestSize ); + + // Search for "best" module. + MODULE* bestModule = nullptr; + MODULE* altModule = nullptr; + + 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; +} + + +void AR_AUTOPLACER::drawPlacementRoutingMatrix( ) +{ + int ii, jj; + COLOR4D color; + int ox, oy; + AR_MATRIX::MATRIX_CELL top_state, bottom_state; + + + for( ii = 0; ii < m_matrix.m_Nrows; ii++ ) + { + oy = m_matrix.m_BrdBox.GetY() + ( ii * m_matrix.m_GridRouting ); + + for( jj = 0; jj < m_matrix.m_Ncols; jj++ ) + { + ox = m_matrix.m_BrdBox.GetX() + (jj * m_matrix.m_GridRouting); + color = COLOR4D::BLACK; + + top_state = m_matrix.GetCell( ii, jj, AR_SIDE_TOP ); + bottom_state = m_matrix.GetCell( ii, jj, AR_SIDE_BOTTOM ); + + if(top_state || bottom_state) + { + // printf("[%d, %d] [%d, %d] TS %x BS %x\n",ii,jj, ox, oy, top_state, bottom_state ); + } + + if( top_state & CELL_IS_ZONE ) + color = COLOR4D( BLUE ); + + // obstacles + if( ( top_state & CELL_IS_EDGE ) || ( bottom_state & CELL_IS_EDGE ) ) + color = COLOR4D::WHITE; + else if( top_state & ( CELL_IS_HOLE | CELL_IS_MODULE ) ) + color = COLOR4D( LIGHTRED ); + else if( bottom_state & ( CELL_IS_HOLE | CELL_IS_MODULE) ) + color = COLOR4D( LIGHTGREEN ); + else // Display the filling and keep out regions. + { + if( m_matrix.GetDist( ii, jj, AR_SIDE_TOP ) + || m_matrix.GetDist( ii, jj, AR_SIDE_BOTTOM ) ) + color = DARKGRAY; + } + + m_overlay->SetIsFill(true); + m_overlay->SetFillColor( color ); + + VECTOR2D p(ox, oy); + m_overlay->Circle(p, m_matrix.m_GridRouting/4 ); + } + } +} + +AR_RESULT AR_AUTOPLACER::AutoplaceModules( std::vector aModules, BOARD_COMMIT* aCommit, bool aPlaceOffboardModules ) +{ + wxPoint PosOK; + wxPoint memopos; + int error; + MODULE* module = nullptr; + bool cancelled = false; + + memopos = m_curPosition; + + //printf("set grid: %d\n", m_gridSize); + + m_matrix.m_GridRouting = m_gridSize; //(int) m_frame->GetScreen()->GetGridSize().x; + + // Ensure Board.m_GridRouting has a reasonable value: + if( m_matrix.m_GridRouting < Millimeter2iu( 0.25 ) ) + m_matrix.m_GridRouting = Millimeter2iu( 0.25 ); + + // Compute module parameters used in auto place + if( genPlacementRoutingMatrix( ) == 0 ) + return AR_FAILURE; + + int moduleCount = 0; + + for ( auto m : m_board->Modules() ) + { + m->SetNeedsPlaced( false ); + } + + std::vector offboardMods; + + if( aPlaceOffboardModules ) + { + for ( auto m : m_board->Modules() ) + { + if( !m_matrix.m_BrdBox.Contains( m->GetPosition() ) ) + { + offboardMods.push_back( m ); + } + } + } + + for ( auto m : aModules ) + { + m->SetNeedsPlaced( true ); + aCommit->Modify(m); + } + + for ( auto m : offboardMods ) + { + m->SetNeedsPlaced( true ); + aCommit->Modify(m); + } + + for ( auto m : m_board->Modules() ) + { + if( m->NeedsPlaced() ) // Erase from screen + { + moduleCount++; + } + else + { + genModuleOnRoutingMatrix( m ); + } + } + + drawPlacementRoutingMatrix(); + + int cnt = 0; + wxString msg; + + if( m_progressReporter ) + { + m_progressReporter->Report( _( "Autoplacing components..." ) ); + m_progressReporter->SetMaxProgress( moduleCount ); + } + + while( ( module = pickModule( ) ) != nullptr ) + { + // Display some info about activity, module placement can take a while: + //printf( _( "Place footprint %d of %d [%s]\n" ), cnt, moduleCount, (const char *)module->GetReference().c_str() ); + //m_frame->SetStatusText( msg ); + + double initialOrient = module->GetOrientation(); + // Display fill area of interest, barriers, penalties. + //drawPlacementRoutingMatrix( ); + + error = getOptimalModulePlacement( module ); + double bestScore = m_minCost; + double bestRotation = 0.0; + int rotAllowed; + PosOK = m_curPosition; + + if( error == AR_ABORT_PLACEMENT ) + goto end_of_tst; + + // Try orientations 90, 180, 270 degrees from initial orientation + rotAllowed = module->GetPlacementCost180(); + + //printf("rotAllowed %d\n", rotAllowed); + + if( rotAllowed != 0 ) + { + rotateModule( module, 1800.0, true ); + error = getOptimalModulePlacement( module ); + m_minCost *= OrientationPenalty[rotAllowed]; + + if( bestScore > m_minCost ) // This orientation is better. + { + PosOK = m_curPosition; + bestScore = m_minCost; + bestRotation = 1800.0; + } + else + { + rotateModule( module, initialOrient, false ); + } + + if( error == AR_ABORT_PLACEMENT ) + goto end_of_tst; + } + + // Determine if the best orientation of a module is 90. + rotAllowed = module->GetPlacementCost90(); + if( rotAllowed != 0 ) + { + rotateModule( module, 900.0, true ); + error = getOptimalModulePlacement( module ); + m_minCost *= OrientationPenalty[rotAllowed]; + + if( bestScore > m_minCost ) // This orientation is better. + { + PosOK = m_curPosition; + bestScore = m_minCost; + bestRotation = 900.0; + } + else + { + rotateModule( module, initialOrient, false ); + } + + if( error == AR_ABORT_PLACEMENT ) + goto end_of_tst; + } + + // Determine if the best orientation of a module is -90. + if( rotAllowed != 0 ) + { + rotateModule( module, 2700.0, true ); + error = getOptimalModulePlacement( module ); + m_minCost *= OrientationPenalty[rotAllowed]; + + if( bestScore > m_minCost ) // This orientation is better. + { + PosOK = m_curPosition; + bestScore = m_minCost; + bestRotation = 2700.0; + } + else + { + rotateModule( module, initialOrient, false ); + } + + if( error == AR_ABORT_PLACEMENT ) + goto end_of_tst; + } + +end_of_tst: + + if( error == AR_ABORT_PLACEMENT ) + break; + + + bestRotation += initialOrient; + + if( bestRotation != module->GetOrientation() ) + { + //printf("best rotation %d\n", bestRotation ); + rotateModule( module, bestRotation, false ); + } + + // Place module. + placeModule( module, true, m_curPosition ); + + module->CalculateBoundingBox(); + genModuleOnRoutingMatrix( module ); + module->SetIsPlaced( true ); + module->SetNeedsPlaced( false ); + + + if( m_progressReporter ) + { + m_progressReporter->AdvanceProgress(); + if ( !m_progressReporter->KeepRefreshing( false ) ) + { + cancelled = true; + break; + } + } + cnt++; + } + + m_curPosition = memopos; + + m_matrix.UnInitRoutingMatrix(); + + for ( auto m : m_board->Modules() ) + { + m->CalculateBoundingBox(); + } + + return cancelled ? AR_CANCELLED : AR_COMPLETED; +} diff --git a/pcbnew/autorouter/ar_autoplacer.h b/pcbnew/autorouter/ar_autoplacer.h new file mode 100644 index 0000000000..f7bc233d40 --- /dev/null +++ b/pcbnew/autorouter/ar_autoplacer.h @@ -0,0 +1,118 @@ +/* + * 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 + */ + + +#ifndef __AR_AUTOPLACER_H +#define __AR_AUTOPLACER_H + +#include "ar_cell.h" +#include "ar_matrix.h" + +#include +#include + +#include + +#include + +enum AR_CELL_STATE +{ + AR_OUT_OF_BOARD = -2, + AR_OCCUIPED_BY_MODULE = -1, + AR_FREE_CELL = 0 +}; + +enum AR_RESULT +{ + AR_COMPLETED = 1, + AR_CANCELLED, + AR_FAILURE +}; + +class PROGRESS_REPORTER; + +class AR_AUTOPLACER +{ +public: + AR_AUTOPLACER( BOARD* aBoard ); + + AR_RESULT AutoplaceModules( std::vector aModules, BOARD_COMMIT* aCommit, + bool aPlaceOffboardModules = false ); + + const std::vector QueryOffboardModules(); + + void SetPlacementGrid( int aGrid ) + { + m_gridSize = aGrid; + } + + void SetOverlay( std::shared_ptr aOverlay ) + { + m_overlay = aOverlay; + } + + void SetRefreshCallback( std::function aCallback ) + { + m_refreshCallback = aCallback; + } + + void SetProgressReporter( PROGRESS_REPORTER* aReporter ) + { + m_progressReporter = aReporter; + } + +private: + void drawPlacementRoutingMatrix(); + void rotateModule( MODULE* module, double angle, bool incremental ); + int genPlacementRoutingMatrix(); + void genModuleOnRoutingMatrix( MODULE* Module ); + int propagate(); + int testRectangle( const EDA_RECT& aRect, int side ); + unsigned int calculateKeepOutArea( const EDA_RECT& aRect, int side ); + int testModuleOnBoard( MODULE* aModule, bool TstOtherSide, const wxPoint& aOffset ); + int getOptimalModulePlacement( MODULE* aModule ); + double computePlacementRatsnestCost( MODULE* aModule, const wxPoint& aOffset ); + MODULE* pickModule(); + void placeModule( MODULE* aModule, bool aDoNotRecreateRatsnest, const wxPoint& aPos ); + const D_PAD* nearestPad( MODULE* aRefModule, D_PAD* aRefPad, const wxPoint& aOffset ); + + AR_MATRIX m_matrix; + + BOARD* m_board; + + wxPoint m_curPosition; + wxPoint m_moduleOffset; + double m_minCost; + int m_gridSize; + + std::shared_ptr m_overlay; + std::unique_ptr m_connectivity; + std::function m_refreshCallback; + PROGRESS_REPORTER* m_progressReporter; +}; + +#endif diff --git a/pcbnew/autorouter/ar_cell.h b/pcbnew/autorouter/ar_cell.h index 63b76aeddb..1136a3dfc0 100644 --- a/pcbnew/autorouter/ar_cell.h +++ b/pcbnew/autorouter/ar_cell.h @@ -1,7 +1,3 @@ -/** - * @file cell.h - */ - /* * This program source code file is part of KiCad, a free EDA CAD application. * @@ -32,85 +28,84 @@ */ -#ifndef _CELL_H_ -#define _CELL_H_ - +#ifndef _AR_CELL_H_ +#define _AR_CELL_H_ /* Bits characterizing cell */ -#define HOLE 0x01 /* a conducting hole or obstacle */ -#define CELL_is_MODULE 0x02 /* auto placement occupied by a module */ -#define CELL_is_EDGE 0x20 /* Area and auto-placement: limiting cell contour (Board, Zone) */ -#define CELL_is_FRIEND 0x40 /* Area and auto-placement: cell part of the net */ -#define CELL_is_ZONE 0x80 /* Area and auto-placement: cell available */ +#define CELL_IS_EMPTY 0x00 +#define CELL_IS_HOLE 0x01 /* a conducting hole or obstacle */ +#define CELL_IS_MODULE 0x02 /* auto placement occupied by a module */ +#define CELL_IS_EDGE 0x20 /* Area and auto-placement: limiting cell contour (Board, Zone) */ +#define CELL_IS_FRIEND 0x40 /* Area and auto-placement: cell part of the net */ +#define CELL_IS_ZONE 0x80 /* Area and auto-placement: cell available */ /* Bit masks for presence of obstacles to autorouting */ -#define OCCUPE 1 /* Autorouting: obstacle tracks and vias. */ -#define VIA_IMPOSSIBLE 2 /* Autorouting: obstacle for vias. */ -#define CURRENT_PAD 4 +#define OCCUPE 1 /* Autorouting: obstacle tracks and vias. */ +#define VIA_IMPOSSIBLE 2 /* Autorouting: obstacle for vias. */ +#define CURRENT_PAD 4 /* traces radiating outward from a hole to a side or corner */ -#define HOLE_NORTH 0x00000002L /* upward */ -#define HOLE_NORTHEAST 0x00000004L /* upward and right */ -#define HOLE_EAST 0x00000008L /* to the right */ -#define HOLE_SOUTHEAST 0x00000010L /* downward and right */ -#define HOLE_SOUTH 0x00000020L /* downward */ -#define HOLE_SOUTHWEST 0x00000040L /* downward and left */ -#define HOLE_WEST 0x00000080L /* to the left */ -#define HOLE_NORTHWEST 0x00000100L /* upward and left */ +#define HOLE_NORTH 0x00000002L /* upward */ +#define HOLE_NORTHEAST 0x00000004L /* upward and right */ +#define HOLE_EAST 0x00000008L /* to the right */ +#define HOLE_SOUTHEAST 0x00000010L /* downward and right */ +#define HOLE_SOUTH 0x00000020L /* downward */ +#define HOLE_SOUTHWEST 0x00000040L /* downward and left */ +#define HOLE_WEST 0x00000080L /* to the left */ +#define HOLE_NORTHWEST 0x00000100L /* upward and left */ /* straight lines through the center */ -#define LINE_HORIZONTAL 0x00000002L /* left-to-right line */ -#define LINE_VERTICAL 0x00000004L /* top-to-bottom line */ +#define LINE_HORIZONTAL 0x00000002L /* left-to-right line */ +#define LINE_VERTICAL 0x00000004L /* top-to-bottom line */ /* lines cutting across a corner, connecting adjacent sides */ -#define CORNER_NORTHEAST 0x00000008L /* upper right corner */ -#define CORNER_SOUTHEAST 0x00000010L /* lower right corner */ -#define CORNER_SOUTHWEST 0x00000020L /* lower left corner */ -#define CORNER_NORTHWEST 0x00000040L /* upper left corner */ +#define CORNER_NORTHEAST 0x00000008L /* upper right corner */ +#define CORNER_SOUTHEAST 0x00000010L /* lower right corner */ +#define CORNER_SOUTHWEST 0x00000020L /* lower left corner */ +#define CORNER_NORTHWEST 0x00000040L /* upper left corner */ /* diagonal lines through the center */ -#define DIAG_NEtoSW 0x00000080L /* northeast to southwest */ -#define DIAG_SEtoNW 0x00000100L /* southeast to northwest */ +#define DIAG_NEtoSW 0x00000080L /* northeast to southwest */ +#define DIAG_SEtoNW 0x00000100L /* southeast to northwest */ /* 135 degree angle side-to-far-corner lines */ -#define BENT_NtoSE 0x00000200L /* north to southeast */ -#define BENT_NtoSW 0x00000400L /* north to southwest */ -#define BENT_EtoSW 0x00000800L /* east to southwest */ -#define BENT_EtoNW 0x00001000L /* east to northwest */ -#define BENT_StoNW 0x00002000L /* south to northwest */ -#define BENT_StoNE 0x00004000L /* south to northeast */ -#define BENT_WtoNE 0x00008000L /* west to northeast */ -#define BENT_WtoSE 0x00010000L /* west to southeast */ +#define BENT_NtoSE 0x00000200L /* north to southeast */ +#define BENT_NtoSW 0x00000400L /* north to southwest */ +#define BENT_EtoSW 0x00000800L /* east to southwest */ +#define BENT_EtoNW 0x00001000L /* east to northwest */ +#define BENT_StoNW 0x00002000L /* south to northwest */ +#define BENT_StoNE 0x00004000L /* south to northeast */ +#define BENT_WtoNE 0x00008000L /* west to northeast */ +#define BENT_WtoSE 0x00010000L /* west to southeast */ /* 90 degree corner-to-adjacent-corner lines */ -#define ANGLE_NEtoSE 0x00020000L /* northeast to southeast */ -#define ANGLE_SEtoSW 0x00040000L /* southeast to southwest */ -#define ANGLE_SWtoNW 0x00080000L /* southwest to northwest */ -#define ANGLE_NWtoNE 0x00100000L /* northwest to northeast */ +#define ANGLE_NEtoSE 0x00020000L /* northeast to southeast */ +#define ANGLE_SEtoSW 0x00040000L /* southeast to southwest */ +#define ANGLE_SWtoNW 0x00080000L /* southwest to northwest */ +#define ANGLE_NWtoNE 0x00100000L /* northwest to northeast */ /* 45 degree angle side-to-near-corner lines */ -#define SHARP_NtoNE 0x00200000L /* north to northeast */ -#define SHARP_EtoNE 0x00400000L /* east to northeast */ -#define SHARP_EtoSE 0x00800000L /* east to southeast */ -#define SHARP_StoSE 0x01000000L /* south to southeast */ -#define SHARP_StoSW 0x02000000L /* south to southwest */ -#define SHARP_WtoSW 0x04000000L /* west to southwest */ -#define SHARP_WtoNW 0x08000000L /* west to northwest */ -#define SHARP_NtoNW 0x10000000L /* north to northwest */ +#define SHARP_NtoNE 0x00200000L /* north to northeast */ +#define SHARP_EtoNE 0x00400000L /* east to northeast */ +#define SHARP_EtoSE 0x00800000L /* east to southeast */ +#define SHARP_StoSE 0x01000000L /* south to southeast */ +#define SHARP_StoSW 0x02000000L /* south to southwest */ +#define SHARP_WtoSW 0x04000000L /* west to southwest */ +#define SHARP_WtoNW 0x08000000L /* west to northwest */ +#define SHARP_NtoNW 0x10000000L /* north to northwest */ /* directions the cell can be reached from (point to previous cell) */ -#define FROM_NOWHERE 0 -#define FROM_NORTH 1 -#define FROM_NORTHEAST 2 -#define FROM_EAST 3 -#define FROM_SOUTHEAST 4 -#define FROM_SOUTH 5 -#define FROM_SOUTHWEST 6 -#define FROM_WEST 7 -#define FROM_NORTHWEST 8 -#define FROM_OTHERSIDE 9 +#define FROM_NOWHERE 0 +#define FROM_NORTH 1 +#define FROM_NORTHEAST 2 +#define FROM_EAST 3 +#define FROM_SOUTHEAST 4 +#define FROM_SOUTH 5 +#define FROM_SOUTHWEST 6 +#define FROM_WEST 7 +#define FROM_NORTHWEST 8 +#define FROM_OTHERSIDE 9 -#endif // _CELL_H_ - +#endif // __AR_CELL_H diff --git a/pcbnew/autorouter/ar_matrix.cpp b/pcbnew/autorouter/ar_matrix.cpp index 856dead922..6622f33141 100644 --- a/pcbnew/autorouter/ar_matrix.cpp +++ b/pcbnew/autorouter/ar_matrix.cpp @@ -25,52 +25,44 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/** - * @file routing_matrix.cpp - * @brief Functions to create autorouting maps - */ +#include "ar_matrix.h" +#include "ar_cell.h" -#include #include +#include +#include -#include -#include -#include - -#include -#include -#include -#include #include -#include -#include +#include +#include - -MATRIX_ROUTING_HEAD::MATRIX_ROUTING_HEAD() +AR_MATRIX::AR_MATRIX() { - m_BoardSide[0] = m_BoardSide[1] = NULL; - m_DistSide[0] = m_DistSide[1] = NULL; - m_DirSide[0] = m_DirSide[1] = NULL; - m_opWriteCell = NULL; - m_InitMatrixDone = false; - m_Nrows = 0; - m_Ncols = 0; - m_MemSize = 0; + m_BoardSide[0] = m_BoardSide[1] = nullptr; + m_DistSide[0] = m_DistSide[1] = nullptr; + m_DirSide[0] = m_DirSide[1] = nullptr; + m_opWriteCell = nullptr; + m_InitMatrixDone = false; + m_Nrows = 0; + m_Ncols = 0; + m_MemSize = 0; m_RoutingLayersCount = 1; - m_GridRouting = 0; - m_RouteCount = 0; + m_GridRouting = 0; + m_RouteCount = 0; } -MATRIX_ROUTING_HEAD::~MATRIX_ROUTING_HEAD() +AR_MATRIX::~AR_MATRIX() { } +// was: bool AR_MATRIX::ComputeMatrixSize( BOARD* aPcb, bool aUseBoardEdgesOnly ) +// aUseBoardEdgesOnly ? aPcb->GetBoardEdgesBoundingBox() : aPcb->GetBoundingBox(); -bool MATRIX_ROUTING_HEAD::ComputeMatrixSize( BOARD* aPcb, bool aUseBoardEdgesOnly ) +bool AR_MATRIX::ComputeMatrixSize( const EDA_RECT& aBoundingBox ) { // The boundary box must have its start point on routing grid: - m_BrdBox = aUseBoardEdgesOnly ? aPcb->GetBoardEdgesBoundingBox() : aPcb->GetBoundingBox(); + m_BrdBox = aBoundingBox; m_BrdBox.SetX( m_BrdBox.GetX() - ( m_BrdBox.GetX() % m_GridRouting ) ); m_BrdBox.SetY( m_BrdBox.GetY() - ( m_BrdBox.GetY() % m_GridRouting ) ); @@ -97,451 +89,1232 @@ bool MATRIX_ROUTING_HEAD::ComputeMatrixSize( BOARD* aPcb, bool aUseBoardEdgesOnl } -int MATRIX_ROUTING_HEAD::InitRoutingMatrix() +int AR_MATRIX::InitRoutingMatrix() { if( m_Nrows <= 0 || m_Ncols <= 0 ) return 0; - m_InitMatrixDone = true; // we have been called + m_InitMatrixDone = true; // we have been called // give a small margin for memory allocation: - int ii = (RoutingMatrix.m_Nrows + 1) * (RoutingMatrix.m_Ncols + 1); + int ii = ( m_Nrows + 1 ) * ( m_Ncols + 1 ); - int side = BOTTOM; - for( int jj = 0; jj < m_RoutingLayersCount; jj++ ) // m_RoutingLayersCount = 1 or 2 + int side = AR_SIDE_BOTTOM; + for( int jj = 0; jj < m_RoutingLayersCount; jj++ ) // m_RoutingLayersCount = 1 or 2 { - m_BoardSide[side] = NULL; - m_DistSide[side] = NULL; - m_DirSide[side] = NULL; + m_BoardSide[side] = nullptr; + m_DistSide[side] = nullptr; + m_DirSide[side] = nullptr; // allocate matrix & initialize everything to empty - m_BoardSide[side] = (MATRIX_CELL*) operator new( ii * sizeof(MATRIX_CELL) ); - memset( m_BoardSide[side], 0, ii * sizeof(MATRIX_CELL) ); + m_BoardSide[side] = (MATRIX_CELL*) operator new( ii * sizeof( MATRIX_CELL ) ); + memset( m_BoardSide[side], 0, ii * sizeof( MATRIX_CELL ) ); - if( m_BoardSide[side] == NULL ) + if( m_BoardSide[side] == nullptr ) return -1; // allocate Distances - m_DistSide[side] = (DIST_CELL*) operator new( ii * sizeof(DIST_CELL) ); - memset( m_DistSide[side], 0, ii * sizeof(DIST_CELL) ); + m_DistSide[side] = (DIST_CELL*) operator new( ii * sizeof( DIST_CELL ) ); + memset( m_DistSide[side], 0, ii * sizeof( DIST_CELL ) ); - if( m_DistSide[side] == NULL ) + if( m_DistSide[side] == nullptr ) return -1; // allocate Dir (chars) m_DirSide[side] = (char*) operator new( ii ); memset( m_DirSide[side], 0, ii ); - if( m_DirSide[side] == NULL ) + if( m_DirSide[side] == nullptr ) return -1; - side = TOP; + side = AR_SIDE_TOP; } - m_MemSize = m_RouteCount * ii * ( sizeof(MATRIX_CELL) - + sizeof(DIST_CELL) + sizeof(char) ); + m_MemSize = + m_RouteCount * ii * ( sizeof( MATRIX_CELL ) + sizeof( DIST_CELL ) + sizeof( char ) ); return m_MemSize; } -void MATRIX_ROUTING_HEAD::UnInitRoutingMatrix() +void AR_MATRIX::UnInitRoutingMatrix() { int ii; m_InitMatrixDone = false; - for( ii = 0; ii < MAX_ROUTING_LAYERS_COUNT; ii++ ) + for( ii = 0; ii < AR_MAX_ROUTING_LAYERS_COUNT; ii++ ) { // de-allocate Dir matrix if( m_DirSide[ii] ) { delete m_DirSide[ii]; - m_DirSide[ii] = NULL; + m_DirSide[ii] = nullptr; } // de-allocate Distances matrix if( m_DistSide[ii] ) { delete m_DistSide[ii]; - m_DistSide[ii] = NULL; + m_DistSide[ii] = nullptr; } // de-allocate cells matrix if( m_BoardSide[ii] ) { delete m_BoardSide[ii]; - m_BoardSide[ii] = NULL; + m_BoardSide[ii] = nullptr; } } m_Nrows = m_Ncols = 0; } - -/** - * Function PlaceCells - * Initialize the matrix routing by setting obstacles for each occupied cell - * a cell set to HOLE is an obstacle for tracks and vias - * a cell set to VIA_IMPOSSIBLE is an obstacle for vias only. - * a cell set to CELL_is_EDGE is a frontier. - * Tracks and vias having the same net code as net_code are skipped - * (htey do not are obstacles) - * - * For single-sided Routing 1: - * BOTTOM side is used, and Route_Layer_BOTTOM = Route_Layer_TOP - * - * If flag == FORCE_PADS: all pads will be put in matrix as obstacles. - */ -void PlaceCells( BOARD* aPcb, int net_code, int flag ) -{ - int ux0 = 0, uy0 = 0, ux1, uy1, dx, dy; - int marge, via_marge; - LSET layerMask; - - // use the default NETCLASS? - NETCLASSPTR nc = aPcb->GetDesignSettings().GetDefault(); - - int trackWidth = nc->GetTrackWidth(); - int clearance = nc->GetClearance(); - int viaSize = nc->GetViaDiameter(); - - marge = clearance + (trackWidth / 2); - via_marge = clearance + (viaSize / 2); - - // Place PADS on matrix routing: - for( unsigned i = 0; i < aPcb->GetPadCount(); ++i ) - { - D_PAD* pad = aPcb->GetPad( i ); - - if( net_code != pad->GetNetCode() || (flag & FORCE_PADS) ) - { - ::PlacePad( pad, HOLE, marge, WRITE_CELL ); - } - - ::PlacePad( pad, VIA_IMPOSSIBLE, via_marge, WRITE_OR_CELL ); - } - - // Place outlines of modules on matrix routing, if they are on a copper layer - // or on the edge layer - - for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) - { - for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) - { - switch( item->Type() ) - { - case PCB_MODULE_EDGE_T: - { - EDGE_MODULE* edge = (EDGE_MODULE*) item; - EDGE_MODULE tmpEdge( *edge ); - - if( tmpEdge.GetLayer() == Edge_Cuts ) - tmpEdge.SetLayer( UNDEFINED_LAYER ); - - TraceSegmentPcb( &tmpEdge, HOLE, marge, WRITE_CELL ); - TraceSegmentPcb( &tmpEdge, VIA_IMPOSSIBLE, via_marge, WRITE_OR_CELL ); - } - break; - - default: - break; - } - } - } - - // Place board outlines and texts on copper layers: - for( auto item : aPcb->Drawings() ) - { - switch( item->Type() ) - { - case PCB_LINE_T: - { - DRAWSEGMENT* DrawSegm; - - int type_cell = HOLE; - DrawSegm = (DRAWSEGMENT*) item; - DRAWSEGMENT tmpSegm( DrawSegm ); - - if( DrawSegm->GetLayer() == Edge_Cuts ) - { - tmpSegm.SetLayer( UNDEFINED_LAYER ); - type_cell |= CELL_is_EDGE; - } - - TraceSegmentPcb( &tmpSegm, type_cell, marge, WRITE_CELL ); - } - break; - - case PCB_TEXT_T: - { - TEXTE_PCB* PtText = (TEXTE_PCB*) item; - - if( PtText->GetText().Length() == 0 ) - break; - - EDA_RECT textbox = PtText->GetTextBox( -1 ); - ux0 = textbox.GetX(); - uy0 = textbox.GetY(); - dx = textbox.GetWidth(); - dy = textbox.GetHeight(); - - // Put bounding box (rectangle) on matrix - dx /= 2; - dy /= 2; - - ux1 = ux0 + dx; - uy1 = uy0 + dy; - - ux0 -= dx; - uy0 -= dy; - - layerMask = LSET( PtText->GetLayer() ); - - TraceFilledRectangle( ux0 - marge, uy0 - marge, ux1 + marge, - uy1 + marge, PtText->GetTextAngle(), - layerMask, HOLE, WRITE_CELL ); - - TraceFilledRectangle( ux0 - via_marge, uy0 - via_marge, - ux1 + via_marge, uy1 + via_marge, - PtText->GetTextAngle(), - layerMask, VIA_IMPOSSIBLE, WRITE_OR_CELL ); - } - break; - - default: - break; - } - } - - // Put tracks and vias on matrix - for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) - { - if( net_code == track->GetNetCode() ) - continue; - - TraceSegmentPcb( track, HOLE, marge, WRITE_CELL ); - TraceSegmentPcb( track, VIA_IMPOSSIBLE, via_marge, WRITE_OR_CELL ); - } -} - - -int Build_Work( BOARD* Pcb ) -{ - RATSNEST_ITEM* pt_rats; - D_PAD* pt_pad; - int r1, r2, c1, c2, current_net_code; - RATSNEST_ITEM* pt_ch; - int demi_pas = RoutingMatrix.m_GridRouting / 2; - wxString msg; - - InitWork(); // clear work list - int cellCount = 0; - - for( unsigned ii = 0; ii < Pcb->GetRatsnestsCount(); ii++ ) - { - pt_rats = &Pcb->m_FullRatsnest[ii]; - - /* We consider here only ratsnest that are active ( obviously not yet routed) - * and routables (that are not yet attempt to be routed and fail - */ - if( (pt_rats->m_Status & CH_ACTIF) == 0 ) - continue; - - if( pt_rats->m_Status & CH_UNROUTABLE ) - continue; - - if( (pt_rats->m_Status & CH_ROUTE_REQ) == 0 ) - continue; - - pt_pad = pt_rats->m_PadStart; - - current_net_code = pt_pad->GetNetCode(); - pt_ch = pt_rats; - - r1 = ( pt_pad->GetPosition().y - RoutingMatrix.m_BrdBox.GetY() + demi_pas ) - / RoutingMatrix.m_GridRouting; - - if( r1 < 0 || r1 >= RoutingMatrix.m_Nrows ) - { - msg.Printf( wxT( "error : row = %d ( padY %d pcbY %d) " ), r1, - pt_pad->GetPosition().y, RoutingMatrix.m_BrdBox.GetY() ); - wxMessageBox( msg ); - return 0; - } - - c1 = ( pt_pad->GetPosition().x - RoutingMatrix.m_BrdBox.GetX() + demi_pas ) / RoutingMatrix.m_GridRouting; - - if( c1 < 0 || c1 >= RoutingMatrix.m_Ncols ) - { - msg.Printf( wxT( "error : col = %d ( padX %d pcbX %d) " ), c1, - pt_pad->GetPosition().x, RoutingMatrix.m_BrdBox.GetX() ); - wxMessageBox( msg ); - return 0; - } - - pt_pad = pt_rats->m_PadEnd; - - r2 = ( pt_pad->GetPosition().y - RoutingMatrix.m_BrdBox.GetY() - + demi_pas ) / RoutingMatrix.m_GridRouting; - - if( r2 < 0 || r2 >= RoutingMatrix.m_Nrows ) - { - msg.Printf( wxT( "error : row = %d ( padY %d pcbY %d) " ), r2, - pt_pad->GetPosition().y, RoutingMatrix.m_BrdBox.GetY() ); - wxMessageBox( msg ); - return 0; - } - - c2 = ( pt_pad->GetPosition().x - RoutingMatrix.m_BrdBox.GetX() + demi_pas ) - / RoutingMatrix.m_GridRouting; - - if( c2 < 0 || c2 >= RoutingMatrix.m_Ncols ) - { - msg.Printf( wxT( "error : col = %d ( padX %d pcbX %d) " ), c2, - pt_pad->GetPosition().x, RoutingMatrix.m_BrdBox.GetX() ); - wxMessageBox( msg ); - return 0; - } - - SetWork( r1, c1, current_net_code, r2, c2, pt_ch, 0 ); - cellCount++; - } - - SortWork(); - return cellCount; -} - // Initialize m_opWriteCell member to make the aLogicOp -void MATRIX_ROUTING_HEAD::SetCellOperation( int aLogicOp ) +void AR_MATRIX::SetCellOperation( AR_MATRIX::CELL_OP aLogicOp ) { switch( aLogicOp ) { default: - case WRITE_CELL: - m_opWriteCell = &MATRIX_ROUTING_HEAD::SetCell; - break; + case WRITE_CELL: m_opWriteCell = &AR_MATRIX::SetCell; break; - case WRITE_OR_CELL: - m_opWriteCell = &MATRIX_ROUTING_HEAD::OrCell; - break; + case WRITE_OR_CELL: m_opWriteCell = &AR_MATRIX::OrCell; break; - case WRITE_XOR_CELL: - m_opWriteCell = &MATRIX_ROUTING_HEAD::XorCell; - break; + case WRITE_XOR_CELL: m_opWriteCell = &AR_MATRIX::XorCell; break; - case WRITE_AND_CELL: - m_opWriteCell = &MATRIX_ROUTING_HEAD::AndCell; - break; + case WRITE_AND_CELL: m_opWriteCell = &AR_MATRIX::AndCell; break; - case WRITE_ADD_CELL: - m_opWriteCell = &MATRIX_ROUTING_HEAD::AddCell; - break; + case WRITE_ADD_CELL: m_opWriteCell = &AR_MATRIX::AddCell; break; } } /* return the value stored in a cell */ -MATRIX_CELL MATRIX_ROUTING_HEAD::GetCell( int aRow, int aCol, int aSide ) +AR_MATRIX::MATRIX_CELL AR_MATRIX::GetCell( int aRow, int aCol, int aSide ) { MATRIX_CELL* p; - p = RoutingMatrix.m_BoardSide[aSide]; + p = m_BoardSide[aSide]; return p[aRow * m_Ncols + aCol]; } /* basic cell operation : WRITE operation */ -void MATRIX_ROUTING_HEAD::SetCell( int aRow, int aCol, int aSide, MATRIX_CELL x ) +void AR_MATRIX::SetCell( int aRow, int aCol, int aSide, MATRIX_CELL x ) { MATRIX_CELL* p; - p = RoutingMatrix.m_BoardSide[aSide]; + p = m_BoardSide[aSide]; p[aRow * m_Ncols + aCol] = x; } /* basic cell operation : OR operation */ -void MATRIX_ROUTING_HEAD::OrCell( int aRow, int aCol, int aSide, MATRIX_CELL x ) +void AR_MATRIX::OrCell( int aRow, int aCol, int aSide, MATRIX_CELL x ) { MATRIX_CELL* p; - p = RoutingMatrix.m_BoardSide[aSide]; + p = m_BoardSide[aSide]; p[aRow * m_Ncols + aCol] |= x; } /* basic cell operation : XOR operation */ -void MATRIX_ROUTING_HEAD::XorCell( int aRow, int aCol, int aSide, MATRIX_CELL x ) +void AR_MATRIX::XorCell( int aRow, int aCol, int aSide, MATRIX_CELL x ) { MATRIX_CELL* p; - p = RoutingMatrix.m_BoardSide[aSide]; + p = m_BoardSide[aSide]; p[aRow * m_Ncols + aCol] ^= x; } /* basic cell operation : AND operation */ -void MATRIX_ROUTING_HEAD::AndCell( int aRow, int aCol, int aSide, MATRIX_CELL x ) +void AR_MATRIX::AndCell( int aRow, int aCol, int aSide, MATRIX_CELL x ) { MATRIX_CELL* p; - p = RoutingMatrix.m_BoardSide[aSide]; + p = m_BoardSide[aSide]; p[aRow * m_Ncols + aCol] &= x; } /* basic cell operation : ADD operation */ -void MATRIX_ROUTING_HEAD::AddCell( int aRow, int aCol, int aSide, MATRIX_CELL x ) +void AR_MATRIX::AddCell( int aRow, int aCol, int aSide, MATRIX_CELL x ) { MATRIX_CELL* p; - p = RoutingMatrix.m_BoardSide[aSide]; + p = m_BoardSide[aSide]; p[aRow * m_Ncols + aCol] += x; } // fetch distance cell -DIST_CELL MATRIX_ROUTING_HEAD::GetDist( int aRow, int aCol, int aSide ) // fetch distance cell +AR_MATRIX::DIST_CELL AR_MATRIX::GetDist( int aRow, int aCol, int aSide ) // fetch distance cell { DIST_CELL* p; - p = RoutingMatrix.m_DistSide[aSide]; + p = m_DistSide[aSide]; return p[aRow * m_Ncols + aCol]; } // store distance cell -void MATRIX_ROUTING_HEAD::SetDist( int aRow, int aCol, int aSide, DIST_CELL x ) +void AR_MATRIX::SetDist( int aRow, int aCol, int aSide, DIST_CELL x ) { DIST_CELL* p; - p = RoutingMatrix.m_DistSide[aSide]; + p = m_DistSide[aSide]; p[aRow * m_Ncols + aCol] = x; } // fetch direction cell -int MATRIX_ROUTING_HEAD::GetDir( int aRow, int aCol, int aSide ) +int AR_MATRIX::GetDir( int aRow, int aCol, int aSide ) { DIR_CELL* p; - p = RoutingMatrix.m_DirSide[aSide]; - return (int) (p[aRow * m_Ncols + aCol]); + p = m_DirSide[aSide]; + return (int) ( p[aRow * m_Ncols + aCol] ); } // store direction cell -void MATRIX_ROUTING_HEAD::SetDir( int aRow, int aCol, int aSide, int x ) +void AR_MATRIX::SetDir( int aRow, int aCol, int aSide, int x ) { DIR_CELL* p; - p = RoutingMatrix.m_DirSide[aSide]; + p = m_DirSide[aSide]; p[aRow * m_Ncols + aCol] = (char) x; } + +/* The tables of distances and keep out areas are established on the basis of a + * 50 units grid size (the pitch between the cells is 50 units). + * The actual distance could be computed by a scaling factor, but this is + * not needed, we can use only reduced values + */ + +/* calculate approximate distance (manhattan distance) + */ +int AR_MATRIX::GetApxDist( int r1, int c1, int r2, int c2 ) +{ + int d1, d2; /* row and column deltas */ + + if( ( d1 = r1 - r2 ) < 0 ) /* get absolute row delta */ + d1 = -d1; + + if( ( d2 = c1 - c2 ) < 0 ) /* get absolute column delta */ + d2 = -d2; + + return ( d1 + d2 ) * 50; +} + + +/* distance to go thru a cell (en mils) */ +static const int dist[10][10] = { /* OT=Otherside, OR=Origin (source) cell */ + /*..........N, NE, E, SE, S, SW, W, NW, OT, OR */ + /* N */ { 50, 60, 35, 60, 99, 60, 35, 60, 12, 12 }, + /* NE */ { 60, 71, 60, 71, 60, 99, 60, 71, 23, 23 }, + /* E */ { 35, 60, 50, 60, 35, 60, 99, 60, 12, 12 }, + /* SE */ { 60, 71, 60, 71, 60, 71, 60, 99, 23, 23 }, + /* S */ { 99, 60, 35, 60, 50, 60, 35, 60, 12, 12 }, + /* SW */ { 60, 99, 60, 71, 60, 71, 60, 71, 23, 23 }, + /* W */ { 35, 60, 99, 60, 35, 60, 50, 60, 12, 12 }, + /* NW */ { 60, 71, 60, 99, 60, 71, 60, 71, 23, 23 }, + + /* OT */ { 12, 23, 12, 23, 12, 23, 12, 23, 99, 99 }, + /* OR */ { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 } +}; + + +/* penalty for extraneous holes and corners, scaled by sharpness of turn */ +static const int penalty[10][10] = { /* OT=Otherside, OR=Origin (source) cell */ + /*......... N, NE, E, SE, S, SW, W, NW, OT, OR */ + /* N */ { 0, 5, 10, 15, 20, 15, 10, 5, 50, 0 }, + /* NE */ { 5, 0, 5, 10, 15, 20, 15, 10, 50, 0 }, + /* E */ { 10, 5, 0, 5, 10, 15, 20, 15, 50, 0 }, + /* SE */ { 15, 10, 5, 0, 5, 10, 15, 20, 50, 0 }, + /* S */ { 20, 15, 10, 5, 0, 5, 10, 15, 50, 0 }, + /* SW */ { 15, 20, 15, 10, 5, 0, 5, 10, 50, 0 }, + /* W */ { 10, 15, 20, 15, 10, 5, 0, 5, 50, 0 }, + /* NW */ { 5, 10, 15, 20, 15, 10, 5, 0, 50, 0 }, + + /* OT */ { 50, 50, 50, 50, 50, 50, 50, 50, 100, 0 }, + /* OR */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + + +/* penalty pour directions preferencielles */ +#define PN 20 +static const int dir_penalty_TOP[10][10] = { + /* OT=Otherside, OR=Origin (source) cell */ + /*......... N, NE, E, SE, S, SW, W, NW, OT, OR */ + /* N */ { PN, 0, 0, 0, PN, 0, 0, 0, 0, 0 }, + /* NE */ { PN, 0, 0, 0, PN, 0, 0, 0, 0, 0 }, + /* E */ { PN, 0, 0, 0, PN, 0, 0, 0, 0, 0 }, + /* SE */ { PN, 0, 0, 0, PN, 0, 0, 0, 0, 0 }, + /* S */ { PN, 0, 0, 0, PN, 0, 0, 0, 0, 0 }, + /* SW */ { PN, 0, 0, 0, PN, 0, 0, 0, 0, 0 }, + /* W */ { PN, 0, 0, 0, PN, 0, 0, 0, 0, 0 }, + /* NW */ { PN, 0, 0, 0, PN, 0, 0, 0, 0, 0 }, + + /* OT */ { PN, 0, 0, 0, PN, 0, 0, 0, 0, 0 }, + /* OR */ { PN, 0, 0, 0, PN, 0, 0, 0, 0, 0 } +}; + + +static int dir_penalty_BOTTOM[10][10] = { + /* OT=Otherside, OR=Origin (source) cell */ + /*......... N, NE, E, SE, S, SW, W, NW, OT, OR */ + /* N */ { 0, 0, PN, 0, 0, 0, PN, 0, 0, 0 }, + /* NE */ { 0, 0, PN, 0, 0, 0, PN, 0, 0, 0 }, + /* E */ { 0, 0, PN, 0, 0, 0, PN, 0, 0, 0 }, + /* SE */ { 0, 0, PN, 0, 0, 0, PN, 0, 0, 0 }, + /* S */ { 0, 0, PN, 0, 0, 0, PN, 0, 0, 0 }, + /* SW */ { 0, 0, PN, 0, 0, 0, PN, 0, 0, 0 }, + /* W */ { 0, 0, PN, 0, 0, 0, PN, 0, 0, 0 }, + /* NW */ { 0, 0, PN, 0, 0, 0, PN, 0, 0, 0 }, + + /* OT */ { 0, 0, PN, 0, 0, 0, PN, 0, 0, 0 }, + /* OR */ { 0, 0, PN, 0, 0, 0, PN, 0, 0, 0 } +}; + +/* +** x is the direction to enter the cell of interest. +** y is the direction to exit the cell of interest. +** z is the direction to really exit the cell, if y=FROM_OTHERSIDE. +** +** return the distance of the trace through the cell of interest. +** the calculation is driven by the tables above. +*/ + + +/* calculate distance (with penalty) of a trace through a cell +*/ +int AR_MATRIX::CalcDist( int x, int y, int z, int side ) +{ + int adjust, ldist; + + adjust = 0; /* set if hole is encountered */ + + if( x == CELL_IS_EMPTY ) + x = 10; + + if( y == CELL_IS_EMPTY ) + { + y = 10; + } + else if( y == FROM_OTHERSIDE ) + { + if( z == CELL_IS_EMPTY ) + z = 10; + + adjust = penalty[x - 1][z - 1]; + } + + ldist = dist[x - 1][y - 1] + penalty[x - 1][y - 1] + adjust; + + if( m_RouteCount > 1 ) + { + if( side == AR_SIDE_BOTTOM ) + ldist += dir_penalty_TOP[x - 1][y - 1]; + + if( side == AR_SIDE_TOP ) + ldist += dir_penalty_BOTTOM[x - 1][y - 1]; + } + + return ldist * 10; +} + + +#define OP_CELL( layer, dy, dx ) \ + { \ + if( layer == UNDEFINED_LAYER ) \ + { \ + WriteCell( dy, dx, AR_SIDE_BOTTOM, color ); \ + if( m_RoutingLayersCount > 1 ) \ + WriteCell( dy, dx, AR_SIDE_TOP, color ); \ + } \ + else \ + { \ + if( layer == m_routeLayerBottom ) \ + WriteCell( dy, dx, AR_SIDE_BOTTOM, color ); \ + if( m_RoutingLayersCount > 1 ) \ + if( layer == m_routeLayerTop ) \ + WriteCell( dy, dx, AR_SIDE_TOP, color ); \ + } \ + } + +/* Fills all cells inside a segment + * half-width = lg, org = ux0,uy0 end = ux1,uy1 + * coordinates are in PCB units + */ +void AR_MATRIX::drawSegmentQcq( int ux0, int uy0, int ux1, int uy1, int lg, LAYER_NUM layer, + int color, AR_MATRIX::CELL_OP op_logic ) +{ + int64_t row, col; + int64_t inc; + int64_t row_max, col_max, row_min, col_min; + int64_t demi_pas; + + int cx, cy, dx, dy; + + + //printf("segmQcq %d %d %d %d\n", ux0, uy0, ux1, uy1); + + SetCellOperation( op_logic ); + + // Make coordinate ux1 tj > ux0 to simplify calculations + if( ux1 < ux0 ) + { + std::swap( ux1, ux0 ); + std::swap( uy1, uy0 ); + } + + // Calculating the incrementing the Y axis + inc = 1; + + if( uy1 < uy0 ) + inc = -1; + + demi_pas = m_GridRouting / 2; + + col_min = ( ux0 - lg ) / m_GridRouting; + + if( col_min < 0 ) + col_min = 0; + + col_max = ( ux1 + lg + demi_pas ) / m_GridRouting; + + if( col_max > ( m_Ncols - 1 ) ) + col_max = m_Ncols - 1; + + if( inc > 0 ) + { + row_min = ( uy0 - lg ) / m_GridRouting; + row_max = ( uy1 + lg + demi_pas ) / m_GridRouting; + } + else + { + row_min = ( uy1 - lg ) / m_GridRouting; + row_max = ( uy0 + lg + demi_pas ) / m_GridRouting; + } + + if( row_min < 0 ) + row_min = 0; + + if( row_min > ( m_Nrows - 1 ) ) + row_min = m_Nrows - 1; + + if( row_max < 0 ) + row_max = 0; + + if( row_max > ( m_Nrows - 1 ) ) + row_max = m_Nrows - 1; + + dx = ux1 - ux0; + dy = uy1 - uy0; + + double angle; + if( dx ) + { + angle = ArcTangente( dy, dx ); + } + else + { + angle = 900; + + if( dy < 0 ) + angle = -900; + } + + RotatePoint( &dx, &dy, angle ); // dx = length, dy = 0 + + + //printf("col_min %d max %d row_min %d max %d\n", col_min, col_max, row_min, row_max); + + for( col = col_min; col <= col_max; col++ ) + { + int64_t cxr; + cxr = ( col * m_GridRouting ) - ux0; + + for( row = row_min; row <= row_max; row++ ) + { + cy = ( row * m_GridRouting ) - uy0; + cx = cxr; + RotatePoint( &cx, &cy, angle ); + + if( abs( cy ) > lg ) + continue; // The point is too far on the Y axis. + + /* This point a test is close to the segment: the position + * along the X axis must be tested. + */ + if( ( cx >= 0 ) && ( cx <= dx ) ) + { + OP_CELL( layer, row, col ); + continue; + } + + // Examination of extremities are rounded. + if( ( cx < 0 ) && ( cx >= -lg ) ) + { + if( ( ( cx * cx ) + ( cy * cy ) ) <= ( lg * lg ) ) + OP_CELL( layer, row, col ); + + continue; + } + + if( ( cx > dx ) && ( cx <= ( dx + lg ) ) ) + { + if( ( ( ( cx - dx ) * ( cx - dx ) ) + ( cy * cy ) ) <= ( lg * lg ) ) + OP_CELL( layer, row, col ); + + continue; + } + } + } +} + + +/* Fills all cells of the routing matrix contained in the circle + * half-width = lg, center = ux0, uy0, ux1,uy1 is a point on the circle. + * coord are in PCB units. + */ +void AR_MATRIX::traceCircle( int ux0, int uy0, int ux1, int uy1, int lg, LAYER_NUM layer, int color, + AR_MATRIX::CELL_OP op_logic ) +{ + int radius, nb_segm; + int x0, y0, // Starting point of the current segment trace. + x1, y1; // End point. + int ii; + int angle; + + radius = KiROUND( Distance( ux0, uy0, ux1, uy1 ) ); + + x0 = x1 = radius; + y0 = y1 = 0; + + if( lg < 1 ) + lg = 1; + + nb_segm = ( 2 * radius ) / lg; + + if( nb_segm < 5 ) + nb_segm = 5; + + if( nb_segm > 100 ) + nb_segm = 100; + + for( ii = 1; ii < nb_segm; ii++ ) + { + angle = ( 3600 * ii ) / nb_segm; + x1 = KiROUND( cosdecideg( radius, angle ) ); + y1 = KiROUND( sindecideg( radius, angle ) ); + drawSegmentQcq( x0 + ux0, y0 + uy0, x1 + ux0, y1 + uy0, lg, layer, color, op_logic ); + x0 = x1; + y0 = y1; + } + + drawSegmentQcq( x1 + ux0, y1 + uy0, ux0 + radius, uy0, lg, layer, color, op_logic ); +} + + +void AR_MATRIX::traceFilledCircle( + int cx, int cy, int radius, LSET aLayerMask, int color, AR_MATRIX::CELL_OP op_logic ) +{ + int row, col; + int ux0, uy0, ux1, uy1; + int row_max, col_max, row_min, col_min; + int trace = 0; + double fdistmin, fdistx, fdisty; + int tstwrite = 0; + int distmin; + + if( aLayerMask[m_routeLayerBottom] ) + trace = 1; // Trace on BOTTOM + + if( aLayerMask[m_routeLayerTop] ) + if( m_RoutingLayersCount > 1 ) + trace |= 2; // Trace on TOP + + if( trace == 0 ) + return; + + SetCellOperation( op_logic ); + + cx -= GetBrdCoordOrigin().x; + cy -= GetBrdCoordOrigin().y; + + distmin = radius; + + // Calculate the bounding rectangle of the circle. + ux0 = cx - radius; + uy0 = cy - radius; + ux1 = cx + radius; + uy1 = cy + radius; + + // Calculate limit coordinates of cells belonging to the rectangle. + row_max = uy1 / m_GridRouting; + col_max = ux1 / m_GridRouting; + row_min = uy0 / m_GridRouting; // if (uy0 > row_min*Board.m_GridRouting) row_min++; + col_min = ux0 / m_GridRouting; // if (ux0 > col_min*Board.m_GridRouting) col_min++; + + if( row_min < 0 ) + row_min = 0; + + if( row_max >= ( m_Nrows - 1 ) ) + row_max = m_Nrows - 1; + + if( col_min < 0 ) + col_min = 0; + + if( col_max >= ( m_Ncols - 1 ) ) + col_max = m_Ncols - 1; + + // Calculate coordinate limits of cell belonging to the rectangle. + if( row_min > row_max ) + row_max = row_min; + + if( col_min > col_max ) + col_max = col_min; + + fdistmin = (double) distmin * distmin; + + for( row = row_min; row <= row_max; row++ ) + { + fdisty = (double) ( cy - ( row * m_GridRouting ) ); + fdisty *= fdisty; + + for( col = col_min; col <= col_max; col++ ) + { + fdistx = (double) ( cx - ( col * m_GridRouting ) ); + fdistx *= fdistx; + + if( fdistmin <= ( fdistx + fdisty ) ) + continue; + + if( trace & 1 ) + WriteCell( row, col, AR_SIDE_BOTTOM, color ); + + if( trace & 2 ) + WriteCell( row, col, AR_SIDE_TOP, color ); + + tstwrite = 1; + } + } + + if( tstwrite ) + return; + + /* If no cell has been written, it affects the 4 neighboring diagonal + * (Adverse event: pad off grid in the center of the 4 neighboring + * diagonal) */ + distmin = m_GridRouting / 2 + 1; + fdistmin = ( (double) distmin * distmin ) * 2; // Distance to center point diagonally + + for( row = row_min; row <= row_max; row++ ) + { + fdisty = (double) ( cy - ( row * m_GridRouting ) ); + fdisty *= fdisty; + + for( col = col_min; col <= col_max; col++ ) + { + fdistx = (double) ( cx - ( col * m_GridRouting ) ); + fdistx *= fdistx; + + if( fdistmin <= ( fdistx + fdisty ) ) + continue; + + if( trace & 1 ) + WriteCell( row, col, AR_SIDE_BOTTOM, color ); + + if( trace & 2 ) + WriteCell( row, col, AR_SIDE_TOP, color ); + } + } +} + + +/* Fills all routing matrix cells contained in the arc + * angle = ArcAngle, half-width lg + * center = ux0,uy0, starting at ux1, uy1. Coordinates are in + * PCB units. + */ +void AR_MATRIX::traceArc( int ux0, int uy0, int ux1, int uy1, double ArcAngle, int lg, + LAYER_NUM layer, int color, AR_MATRIX::CELL_OP op_logic ) +{ + int radius, nb_segm; + int x0, y0, // Starting point of the current segment trace + x1, y1; // End point + int ii; + double angle, StAngle; + + + radius = KiROUND( Distance( ux0, uy0, ux1, uy1 ) ); + + x0 = ux1 - ux0; + y0 = uy1 - uy0; + StAngle = ArcTangente( uy1 - uy0, ux1 - ux0 ); + + if( lg < 1 ) + lg = 1; + + nb_segm = ( 2 * radius ) / lg; + nb_segm = ( nb_segm * std::abs( ArcAngle ) ) / 3600; + + if( nb_segm < 5 ) + nb_segm = 5; + + if( nb_segm > 100 ) + nb_segm = 100; + + for( ii = 1; ii <= nb_segm; ii++ ) + { + angle = ( ArcAngle * ii ) / nb_segm; + angle += StAngle; + + NORMALIZE_ANGLE_POS( angle ); + + x1 = KiROUND( cosdecideg( radius, angle ) ); + y1 = KiROUND( cosdecideg( radius, angle ) ); + drawSegmentQcq( x0 + ux0, y0 + uy0, x1 + ux0, y1 + uy0, lg, layer, color, op_logic ); + x0 = x1; + y0 = y1; + } +} + + +void AR_MATRIX::TraceFilledRectangle( int ux0, int uy0, int ux1, int uy1, double angle, + LSET aLayerMask, int color, AR_MATRIX::CELL_OP op_logic ) +{ + int row, col; + int cx, cy; // Center of rectangle + int radius; // Radius of the circle + int row_min, row_max, col_min, col_max; + int rotrow, rotcol; + int trace = 0; + + if( aLayerMask[m_routeLayerBottom] ) + trace = 1; // Trace on BOTTOM + + if( aLayerMask[m_routeLayerTop] ) + { + if( m_RoutingLayersCount > 1 ) + trace |= 2; // Trace on TOP + } + + if( trace == 0 ) + return; + + SetCellOperation( op_logic ); + + ux0 -= GetBrdCoordOrigin().x; + uy0 -= GetBrdCoordOrigin().y; + ux1 -= GetBrdCoordOrigin().x; + uy1 -= GetBrdCoordOrigin().y; + + cx = ( ux0 + ux1 ) / 2; + cy = ( uy0 + uy1 ) / 2; + radius = KiROUND( Distance( ux0, uy0, cx, cy ) ); + + // Calculating coordinate limits belonging to the rectangle. + row_max = ( cy + radius ) / m_GridRouting; + col_max = ( cx + radius ) / m_GridRouting; + row_min = ( cy - radius ) / m_GridRouting; + + if( uy0 > row_min * m_GridRouting ) + row_min++; + + col_min = ( cx - radius ) / m_GridRouting; + + if( ux0 > col_min * m_GridRouting ) + col_min++; + + if( row_min < 0 ) + row_min = 0; + + if( row_max >= ( m_Nrows - 1 ) ) + row_max = m_Nrows - 1; + + if( col_min < 0 ) + col_min = 0; + + if( col_max >= ( m_Ncols - 1 ) ) + col_max = m_Ncols - 1; + + for( row = row_min; row <= row_max; row++ ) + { + for( col = col_min; col <= col_max; col++ ) + { + rotrow = row * m_GridRouting; + rotcol = col * m_GridRouting; + RotatePoint( &rotcol, &rotrow, cx, cy, -angle ); + + if( rotrow <= uy0 ) + continue; + + if( rotrow >= uy1 ) + continue; + + if( rotcol <= ux0 ) + continue; + + if( rotcol >= ux1 ) + continue; + + if( trace & 1 ) + WriteCell( row, col, AR_SIDE_BOTTOM, color ); + + if( trace & 2 ) + WriteCell( row, col, AR_SIDE_TOP, color ); + } + } +} + + +void AR_MATRIX::TraceFilledRectangle( int ux0, int uy0, int ux1, int uy1, LSET aLayerMask, + int color, AR_MATRIX::CELL_OP op_logic ) +{ + int row, col; + int row_min, row_max, col_min, col_max; + int trace = 0; + + if( aLayerMask[m_routeLayerBottom] ) + trace = 1; // Trace on BOTTOM + + if( aLayerMask[m_routeLayerTop] && m_RoutingLayersCount > 1 ) + trace |= 2; // Trace on TOP + + if( trace == 0 ) + return; + + SetCellOperation( op_logic ); + + ux0 -= GetBrdCoordOrigin().x; + uy0 -= GetBrdCoordOrigin().y; + ux1 -= GetBrdCoordOrigin().x; + uy1 -= GetBrdCoordOrigin().y; + + // Calculating limits coord cells belonging to the rectangle. + row_max = uy1 / m_GridRouting; + col_max = ux1 / m_GridRouting; + row_min = uy0 / m_GridRouting; + + if( uy0 > row_min * m_GridRouting ) + row_min++; + + col_min = ux0 / m_GridRouting; + + if( ux0 > col_min * m_GridRouting ) + col_min++; + + if( row_min < 0 ) + row_min = 0; + + if( row_max >= ( m_Nrows - 1 ) ) + row_max = m_Nrows - 1; + + if( col_min < 0 ) + col_min = 0; + + if( col_max >= ( m_Ncols - 1 ) ) + col_max = m_Ncols - 1; + + for( row = row_min; row <= row_max; row++ ) + { + for( col = col_min; col <= col_max; col++ ) + { + if( trace & 1 ) + WriteCell( row, col, AR_SIDE_BOTTOM, color ); + + if( trace & 2 ) + WriteCell( row, col, AR_SIDE_TOP, color ); + } + } +} + + +/* Draws a line, if layer = -1 on all layers + */ +void AR_MATRIX::tracePcbLine( + int x0, int y0, int x1, int y1, LAYER_NUM layer, int color, AR_MATRIX::CELL_OP op_logic ) +{ + int dx, dy, lim; + int cumul, inc, il, delta; + + SetCellOperation( op_logic ); + + if( x0 == x1 ) // Vertical. + { + if( y1 < y0 ) + std::swap( y0, y1 ); + + dy = y0 / m_GridRouting; + lim = y1 / m_GridRouting; + dx = x0 / m_GridRouting; + + // Clipping limits of board. + if( ( dx < 0 ) || ( dx >= m_Ncols ) ) + return; + + if( dy < 0 ) + dy = 0; + + if( lim >= m_Nrows ) + lim = m_Nrows - 1; + + for( ; dy <= lim; dy++ ) + { + OP_CELL( layer, dy, dx ); + } + + return; + } + + if( y0 == y1 ) // Horizontal + { + if( x1 < x0 ) + std::swap( x0, x1 ); + + dx = x0 / m_GridRouting; + lim = x1 / m_GridRouting; + dy = y0 / m_GridRouting; + + // Clipping limits of board. + if( ( dy < 0 ) || ( dy >= m_Nrows ) ) + return; + + if( dx < 0 ) + dx = 0; + + if( lim >= m_Ncols ) + lim = m_Ncols - 1; + + for( ; dx <= lim; dx++ ) + { + OP_CELL( layer, dy, dx ); + } + + return; + } + + // Here is some perspective: using the algorithm LUCAS. + if( abs( x1 - x0 ) >= abs( y1 - y0 ) ) // segment slightly inclined/ + { + if( x1 < x0 ) + { + std::swap( x1, x0 ); + std::swap( y1, y0 ); + } + + dx = x0 / m_GridRouting; + lim = x1 / m_GridRouting; + dy = y0 / m_GridRouting; + inc = 1; + + if( y1 < y0 ) + inc = -1; + + il = lim - dx; + cumul = il / 2; + delta = abs( y1 - y0 ) / m_GridRouting; + + for( ; dx <= lim; ) + { + if( ( dx >= 0 ) && ( dy >= 0 ) && ( dx < m_Ncols ) && ( dy < m_Nrows ) ) + { + OP_CELL( layer, dy, dx ); + } + + dx++; + cumul += delta; + + if( cumul > il ) + { + cumul -= il; + dy += inc; + } + } + } + else + { + if( y1 < y0 ) + { + std::swap( x1, x0 ); + std::swap( y1, y0 ); + } + + dy = y0 / m_GridRouting; + lim = y1 / m_GridRouting; + dx = x0 / m_GridRouting; + inc = 1; + + if( x1 < x0 ) + inc = -1; + + il = lim - dy; + cumul = il / 2; + delta = abs( x1 - x0 ) / m_GridRouting; + + for( ; dy <= lim; ) + { + if( ( dx >= 0 ) && ( dy >= 0 ) && ( dx < m_Ncols ) && ( dy < m_Nrows ) ) + { + OP_CELL( layer, dy, dx ); + } + + dy++; + cumul += delta; + + if( cumul > il ) + { + cumul -= il; + dx += inc; + } + } + } +} + +void AR_MATRIX::TraceSegmentPcb( + DRAWSEGMENT* pt_segm, int color, int marge, AR_MATRIX::CELL_OP op_logic ) +{ + int half_width = ( pt_segm->GetWidth() / 2 ) + marge; + + // Calculate the bounding rectangle of the segment (if H, V or Via) + int ux0 = pt_segm->GetStart().x - GetBrdCoordOrigin().x; + int uy0 = pt_segm->GetStart().y - GetBrdCoordOrigin().y; + int ux1 = pt_segm->GetEnd().x - GetBrdCoordOrigin().x; + int uy1 = pt_segm->GetEnd().y - GetBrdCoordOrigin().y; + + //printf("traceSegm %d %d %d %d\n", ux0, uy0, ux1, uy1); + + LAYER_NUM layer = pt_segm->GetLayer(); + + //if( color == VIA_IMPOSSIBLE ) + layer = UNDEFINED_LAYER; + + + switch( pt_segm->GetShape() ) + { + // The segment is here a straight line or a circle or an arc.: + case S_CIRCLE: traceCircle( ux0, uy0, ux1, uy1, half_width, layer, color, op_logic ); break; + + case S_ARC: + traceArc( ux0, uy0, ux1, uy1, pt_segm->GetAngle(), half_width, layer, color, op_logic ); + break; + + // The segment is here a line segment. + default: drawSegmentQcq( ux0, uy0, ux1, uy1, half_width, layer, color, op_logic ); break; + } +} + + +void AR_MATRIX::TraceSegmentPcb( TRACK* aTrack, int color, int marge, AR_MATRIX::CELL_OP op_logic ) +{ + int half_width = ( aTrack->GetWidth() / 2 ) + marge; + + // Test if VIA (filled circle need to be drawn) + if( aTrack->Type() == PCB_VIA_T ) + { + LSET layer_mask; + + if( aTrack->IsOnLayer( m_routeLayerBottom ) ) + layer_mask.set( m_routeLayerBottom ); + + if( aTrack->IsOnLayer( m_routeLayerTop ) ) + { + if( !layer_mask.any() ) + layer_mask = LSET( m_routeLayerTop ); + else + layer_mask.set(); + } + + if( color == VIA_IMPOSSIBLE ) + layer_mask.set(); + + if( layer_mask.any() ) + traceFilledCircle( aTrack->GetStart().x, aTrack->GetStart().y, half_width, layer_mask, + color, op_logic ); + } + else + { + // Calculate the bounding rectangle of the segment + int ux0 = aTrack->GetStart().x - GetBrdCoordOrigin().x; + int uy0 = aTrack->GetStart().y - GetBrdCoordOrigin().y; + int ux1 = aTrack->GetEnd().x - GetBrdCoordOrigin().x; + int uy1 = aTrack->GetEnd().y - GetBrdCoordOrigin().y; + + // Ordinary track + PCB_LAYER_ID layer = aTrack->GetLayer(); + + if( color == VIA_IMPOSSIBLE ) + layer = UNDEFINED_LAYER; + + drawSegmentQcq( ux0, uy0, ux1, uy1, half_width, layer, color, op_logic ); + } +} + + +/** + * 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 depends on the distance to the first rectangle + * Therefore the cost is high in rect x0,y0 to x1,y1, and decrease outside this rectangle + */ +void AR_MATRIX::CreateKeepOutRectangle( + int ux0, int uy0, int ux1, int uy1, int marge, int aKeepOut, LSET 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[m_routeLayerBottom] ) + trace = 1; // Trace on bottom layer. + + if( aLayerMask[m_routeLayerTop] && m_RoutingLayersCount ) + trace |= 2; // Trace on top layer. + + if( trace == 0 ) + return; + + ux0 -= m_BrdBox.GetX(); + uy0 -= m_BrdBox.GetY(); + ux1 -= m_BrdBox.GetX(); + uy1 -= m_BrdBox.GetY(); + + ux0 -= marge; + ux1 += marge; + uy0 -= marge; + uy1 += marge; + + pmarge = marge / m_GridRouting; + + if( pmarge < 1 ) + pmarge = 1; + + // Calculate the coordinate limits of the rectangle. + row_max = uy1 / m_GridRouting; + col_max = ux1 / m_GridRouting; + row_min = uy0 / m_GridRouting; + + if( uy0 > row_min * m_GridRouting ) + row_min++; + + col_min = ux0 / m_GridRouting; + + if( ux0 > col_min * m_GridRouting ) + col_min++; + + if( row_min < 0 ) + row_min = 0; + + if( row_max >= ( m_Nrows - 1 ) ) + row_max = m_Nrows - 1; + + if( col_min < 0 ) + col_min = 0; + + if( col_max >= ( m_Ncols - 1 ) ) + col_max = 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++ ) + { + // RoutingMatrix Dist map containt the "cost" of the cell + // at position (row, col) + // in autoplace this is the cost of the cell, when + // a footprint overlaps it, near a "master" footprint + // this cost is hight near the "master" footprint + // and decrease with the distance + 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 = GetDist( row, col, AR_SIDE_BOTTOM ) + LocalKeepOut; + SetDist( row, col, AR_SIDE_BOTTOM, data ); + } + + if( trace & 2 ) + { + data = GetDist( row, col, AR_SIDE_TOP ); + data = std::max( data, LocalKeepOut ); + SetDist( row, col, AR_SIDE_TOP, data ); + } + } + } +} + + +void AR_MATRIX::PlacePad( D_PAD* aPad, int color, int marge, AR_MATRIX::CELL_OP op_logic ) +{ + int dx, dy; + wxPoint shape_pos = aPad->ShapePos(); + + dx = aPad->GetSize().x / 2; + dx += marge; + + if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) + { + traceFilledCircle( shape_pos.x, shape_pos.y, dx, aPad->GetLayerSet(), color, op_logic ); + return; + } + + dy = aPad->GetSize().y / 2; + dy += marge; + + if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ) + { + dx += abs( aPad->GetDelta().y ) / 2; + dy += abs( aPad->GetDelta().x ) / 2; + } + + // The pad is a rectangle ( horizontal or vertical ) + if( int( aPad->GetOrientation() ) % 900 == 0 ) + { + // Orientation turned 90 deg. + if( aPad->GetOrientation() == 900 || aPad->GetOrientation() == 2700 ) + { + std::swap( dx, dy ); + } + + TraceFilledRectangle( shape_pos.x - dx, shape_pos.y - dy, shape_pos.x + dx, + shape_pos.y + dy, aPad->GetLayerSet(), color, op_logic ); + } + else + { + TraceFilledRectangle( shape_pos.x - dx, shape_pos.y - dy, shape_pos.x + dx, + shape_pos.y + dy, aPad->GetOrientation(), aPad->GetLayerSet(), color, op_logic ); + } +} diff --git a/pcbnew/autorouter/ar_matrix.h b/pcbnew/autorouter/ar_matrix.h new file mode 100644 index 0000000000..e673e3a05a --- /dev/null +++ b/pcbnew/autorouter/ar_matrix.h @@ -0,0 +1,171 @@ +/* + * 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-2015 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 + */ + + +#ifndef __AR_MATRIX_H +#define __AR_MATRIX_H + +#include +#include + +class DRAWSEGMENT; +class TRACK; +class D_PAD; +class MODULE; + +#define AR_MAX_ROUTING_LAYERS_COUNT 2 + +#define AR_SIDE_TOP 0 +#define AR_SIDE_BOTTOM 1 + +/** + * class AR_MATRIX + * handle the matrix routing that describes the actual board + */ +class AR_MATRIX +{ +public: + typedef unsigned char MATRIX_CELL; + typedef int DIST_CELL; + typedef char DIR_CELL; + + MATRIX_CELL* m_BoardSide[AR_MAX_ROUTING_LAYERS_COUNT]; // the image map of 2 board sides + DIST_CELL* m_DistSide[AR_MAX_ROUTING_LAYERS_COUNT]; // the image map of 2 board sides: + // distance to cells + DIR_CELL* m_DirSide[AR_MAX_ROUTING_LAYERS_COUNT]; // the image map of 2 board sides: + // pointers back to source + bool m_InitMatrixDone; + int m_RoutingLayersCount; // Number of layers for autorouting (0 or 1) + int m_GridRouting; // Size of grid for autoplace/autoroute + EDA_RECT m_BrdBox; // Actual board bounding box + int m_Nrows, m_Ncols; // Matrix size + int m_MemSize; // Memory requirement, just for statistics + int m_RouteCount; // Number of routes + + PCB_LAYER_ID m_routeLayerTop; + PCB_LAYER_ID m_routeLayerBottom; + +private: + // a pointer to the current selected cell operation + void ( AR_MATRIX::*m_opWriteCell )( int aRow, int aCol, int aSide, MATRIX_CELL aCell ); + +public: + enum CELL_OP + { + WRITE_CELL = 0, + WRITE_OR_CELL = 1, + WRITE_XOR_CELL = 2, + WRITE_AND_CELL = 3, + WRITE_ADD_CELL = 4 + }; + + AR_MATRIX(); + ~AR_MATRIX(); + + void WriteCell( int aRow, int aCol, int aSide, MATRIX_CELL aCell ) + { + ( *this.*m_opWriteCell )( aRow, aCol, aSide, aCell ); + } + + /** + * function GetBrdCoordOrigin + * @return the board coordinate corresponding to the + * routing matrix origin ( board coordinate offset ) + */ + wxPoint GetBrdCoordOrigin() + { + return m_BrdBox.GetOrigin(); + } + + /** + * Function ComputeMatrixSize + * calculates the number of rows and columns of dimensions of \a aPcb for routing and + * automatic calculation of area. + * @param aPcb = the physical board + * @param aUseBoardEdgesOnly = true to use board edges only, + * = false to use the full board bounding box (default) + */ + bool ComputeMatrixSize( const EDA_RECT& aBoundingBox ); + + /** + * Function InitBoard + * initializes the data structures. + * + * @return the amount of memory used or -1 if default. + */ + int InitRoutingMatrix(); + + void UnInitRoutingMatrix(); + + // Initialize WriteCell to make the aLogicOp + void SetCellOperation( CELL_OP aLogicOp ); + + // functions to read/write one cell ( point on grid routing matrix: + MATRIX_CELL GetCell( int aRow, int aCol, int aSide ); + void SetCell( int aRow, int aCol, int aSide, MATRIX_CELL aCell ); + void OrCell( int aRow, int aCol, int aSide, MATRIX_CELL aCell ); + void XorCell( int aRow, int aCol, int aSide, MATRIX_CELL aCell ); + void AndCell( int aRow, int aCol, int aSide, MATRIX_CELL aCell ); + void AddCell( int aRow, int aCol, int aSide, MATRIX_CELL aCell ); + DIST_CELL GetDist( int aRow, int aCol, int aSide ); + void SetDist( int aRow, int aCol, int aSide, DIST_CELL ); + int GetDir( int aRow, int aCol, int aSide ); + void SetDir( int aRow, int aCol, int aSide, int aDir ); + + // calculate distance (with penalty) of a trace through a cell + int CalcDist( int x, int y, int z, int side ); + + // calculate approximate distance (manhattan distance) + int GetApxDist( int r1, int c1, int r2, int c2 ); + + + void TraceSegmentPcb( DRAWSEGMENT* pt_segm, int color, int marge, AR_MATRIX::CELL_OP op_logic ); + void TraceSegmentPcb( TRACK* aTrack, int color, int marge, AR_MATRIX::CELL_OP op_logic ); + void CreateKeepOutRectangle( + int ux0, int uy0, int ux1, int uy1, int marge, int aKeepOut, LSET aLayerMask ); + void PlacePad( D_PAD* aPad, int color, int marge, AR_MATRIX::CELL_OP op_logic ); + void TraceFilledRectangle( int ux0, int uy0, int ux1, int uy1, double angle, LSET aLayerMask, + int color, AR_MATRIX::CELL_OP op_logic ); + void TraceFilledRectangle( int ux0, int uy0, int ux1, int uy1, LSET aLayerMask, int color, + AR_MATRIX::CELL_OP op_logic ); + +private: + + void drawSegmentQcq( int ux0, int uy0, int ux1, int uy1, int lg, LAYER_NUM layer, int color, + CELL_OP op_logic ); + + void traceCircle( int ux0, int uy0, int ux1, int uy1, int lg, LAYER_NUM layer, int color, + AR_MATRIX::CELL_OP op_logic ); + void traceFilledCircle( + int cx, int cy, int radius, LSET aLayerMask, int color, AR_MATRIX::CELL_OP op_logic ); + void traceArc( int ux0, int uy0, int ux1, int uy1, double ArcAngle, int lg, LAYER_NUM layer, + int color, AR_MATRIX::CELL_OP op_logic ); + void tracePcbLine( int x0, int y0, int x1, int y1, LAYER_NUM layer, int color, + AR_MATRIX::CELL_OP op_logic ); +}; + +#endif diff --git a/pcbnew/autorouter/autoplacer_tool.cpp b/pcbnew/autorouter/autoplacer_tool.cpp new file mode 100644 index 0000000000..07c700abca --- /dev/null +++ b/pcbnew/autorouter/autoplacer_tool.cpp @@ -0,0 +1,124 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 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 + +// For frame ToolID values +#include + +// For action icons +#include + +#include +#include +#include +#include +#include + +#include + +#include "ar_autoplacer.h" +#include "autoplacer_tool.h" + + +TOOL_ACTION PCB_ACTIONS::autoplaceSelectedComponents( "pcbnew.Autoplacer.autoplaceSelected", + AS_GLOBAL, 0, _( "Auto-place selected components" ), + _( "Performs automatic placement of selected components" ) ); + +TOOL_ACTION PCB_ACTIONS::autoplaceOffboardComponents( "pcbnew.Autoplacer.autoplaceOffboard", + AS_GLOBAL, 0, _( "Auto-place off-board components" ), + _( "Performs automatic placement of components outside board area" ) ); + +AUTOPLACE_TOOL::AUTOPLACE_TOOL() : PCB_TOOL( "pcbnew.Autoplacer" ) +{ +} + + +AUTOPLACE_TOOL::~AUTOPLACE_TOOL() +{ +} + + +int AUTOPLACE_TOOL::autoplace( std::vector& aModules, bool aPlaceOffboard ) +{ + auto overlay = view()->MakeOverlay(); + + Activate(); + + AR_AUTOPLACER autoplacer( board() ); + + BOARD_COMMIT commit( frame() ); + + autoplacer.SetOverlay( overlay ); + + std::unique_ptr progressReporter( + new WX_PROGRESS_REPORTER( frame(), _( "Autoplace Components" ), 1 ) ); + + autoplacer.SetProgressReporter( progressReporter.get() ); + auto result = autoplacer.AutoplaceModules( aModules, &commit, aPlaceOffboard ); + + if( result == AR_COMPLETED ) + commit.Push( _( "Autoplace components" ) ); + else + commit.Revert(); + + return 0; +} + + +int AUTOPLACE_TOOL::autoplaceSelected( const TOOL_EVENT& aEvent ) +{ + std::vector mods; + + for( auto item : selection() ) + { + if( item->Type() == PCB_MODULE_T ) + mods.push_back( static_cast( item ) ); + } + + return autoplace( mods, false ); +} + + +int AUTOPLACE_TOOL::autoplaceOffboard( const TOOL_EVENT& aEvent ) +{ + std::vector mods; + + return autoplace( mods, true ); +} + + +void AUTOPLACE_TOOL::setTransitions() +{ + Go( &AUTOPLACE_TOOL::autoplaceSelected, PCB_ACTIONS::autoplaceSelectedComponents.MakeEvent() ); + Go( &AUTOPLACE_TOOL::autoplaceOffboard, PCB_ACTIONS::autoplaceOffboardComponents.MakeEvent() ); +} diff --git a/pcbnew/autorouter/autoplacer_tool.h b/pcbnew/autorouter/autoplacer_tool.h new file mode 100644 index 0000000000..1e077a875d --- /dev/null +++ b/pcbnew/autorouter/autoplacer_tool.h @@ -0,0 +1,52 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 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 + */ + +#ifndef TOOLS_AUTOPLACE_TOOL_H +#define TOOLS_AUTOPLACE_TOOL_H + +#include + + +/** + * Class AUTOPLACE_TOOL + * + * Tool responsible for automagic placement of components. + */ +class AUTOPLACE_TOOL : public PCB_TOOL +{ +public: + AUTOPLACE_TOOL(); + ~AUTOPLACE_TOOL(); + + ///> Bind handlers to corresponding TOOL_ACTIONs + void setTransitions() override; + +private: + int autoplace( std::vector& aModules, bool aPlaceOffboard ); + + int autoplaceSelected( const TOOL_EVENT& aEvent ); + int autoplaceOffboard( const TOOL_EVENT& aEvent ); +}; + + +#endif // TOOLS_AUTOPLACE_TOOL_H diff --git a/pcbnew/connectivity_data.cpp b/pcbnew/connectivity_data.cpp index 8d0a13dbf8..dbcc8d58d4 100644 --- a/pcbnew/connectivity_data.cpp +++ b/pcbnew/connectivity_data.cpp @@ -239,12 +239,12 @@ int CONNECTIVITY_DATA::countRelevantItems( const std::vector& aItem case PCB_VIA_T: n++; break; - + default: break; } } - + return n; } @@ -261,7 +261,7 @@ void CONNECTIVITY_DATA::ComputeDynamicRatsnest( const std::vector& m_dynamicConnectivity->Build( aItems ); m_dynamicRatsnest.clear(); - + BlockRatsnestItems( aItems ); for( unsigned int nc = 1; nc < m_dynamicConnectivity->m_nets.size(); nc++ ) @@ -659,8 +659,51 @@ void CONNECTIVITY_DATA::MarkItemNetAsDirty( BOARD_ITEM *aItem ) } } + void CONNECTIVITY_DATA::SetProgressReporter( PROGRESS_REPORTER* aReporter ) { m_progressReporter = aReporter; m_connAlgo->SetProgressReporter( m_progressReporter ); } + + +const std::vector CONNECTIVITY_DATA::GetRatsnestForComponent( MODULE* aComponent, bool aSkipInternalConnections ) +{ + std::set nets; + std::set pads; + std::vector edges; + + for( auto pad : aComponent->Pads() ) + { + nets.insert( pad->GetNetCode() ); + pads.insert( pad ); + } + + for ( auto netcode : nets ) + { + auto net = GetRatsnestForNet( netcode ); + + for ( auto edge : net->GetEdges() ) + { + auto srcNode = edge.GetSourceNode(); + auto dstNode = edge.GetTargetNode(); + + auto srcParent = static_cast( srcNode->Parent() ); + auto dstParent = static_cast( dstNode->Parent() ); + + bool srcFound = ( pads.find(srcParent) != pads.end() ); + bool dstFound = ( pads.find(dstParent) != pads.end() ); + + if ( srcFound && dstFound && !aSkipInternalConnections ) + { + edges.push_back( edge ); + } + else if ( srcFound || dstFound ) + { + edges.push_back( edge ); + } + } + } + + return edges; +} diff --git a/pcbnew/connectivity_data.h b/pcbnew/connectivity_data.h index 004b096eaf..b754443d06 100644 --- a/pcbnew/connectivity_data.h +++ b/pcbnew/connectivity_data.h @@ -48,6 +48,7 @@ class RN_DATA; class RN_NET; class TRACK; class D_PAD; +class MODULE; class PROGRESS_REPORTER; struct CN_DISJOINT_NET_ENTRY @@ -234,11 +235,11 @@ public: } void MarkItemNetAsDirty( BOARD_ITEM* aItem ); - void SetProgressReporter( PROGRESS_REPORTER* aReporter ); + const std::vector GetRatsnestForComponent( MODULE* aComponent, bool aSkipInternalConnections = false ); private: - + int countRelevantItems( const std::vector& aItems ); void updateRatsnest(); void addRatsnestCluster( const std::shared_ptr& aCluster ); diff --git a/pcbnew/edit.cpp b/pcbnew/edit.cpp index 0652c1850c..f51475eaf9 100644 --- a/pcbnew/edit.cpp +++ b/pcbnew/edit.cpp @@ -156,10 +156,6 @@ void PCB_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) case ID_POPUP_PCB_MOVE_MODULE_REQUEST: case ID_POPUP_PCB_MOVE_TEXTMODULE_REQUEST: case ID_POPUP_PCB_MOVE_PCB_TARGET_REQUEST: - case ID_POPUP_PCB_AUTOPLACE_FIXE_ALL_MODULES: - case ID_POPUP_PCB_AUTOPLACE_FIXE_MODULE: - case ID_POPUP_PCB_AUTOPLACE_FREE_ALL_MODULES: - case ID_POPUP_PCB_AUTOPLACE_FREE_MODULE: break; case ID_POPUP_CANCEL_CURRENT_COMMAND: @@ -1257,44 +1253,7 @@ void PCB_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) m_canvas->Refresh(); break; - case ID_POPUP_PCB_SPREAD_ALL_MODULES: - if( !IsOK( this, - _("Not locked footprints inside the board will be moved. OK?") ) ) - break; - // Fall through - case ID_POPUP_PCB_SPREAD_NEW_MODULES: - if( GetBoard()->m_Modules == NULL ) - { - DisplayError( this, _( "No footprint found!" ) ); - return; - } - else - { - MODULE* footprint = GetBoard()->m_Modules; - std::vector footprintList; - for( ; footprint != NULL; footprint = footprint->Next() ) - footprintList.push_back( footprint ); - - SpreadFootprints( &footprintList, id == ID_POPUP_PCB_SPREAD_NEW_MODULES, - true, GetCrossHairPosition() ); - } - break; - case ID_POPUP_PCB_AUTOPLACE_FIXE_MODULE: - LockModule( (MODULE*) GetScreen()->GetCurItem(), true ); - break; - - case ID_POPUP_PCB_AUTOPLACE_FREE_MODULE: - LockModule( (MODULE*) GetScreen()->GetCurItem(), false ); - break; - - case ID_POPUP_PCB_AUTOPLACE_FREE_ALL_MODULES: - LockModule( NULL, false ); - break; - - case ID_POPUP_PCB_AUTOPLACE_FIXE_ALL_MODULES: - LockModule( NULL, true ); - break; - + default: wxString msg; msg.Printf( wxT( "PCB_EDIT_FRAME::Process_Special_Functions() unknown event id %d" ), id ); diff --git a/pcbnew/menubar_pcb_editor.cpp b/pcbnew/menubar_pcb_editor.cpp index c7778b3f1b..fd5201bc8e 100644 --- a/pcbnew/menubar_pcb_editor.cpp +++ b/pcbnew/menubar_pcb_editor.cpp @@ -337,6 +337,22 @@ void preparePlaceMenu( wxMenu* aParentMenu ) _( "&Grid Origin" ), _( "Set grid origin point" ), KiBitmap( grid_select_axis_xpm ) ); + + aParentMenu->AppendSeparator(); + + wxMenu* autoplaceSubmenu = new wxMenu; + AddMenuItem( autoplaceSubmenu, ID_POPUP_PCB_AUTOPLACE_OFF_BOARD_MODULES, + _( "&Autoplace off-board components" ), _( "" ), KiBitmap( grid_select_axis_xpm ) // fixme: icons + ); + + AddMenuItem( autoplaceSubmenu, ID_POPUP_PCB_AUTOPLACE_SELECTED_MODULES, + _( "&Autoplace selected components" ), _( "" ), KiBitmap( grid_select_axis_xpm ) // fixme: icons + ); + + AddMenuItem( aParentMenu, autoplaceSubmenu, + -1, _( "&Auto-place" ), + _( "Automatic component placement" ), KiBitmap( grid_select_axis_xpm ) // fixme: icons + ); } diff --git a/pcbnew/onrightclick.cpp b/pcbnew/onrightclick.cpp index fa1b2ce65f..a01d926731 100644 --- a/pcbnew/onrightclick.cpp +++ b/pcbnew/onrightclick.cpp @@ -394,17 +394,7 @@ bool PCB_EDIT_FRAME::OnRightClick( const wxPoint& aMousePos, wxMenu* aPopMenu ) case ID_NO_TOOL_SELECTED: { wxMenu* commands = new wxMenu; - AddMenuItem( aPopMenu, commands, ID_POPUP_PCB_AUTOPLACE_COMMANDS, - _( "Global Spread and Place" ), KiBitmap( move_xpm ) ); - AddMenuItem( commands, ID_POPUP_PCB_SPREAD_ALL_MODULES, - _( "Spread out All Footprints" ), KiBitmap( move_xpm ) ); - commands->Append( ID_POPUP_PCB_SPREAD_NEW_MODULES, - _( "Spread out Footprints not Already on Board" ) ); - AddMenuItem( commands, ID_POPUP_PCB_AUTOPLACE_FREE_ALL_MODULES, - _( "Unlock All Footprints" ), KiBitmap( unlocked_xpm ) ); - AddMenuItem( commands, ID_POPUP_PCB_AUTOPLACE_FIXE_ALL_MODULES, - _( "Lock All Footprints" ), KiBitmap( locked_xpm ) ); - + if( !trackFound ) { msg = AddHotkeyName( _( "Begin Track" ), g_Board_Editor_Hotkeys_Descr, HK_ADD_NEW_TRACK ); @@ -808,17 +798,9 @@ void PCB_EDIT_FRAME::createPopUpMenuForFootprints( MODULE* aModule, wxMenu* menu if( !aModule->IsLocked() ) { - msg = AddHotkeyName( _("Lock Footprint" ), g_Board_Editor_Hotkeys_Descr, - HK_LOCK_UNLOCK_FOOTPRINT ); - AddMenuItem( sub_menu_footprint, ID_POPUP_PCB_AUTOPLACE_FIXE_MODULE, msg, - KiBitmap( locked_xpm ) ); } else { - msg = AddHotkeyName( _( "Unlock Footprint" ), g_Board_Editor_Hotkeys_Descr, - HK_LOCK_UNLOCK_FOOTPRINT ); - AddMenuItem( sub_menu_footprint, ID_POPUP_PCB_AUTOPLACE_FREE_MODULE, msg, - KiBitmap( unlocked_xpm ) ); } } diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index 9149d4bde4..e8bbb886d2 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -256,12 +256,6 @@ BEGIN_EVENT_TABLE( PCB_EDIT_FRAME, PCB_BASE_FRAME ) EVT_MENU( ID_POPUP_PCB_DELETE_TRACKSEG, PCB_EDIT_FRAME::Process_Special_Functions ) EVT_MENU_RANGE( ID_POPUP_GENERAL_START_RANGE, ID_POPUP_GENERAL_END_RANGE, PCB_EDIT_FRAME::Process_Special_Functions ) - EVT_MENU( ID_POPUP_PCB_SPREAD_ALL_MODULES, PCB_EDIT_FRAME::Process_Special_Functions ) - EVT_MENU( ID_POPUP_PCB_SPREAD_NEW_MODULES, PCB_EDIT_FRAME::Process_Special_Functions ) - EVT_MENU( ID_POPUP_PCB_AUTOPLACE_FIXE_MODULE, PCB_EDIT_FRAME::Process_Special_Functions ) - EVT_MENU( ID_POPUP_PCB_AUTOPLACE_FIXE_ALL_MODULES, PCB_EDIT_FRAME::Process_Special_Functions ) - EVT_MENU( ID_POPUP_PCB_AUTOPLACE_FREE_ALL_MODULES, PCB_EDIT_FRAME::Process_Special_Functions ) - EVT_MENU( ID_POPUP_PCB_AUTOPLACE_FREE_MODULE, PCB_EDIT_FRAME::Process_Special_Functions ) // User interface update event handlers. EVT_UPDATE_UI( ID_SAVE_BOARD, PCB_EDIT_FRAME::OnUpdateSave ) diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h index b07638337a..abd5008070 100644 --- a/pcbnew/pcbnew_id.h +++ b/pcbnew/pcbnew_id.h @@ -250,28 +250,11 @@ enum pcbnew_ids // reserve a block of MAX_ITEMS_IN_PICKER ids for the item selection popup ID_POPUP_PCB_ITEM_SELECTION_START, ID_POPUP_PCB_ITEM_SELECTION_END = MAX_ITEMS_IN_PICKER + ID_POPUP_PCB_ITEM_SELECTION_START, - ID_POPUP_PCB_AUTOPLACE_START_RANGE, - ID_POPUP_PCB_AUTOPLACE_FIXE_MODULE, - ID_POPUP_PCB_AUTOPLACE_FREE_MODULE, - ID_POPUP_PCB_AUTOPLACE_FREE_ALL_MODULES, - ID_POPUP_PCB_AUTOPLACE_FIXE_ALL_MODULES, - ID_POPUP_PCB_AUTOPLACE_CURRENT_MODULE, - ID_POPUP_PCB_SPREAD_ALL_MODULES, - ID_POPUP_PCB_SPREAD_NEW_MODULES, - ID_POPUP_PCB_AUTOPLACE_COMMANDS, - ID_POPUP_PCB_AUTOPLACE_ALL_MODULES, - ID_POPUP_PCB_AUTOPLACE_NEW_MODULES, - ID_POPUP_PCB_AUTOPLACE_NEXT_MODULE, - - ID_POPUP_PCB_AUTOROUTE_COMMANDS, - ID_POPUP_PCB_AUTOROUTE_ALL_MODULES, - ID_POPUP_PCB_AUTOROUTE_MODULE, - ID_POPUP_PCB_AUTOROUTE_PAD, - ID_POPUP_PCB_AUTOROUTE_NET, - - ID_POPUP_PCB_AUTOROUTE_RESET_UNROUTED, - ID_POPUP_PCB_AUTOROUTE_SELECT_LAYERS, - ID_POPUP_PCB_AUTOPLACE_END_RANGE, + + ID_POPUP_PCB_SPREAD_SELECTED_MODULES, + ID_POPUP_PCB_SPREAD_OFF_BOARD_MODULES, + ID_POPUP_PCB_AUTOPLACE_SELECTED_MODULES, + ID_POPUP_PCB_AUTOPLACE_OFF_BOARD_MODULES, ID_MENU_READ_BOARD_BACKUP_FILE, ID_MENU_RECOVER_BOARD_AUTOSAVE, diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp index cded71f543..22ee6225ee 100644 --- a/pcbnew/tools/pcb_actions.cpp +++ b/pcbnew/tools/pcb_actions.cpp @@ -212,6 +212,13 @@ OPT PCB_ACTIONS::TranslateLegacyId( int aId ) case ID_POPUP_PCB_REMOVE_FILLED_AREAS_IN_ALL_ZONES: return PCB_ACTIONS::zoneUnfillAll.MakeEvent(); + + case ID_POPUP_PCB_AUTOPLACE_OFF_BOARD_MODULES: + return PCB_ACTIONS::autoplaceOffboardComponents.MakeEvent(); + + case ID_POPUP_PCB_AUTOPLACE_SELECTED_MODULES: + return PCB_ACTIONS::autoplaceSelectedComponents.MakeEvent(); + } return OPT(); diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h index 987adbfdc0..e33481043e 100644 --- a/pcbnew/tools/pcb_actions.h +++ b/pcbnew/tools/pcb_actions.h @@ -403,6 +403,8 @@ public: static TOOL_ACTION editFootprintInFpEditor; + static TOOL_ACTION autoplaceOffboardComponents; + static TOOL_ACTION autoplaceSelectedComponents; ///> @copydoc COMMON_ACTIONS::TranslateLegacyId() virtual OPT TranslateLegacyId( int aId ) override; diff --git a/pcbnew/tools/tools_common.cpp b/pcbnew/tools/tools_common.cpp index 76d2d57047..4bc4cfa380 100644 --- a/pcbnew/tools/tools_common.cpp +++ b/pcbnew/tools/tools_common.cpp @@ -45,6 +45,7 @@ #include #include +#include void PCB_ACTIONS::RegisterAllTools( TOOL_MANAGER* aToolManager ) { @@ -64,4 +65,5 @@ void PCB_ACTIONS::RegisterAllTools( TOOL_MANAGER* aToolManager ) aToolManager->RegisterTool( new MICROWAVE_TOOL ); aToolManager->RegisterTool( new POSITION_RELATIVE_TOOL ); aToolManager->RegisterTool( new ZONE_FILLER_TOOL ); + aToolManager->RegisterTool( new AUTOPLACE_TOOL ); }