Pcbnew: Autoplace functions: renamed spread footprint functions. Rewritten.

Now footprints, after loaded by reading a netlist are grouped by sheets by the footprints spread function, and the grouping is better.
Rename 2 files. Fix minor issues. Clean code
This commit is contained in:
jean-pierre charras 2013-11-27 14:20:42 +01:00
parent 251f0c7f9b
commit f068c0d94f
14 changed files with 825 additions and 171 deletions

View File

@ -228,8 +228,6 @@ void EDA_3D_CANVAS::SetView3D( int keycode )
void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
{
wxSize size( GetClientSize() );
if( event.ShiftDown() )
{
if( event.GetWheelRotation() < 0 )

View File

@ -906,11 +906,22 @@ void EDA_DRAW_PANEL::OnMouseWheel( wxMouseEvent& event )
bool offCenterReq = event.ControlDown() && event.ShiftDown();
offCenterReq = offCenterReq || m_enableZoomNoCenter;
#if wxMAJOR_VERSION >= 2 && wxMINOR_VERSION >= 9
int axis = event.GetWheelAxis();
#else
const int axis = 0;
#endif
// This is a zoom in or out command
if( event.GetWheelRotation() > 0 )
{
if( event.ShiftDown() && !event.ControlDown() )
cmd.SetId( ID_PAN_UP );
{
if( axis == 0 )
cmd.SetId( ID_PAN_UP );
else
cmd.SetId( ID_PAN_RIGHT );
}
else if( event.ControlDown() && !event.ShiftDown() )
cmd.SetId( ID_PAN_LEFT );
else if( offCenterReq )
@ -921,7 +932,12 @@ void EDA_DRAW_PANEL::OnMouseWheel( wxMouseEvent& event )
else if( event.GetWheelRotation() < 0 )
{
if( event.ShiftDown() && !event.ControlDown() )
cmd.SetId( ID_PAN_DOWN );
{
if( axis == 0 )
cmd.SetId( ID_PAN_DOWN );
else
cmd.SetId( ID_PAN_LEFT );
}
else if( event.ControlDown() && !event.ShiftDown() )
cmd.SetId( ID_PAN_RIGHT );
else if( offCenterReq )

View File

@ -1494,7 +1494,7 @@ public:
// Autoplacement:
void AutoPlace( wxCommandEvent& event );
void OnPlaceOrRouteFootprints( wxCommandEvent& event );
/**
* Function ScriptingConsoleEnableDisable
@ -1520,7 +1520,17 @@ public:
*/
bool ReOrientModules( const wxString& ModuleMask, double Orient, bool include_fixe );
void LockModule( MODULE* aModule, bool aLocked );
void AutoMoveModulesOnPcb( bool PlaceModulesHorsPcb );
/**
* Function SpreadFootprints
* Footprints (after loaded by reading a netlist for instance) are moved
* to be in a small free area (outside the current board) without overlapping.
* @param aFootprintsOutsideBoardOnly: true to move only
* footprints outside the board outlines
* (they are outside if the position of a footprint is outside
* the board outlines bounding box
*/
void SpreadFootprints( bool aFootprintsOutsideBoardOnly );
/**
* Function AutoPlaceModule

View File

@ -124,12 +124,14 @@ set( PCBNEW_IMPORT_DXF
)
set( PCBNEW_AUTOROUTER_SRCS
autorouter/automove.cpp
autorouter/autoplac.cpp
autorouter/rect_placement/rect_placement.cpp
autorouter/move_and_route_event_functions.cpp
autorouter/auto_place_footprints.cpp
autorouter/autorout.cpp
autorouter/routing_matrix.cpp
autorouter/dist.cpp
autorouter/queue.cpp
autorouter/spread_footprints.cpp
autorouter/solve.cpp
autorouter/graphpcb.cpp
autorouter/work.cpp

View File

@ -1085,7 +1085,7 @@ static bool Tri_PlaceModules( MODULE* ref, MODULE* compare )
}
static bool Tri_RatsModules( MODULE* ref, MODULE* compare )
static bool sortFootprintsByRatsnestSize( MODULE* ref, MODULE* compare )
{
double ff1, ff2;
@ -1141,7 +1141,7 @@ static MODULE* PickModule( PCB_EDIT_FRAME* pcbframe, wxDC* DC )
pcbframe->GetBoard()->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK;
sort( moduleList.begin(), moduleList.end(), Tri_RatsModules );
sort( moduleList.begin(), moduleList.end(), sortFootprintsByRatsnestSize );
// Search for "best" module.
MODULE* bestModule = NULL;

View File

@ -55,15 +55,13 @@ typedef enum {
} SelectFixeFct;
static bool sortModulesbySize( MODULE* ref, MODULE* compare );
wxString ModulesMaskSelection = wxT( "*" );
/* Called on events (popup menus) relative to automove and autoplace footprints
*/
void PCB_EDIT_FRAME::AutoPlace( wxCommandEvent& event )
void PCB_EDIT_FRAME::OnPlaceOrRouteFootprints( wxCommandEvent& event )
{
int id = event.GetId();
@ -130,12 +128,19 @@ void PCB_EDIT_FRAME::AutoPlace( wxCommandEvent& event )
AutoPlaceModule( NULL, PLACE_INCREMENTAL, &dc );
break;
case ID_POPUP_PCB_AUTOMOVE_ALL_MODULES:
AutoMoveModulesOnPcb( false );
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 modules found!" ) );
return;
}
case ID_POPUP_PCB_AUTOMOVE_NEW_MODULES:
AutoMoveModulesOnPcb( true );
SpreadFootprints( id == ID_POPUP_PCB_SPREAD_NEW_MODULES );
break;
case ID_POPUP_PCB_AUTOROUTE_ALL_MODULES:
@ -159,7 +164,7 @@ void PCB_EDIT_FRAME::AutoPlace( wxCommandEvent& event )
break;
default:
wxMessageBox( wxT( "AutoPlace command error" ) );
wxMessageBox( wxT( "OnPlaceOrRouteFootprints command error" ) );
break;
}
@ -168,135 +173,6 @@ void PCB_EDIT_FRAME::AutoPlace( wxCommandEvent& event )
}
/* Function to move components in a rectangular area format 4 / 3,
* starting from the mouse cursor
* The components with the FIXED status set are not moved
*/
void PCB_EDIT_FRAME::AutoMoveModulesOnPcb( bool PlaceModulesHorsPcb )
{
std::vector <MODULE*> moduleList;
wxPoint start, current;
int Ymax_size, Xsize_allowed;
int pas_grille = (int) GetScreen()->GetGridSize().x;
double surface;
// Undo: init list
PICKED_ITEMS_LIST newList;
newList.m_Status = UR_CHANGED;
ITEM_PICKER picker( NULL, UR_CHANGED );
if( GetBoard()->m_Modules == NULL )
{
DisplayError( this, _( "No modules found!" ) );
return;
}
// Confirmation
if( !IsOK( this, _( "Move modules?" ) ) )
return;
EDA_RECT bbbox = GetBoard()->ComputeBoundingBox( true );
bool edgesExist = ( bbbox.GetWidth() || bbbox.GetHeight() );
// no edges exist
if( PlaceModulesHorsPcb && !edgesExist )
{
DisplayError( this,
_( "Could not automatically place modules. No board outlines detected." ) );
return;
}
// Build sorted footprints list (sort by decreasing size )
MODULE* Module = GetBoard()->m_Modules;
for( ; Module != NULL; Module = Module->Next() )
{
Module->CalculateBoundingBox();
moduleList.push_back(Module);
}
sort( moduleList.begin(), moduleList.end(), sortModulesbySize );
/* to move modules outside the board, the cursor is placed below
* the current board, to avoid placing components in board area.
*/
if( PlaceModulesHorsPcb && edgesExist )
{
if( GetCrossHairPosition().y < (bbbox.GetBottom() + 2000) )
{
wxPoint pos = GetCrossHairPosition();
pos.y = bbbox.GetBottom() + 2000;
SetCrossHairPosition( pos );
}
}
// calculate the area needed by footprints
surface = 0.0;
for( unsigned ii = 0; ii < moduleList.size(); ii++ )
{
Module = moduleList[ii];
if( PlaceModulesHorsPcb && edgesExist )
{
if( bbbox.Contains( Module->GetPosition() ) )
continue;
}
surface += Module->GetArea();
}
Xsize_allowed = (int) ( sqrt( surface ) * 4.0 / 3.0 );
start = current = GetCrossHairPosition();
Ymax_size = 0;
for( unsigned ii = 0; ii < moduleList.size(); ii++ )
{
Module = moduleList[ii];
if( Module->IsLocked() )
continue;
if( PlaceModulesHorsPcb && edgesExist )
{
if( bbbox.Contains( Module->GetPosition() ) )
continue;
}
// Undo: add copy of old Module to undo
picker.SetItem( Module );
picker.SetLink( Module->Clone() );
if( current.x > (Xsize_allowed + start.x) )
{
current.x = start.x;
current.y += Ymax_size + pas_grille;
Ymax_size = 0;
}
SetCrossHairPosition( current + Module->GetPosition() -
Module->GetBoundingBox().GetPosition() );
Ymax_size = std::max( Ymax_size, Module->GetBoundingBox().GetHeight() );
PlaceModule( Module, NULL, true );
// Undo: add new Module to undo
newList.PushItem( picker );
current.x += Module->GetBoundingBox().GetWidth() + pas_grille;
}
// Undo: commit
if( newList.GetCount() )
SaveCopyInUndoList( newList, UR_CHANGED );
m_canvas->Refresh();
}
/* Set or reset (true or false) Lock attribute of aModule or all modules if aModule == NULL
*/
void PCB_EDIT_FRAME::LockModule( MODULE* aModule, bool aLocked )
@ -322,8 +198,3 @@ void PCB_EDIT_FRAME::LockModule( MODULE* aModule, bool aLocked )
}
}
static bool sortModulesbySize( MODULE* ref, MODULE* compare )
{
return compare->GetArea() < ref->GetArea();
}

View File

@ -0,0 +1,38 @@
A class that fits subrectangles into a power-of-2 rectangle
(C) Copyright 2000-2002 by Javier Arevalo
This code is free to use and modify for all purposes
You have a bunch of rectangular pieces. You need to arrange them in a
rectangular surface so that they don't overlap, keeping the total area of the
rectangle as small as possible. This is fairly common when arranging characters
in a bitmapped font, lightmaps for a 3D engine, and I guess other situations as
well.
The idea of this algorithm is that, as we add rectangles, we can pre-select
"interesting" places where we can try to add the next rectangles. For optimal
results, the rectangles should be added in order. I initially tried using area
as a sorting criteria, but it didn't work well with very tall or very flat
rectangles. I then tried using the longest dimension as a selector, and it
worked much better. So much for intuition...
These "interesting" places are just to the right and just below the currently
added rectangle. The first rectangle, obviously, goes at the top left, the next
one would go either to the right or below this one, and so on. It is a weird way
to do it, but it seems to work very nicely.
The way we search here is fairly brute-force, the fact being that for most off-
line purposes the performance seems more than adequate. I have generated a
japanese font with around 8500 characters and all the time was spent generating
the bitmaps.
Also, for all we care, we could grow the parent rectangle in a different way
than power of two. It just happens that power of 2 is very convenient for
graphics hardware textures.
I'd be interested in hearing of other approaches to this problem. Make sure
to post them on http://www.flipcode.com
See also
http://www.flipcode.com/archives/Rectangle_Placement.shtml
http://kossovsky.net/index.php/2009/07/cshar-rectangle-packing

View File

@ -0,0 +1,259 @@
// ----------------------------------------------------------------------------------------
// Name : rect_placement.cpp
// Description : A class that fits subrectangles into a power-of-2 rectangle
// (C) Copyright 2000-2002 by Javier Arevalo
// This code is free to use and modify for all purposes
// ----------------------------------------------------------------------------------------
/*
* You have a bunch of rectangular pieces. You need to arrange them in a
* rectangular surface so that they don't overlap, keeping the total area of the
* rectangle as small as possible. This is fairly common when arranging characters
* in a bitmapped font, lightmaps for a 3D engine, and I guess other situations as
* well.
*
* The idea of this algorithm is that, as we add rectangles, we can pre-select
* "interesting" places where we can try to add the next rectangles. For optimal
* results, the rectangles should be added in order. I initially tried using area
* as a sorting criteria, but it didn't work well with very tall or very flat
* rectangles. I then tried using the longest dimension as a selector, and it
* worked much better. So much for intuition...
*
* These "interesting" places are just to the right and just below the currently
* added rectangle. The first rectangle, obviously, goes at the top left, the next
* one would go either to the right or below this one, and so on. It is a weird way
* to do it, but it seems to work very nicely.
*
* The way we search here is fairly brute-force, the fact being that for most off-
* line purposes the performance seems more than adequate. I have generated a
* japanese font with around 8500 characters and all the time was spent generating
* the bitmaps.
*
* Also, for all we care, we could grow the parent rectangle.
*
* I'd be interested in hearing of other approaches to this problem. Make sure
* to post them on http://www.flipcode.com
*/
#include "rect_placement.h"
// --------------------------------------------------------------------------------
// Name :
// Description :
// --------------------------------------------------------------------------------
void CRectPlacement::Init( int w, int h )
{
End();
m_size = TRect( 0, 0, w, h );
m_vPositions.push_back( TPos( 0, 0 ) );
m_area = 0;
}
// --------------------------------------------------------------------------------
// Name :
// Description :
// --------------------------------------------------------------------------------
void CRectPlacement::End()
{
m_vPositions.clear();
m_vRects.clear();
m_size.w = 0;
}
// --------------------------------------------------------------------------------
// Name : IsFree
// Description : Check if the given rectangle is partially or totally used
// --------------------------------------------------------------------------------
bool CRectPlacement::IsFree( const TRect& r ) const
{
if( !m_size.Contains( r ) )
return false;
for( CRectArray::const_iterator it = m_vRects.begin();
it != m_vRects.end(); ++it )
{
if( it->Intersects( r ) )
return false;
}
return true;
}
// --------------------------------------------------------------------------------
// Name : AddPosition
// Description : Add new anchor point
// --------------------------------------------------------------------------------
void CRectPlacement::AddPosition( const TPos& p )
{
// Try to insert anchor as close as possible to the top left corner
// So it will be tried first
bool bFound = false;
CPosArray::iterator it;
for( it = m_vPositions.begin();
!bFound && it != m_vPositions.end();
++it )
{
if( p.x + p.y < it->x + it->y )
bFound = true;
}
if( bFound )
m_vPositions.insert( it, p );
else
m_vPositions.push_back( p );
}
// --------------------------------------------------------------------------------
// Name : AddRect
// Description : Add the given rect and updates anchor points
// --------------------------------------------------------------------------------
void CRectPlacement::AddRect( const TRect& r )
{
m_vRects.push_back( r );
m_area += r.w * r.h;
// Add two new anchor points
AddPosition( TPos( r.x, r.y + r.h ) );
AddPosition( TPos( r.x + r.w, r.y ) );
}
// --------------------------------------------------------------------------------
// Name : AddAtEmptySpot
// Description : Add the given rectangle
// --------------------------------------------------------------------------------
bool CRectPlacement::AddAtEmptySpot( TRect& r )
{
// Find a valid spot among available anchors.
bool bFound = false;
CPosArray::iterator it;
for( it = m_vPositions.begin();
!bFound && it != m_vPositions.end();
++it )
{
TRect Rect( it->x, it->y, r.w, r.h );
if( IsFree( Rect ) )
{
r = Rect;
bFound = true;
break; // Don't let the loop increase the iterator.
}
}
if( bFound )
{
int x, y;
// Remove the used anchor point
m_vPositions.erase( it );
// Sometimes, anchors end up displaced from the optimal position
// due to irregular sizes of the subrects.
// So, try to adjut it up & left as much as possible.
for( x = 1; x <= r.x; x++ )
{
if( !IsFree( TRect( r.x - x, r.y, r.w, r.h ) ) )
break;
}
for( y = 1; y <= r.y; y++ )
{
if( !IsFree( TRect( r.x, r.y - y, r.w, r.h ) ) )
break;
}
if( y > x )
r.y -= y - 1;
else
r.x -= x - 1;
AddRect( r );
}
return bFound;
}
#include <stdio.h>
// --------------------------------------------------------------------------------
// Name : AddAtEmptySpotAutoGrow
// Description : Add a rectangle of the given size, growing our area if needed
// Area grows only until the max given.
// Returns the placement of the rect in the rect's x,y coords
// --------------------------------------------------------------------------------
bool CRectPlacement::AddAtEmptySpotAutoGrow( TRect* pRect, int maxW, int maxH )
{
double growing_factor = 1.2; // Must be > 1.0, and event > 1.1 for fast optimization
#define GROW(x) ((x * growing_factor) + 1)
if( pRect->w <= 0 )
return true;
int orgW = m_size.w;
int orgH = m_size.h;
// Try to add it in the existing space
while( !AddAtEmptySpot( *pRect ) )
{
int pw = m_size.w;
int ph = m_size.h;
// Sanity check - if area is complete.
if( pw >= maxW && ph >= maxH )
{
m_size.w = orgW;
m_size.h = orgH;
return false;
}
// Try growing the smallest dim
if( pw < maxW && ( pw < ph || ( (pw == ph) && (pRect->w >= pRect->h) ) ) )
m_size.w = GROW( pw );
else
m_size.h = GROW( ph );
if( AddAtEmptySpot( *pRect ) )
break;
// Try growing the other dim instead
if( pw != m_size.w )
{
m_size.w = pw;
if( ph < maxW )
m_size.h = GROW( ph );
}
else
{
m_size.h = ph;
if( pw < maxW )
m_size.w = GROW( pw );
}
if( pw != m_size.w || ph != m_size.h )
if( AddAtEmptySpot( *pRect ) )
break;
// Grow both if possible, and reloop.
m_size.w = pw;
m_size.h = ph;
if( pw < maxW )
m_size.w = GROW( pw );
if( ph < maxH )
m_size.h = GROW( ph );
}
return true;
}

View File

@ -0,0 +1,104 @@
// --------------------------------------------------------------------------------
// Name : rect_placement.h
// Description : A class that allocates subrectangles into power-of-2 rectangles
// (C) Copyright 2000-2002 by Javier Arevalo
// This code is free to use and modify for all purposes
// --------------------------------------------------------------------------------
/**
* @file rect_placement.h
*/
#ifndef _RECT_PLACEMENT_H_
#define _RECT_PLACEMENT_H_
#include <vector>
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
class CRectPlacement
{
public:
// Helper classes
struct TPos
{
int x, y;
TPos() { }
TPos( int _x, int _y ) : x( _x ), y( _y ) { }
bool operator ==( const TPos& p ) const { return x == p.x && y == p.y; }
};
struct TRect : public TPos
{
int w, h;
TRect() { }
TRect( int _x, int _y, int _w, int _h ) : TPos( _x, _y ), w( _w > 0 ? _w : 0 ), h(
_h > 0 ? _h : 0 ) { }
bool Contains( const TPos& p ) const
{
return p.x >= x && p.y >= y && p.x < (x + w) && p.y < (y + h);
}
bool Contains( const TRect& r ) const
{
return r.x >= x && r.y >= y &&
(r.x + r.w) <= (x + w) && (r.y + r.h) <= (y + h);
}
bool Intersects( const TRect& r ) const
{
return w > 0 && h > 0 && r.w > 0 && r.h > 0
&& ( (r.x + r.w) > x && r.x < (x + w) && (r.y + r.h) > y && r.y < (y + h) );
}
// static bool Greater(const TRect &a, const TRect &b)
// { return a.w*a.h > b.w*b.h; }
// Greater rect area. Not as good as the next heuristic:
// Greater size in at least one dim.
static bool Greater( const TRect& a, const TRect& b )
{
return (a.w > b.w && a.w > b.h) || (a.h > b.w && a.h > b.h);
}
};
// ---------------------
typedef std::vector<TPos> CPosArray;
typedef std::vector<TRect> CRectArray;
// ---------------------
CRectPlacement() { Init(); }
~CRectPlacement() { End(); }
void Init( int w = 1, int h = 1 );
void End();
bool IsOk() const { return m_size.w > 0; }
int GetW() const { return m_size.w; }
int GetH() const { return m_size.h; }
double GetArea() const { return m_area; }
double GetTotalArea() const { return (double)m_size.w * m_size.h; }
bool AddAtEmptySpotAutoGrow( TRect* pRect, int maxW, int maxH );
private:
TRect m_size;
CRectArray m_vRects;
CPosArray m_vPositions;
double m_area;
// ---------------------
bool IsFree( const TRect& r ) const;
void AddPosition( const TPos& p );
void AddRect( const TRect& r );
bool AddAtEmptySpot( TRect& r );
};
#endif // _RECT_PLACEMENT_H_

View File

@ -324,7 +324,6 @@ int PCB_EDIT_FRAME::Solve( wxDC* DC, int aLayersCount )
AppendMsgPanel( wxT( "Activity" ), msg, BROWN );
}
pt_cur_ch = pt_cur_ch;
segm_oX = GetBoard()->GetBoundingBox().GetX() + (RoutingMatrix.m_GridRouting * col_source);
segm_oY = GetBoard()->GetBoundingBox().GetY() + (RoutingMatrix.m_GridRouting * row_source);
segm_fX = GetBoard()->GetBoundingBox().GetX() + (RoutingMatrix.m_GridRouting * col_target);

View File

@ -0,0 +1,356 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2013 Wayne Stambaugh <stambaughw@verizon.net>
*
* Copyright (C) 1992-2013 KiCad Developers, see AUTHORS.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
*/
/**
* @file spread_footprints.cpp
* @brief functions to spread footprints on free areas outside a board.
* this is usefull after reading a netlist, when new footprints are loaded
* and stacked at 0,0 coordinate.
* Often, spread them on a free area near the board being edited make more easy
* their selection.
*/
#include <algorithm>
#include <fctsys.h>
#include <convert_to_biu.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <pcbnew.h>
#include <wxPcbStruct.h>
#include <class_board.h>
#include <class_module.h>
#include <rect_placement/rect_placement.h>
struct TSubRect : public CRectPlacement::TRect
{
int n; // Original index of this subrect, before sorting
TSubRect() { }
TSubRect( int _w, int _h, int _n ) :
TRect( 0, 0, _w, _h ), n( _n ) { }
};
typedef std::vector<TSubRect> CSubRectArray;
// Use 0.01 mm units to calculate placement, to avoid long calculation time
const int scale = (int)(0.01 * IU_PER_MM);
// Populates a list of rectangles, from a list of modules
void fillRectList( CSubRectArray& vecSubRects, std::vector <MODULE*>& aModuleList )
{
vecSubRects.clear();
for( unsigned ii = 0; ii < aModuleList.size(); ii++ )
{
EDA_RECT fpBox = aModuleList[ii]->GetBoundingBox();
TSubRect fpRect( fpBox.GetWidth()/scale, fpBox.GetHeight()/scale, ii );
vecSubRects.push_back( fpRect );
}
}
// Populates a list of rectangles, from a list of EDA_RECT
void fillRectList( CSubRectArray& vecSubRects, std::vector <EDA_RECT>& aRectList )
{
vecSubRects.clear();
for( unsigned ii = 0; ii < aRectList.size(); ii++ )
{
EDA_RECT& rect = aRectList[ii];
TSubRect fpRect( rect.GetWidth()/scale, rect.GetHeight()/scale, ii );
vecSubRects.push_back( fpRect );
}
}
// Spread a list of rectangles inside a placement area
void spreadRectangles( CRectPlacement& aPlacementArea,
CSubRectArray& vecSubRects,
int areaSizeX, int areaSizeY )
{
areaSizeX/= scale;
areaSizeY/= scale;
// Sort the subRects based on dimensions, larger dimension goes first.
std::sort( vecSubRects.begin(), vecSubRects.end(), CRectPlacement::TRect::Greater );
// gives the initial size to the area
aPlacementArea.Init( areaSizeX, areaSizeY );
// Add all subrects
CSubRectArray::iterator it;
for( it = vecSubRects.begin(); it != vecSubRects.end(); )
{
CRectPlacement::TRect r( 0, 0, it->w, it->h );
bool bPlaced = aPlacementArea.AddAtEmptySpotAutoGrow( &r, areaSizeX, areaSizeY );
if( !bPlaced ) // No room to place the rectangle: enlarge area and retry
{
areaSizeX = ceil(areaSizeX * 1.1);
areaSizeY = ceil(areaSizeY * 1.1);
aPlacementArea.Init( areaSizeX, areaSizeY );
it = vecSubRects.begin();
continue;
}
// When correctly placed in a placement area, the coords are returned in r.x and r.y
// Store them.
it->x = r.x;
it->y = r.y;
it++;
}
}
void moveFootprintsInArea( CRectPlacement& aPlacementArea,
std::vector <MODULE*>& aModuleList, EDA_RECT& aFreeArea,
bool aFindAreaOnly )
{
CSubRectArray vecSubRects;
fillRectList( vecSubRects, aModuleList );
spreadRectangles( aPlacementArea, vecSubRects,
aFreeArea.GetWidth(), aFreeArea.GetHeight() );
if( aFindAreaOnly )
return;
for( unsigned it = 0; it < vecSubRects.size(); ++it )
{
wxPoint pos( vecSubRects[it].x, vecSubRects[it].y );
pos.x *= scale;
pos.y *= scale;
MODULE * module = aModuleList[vecSubRects[it].n];
EDA_RECT fpBBox = module->GetBoundingBox();
wxPoint mod_pos = pos + ( module->GetPosition() - fpBBox.GetOrigin() )
+ aFreeArea.GetOrigin();
module->Move( mod_pos - module->GetPosition() );
}
}
static bool sortModulesbySheetPath( MODULE* ref, MODULE* compare );
/* Function to move components in a rectangular area format 4 / 3,
* starting from the mouse cursor
* The components with the FIXED status set are not moved
*/
void PCB_EDIT_FRAME::SpreadFootprints( bool aFootprintsOutsideBoardOnly )
{
EDA_RECT bbox = GetBoard()->ComputeBoundingBox( true );
bool edgesExist = ( bbox.GetWidth() || bbox.GetHeight() );
// no edges exist
if( aFootprintsOutsideBoardOnly && !edgesExist )
{
DisplayError( this,
_( "Could not automatically place modules. No board outlines detected." ) );
return;
}
// if aFootprintsOutsideBoardOnly is true, and if board outline exists,
// wue have to filter footprints to move:
bool outsideBrdFilter = aFootprintsOutsideBoardOnly && edgesExist;
// Build candidate list
// calculate also the area needed by these footprints
MODULE* Module = GetBoard()->m_Modules;
std::vector <MODULE*> moduleList;
for( ; Module != NULL; Module = Module->Next() )
{
Module->CalculateBoundingBox();
if( outsideBrdFilter )
{
if( bbox.Contains( Module->GetPosition() ) )
continue;
}
if( Module->IsLocked() )
continue;
moduleList.push_back(Module);
}
if( moduleList.size() == 0 ) // Nothing to do
return;
// sort footprints by sheet path. we group them later by sheet
sort( moduleList.begin(), moduleList.end(), sortModulesbySheetPath );
// Undo command: init undo list
PICKED_ITEMS_LIST undoList;
undoList.m_Status = UR_CHANGED;
ITEM_PICKER picker( NULL, UR_CHANGED );
for( unsigned ii = 0; ii < moduleList.size(); ii++ )
{
Module = moduleList[ii];
// Undo: add copy of module to undo list
picker.SetItem( Module );
picker.SetLink( Module->Clone() );
undoList.PushItem( picker );
}
// Extract and place footprints by sheet
std::vector <MODULE*> moduleListBySheet;
std::vector <EDA_RECT> placementSheetAreas;
wxString curr_sheetPath ;
double subsurface;
double placementsurface = 0.0;
wxPoint placementAreaPosition = GetCrossHairPosition();
// We do not want to move footprints inside an existing board.
// move the placement area position outside the board bounding box
// to the left of the board
if( edgesExist )
{
if( placementAreaPosition.x < bbox.GetEnd().x &&
placementAreaPosition.y < bbox.GetEnd().y )
{
placementAreaPosition.x = bbox.GetEnd().x;
placementAreaPosition.y = bbox.GetOrigin().y;
}
}
// The placement uses 2 passes:
// the first pass creates the rectangular areas to place footprints
// each sheet in schematic creates one rectangular area.
// the second pass moves footprints inside these areas
for( int pass = 0; pass < 2; pass++ )
{
int subareaIdx = 0;
curr_sheetPath = moduleList[0]->GetPath().BeforeLast( '/' );
moduleListBySheet.clear();
subsurface = 0.0;
for( unsigned ii = 0; ii < moduleList.size(); ii++ )
{
Module = moduleList[ii];
bool iscurrPath = curr_sheetPath == moduleList[ii]->GetPath().BeforeLast( '/' );
if( iscurrPath )
{
moduleListBySheet.push_back( Module );
subsurface += Module->GetArea();
}
if( !iscurrPath || (ii == moduleList.size()-1) )
{
// end of the footprint sublist relative to the same sheet path
// calculate placement of the current sublist
EDA_RECT freeArea;
int Xsize_allowed = (int) ( sqrt( subsurface ) * 4.0 / 3.0 );
int Ysize_allowed = (int) ( subsurface / Xsize_allowed );
freeArea.SetWidth( Xsize_allowed );
freeArea.SetHeight( Ysize_allowed );
CRectPlacement placementArea;
if( pass == 1 )
{
wxPoint areapos = placementSheetAreas[subareaIdx].GetOrigin()
+ placementAreaPosition;
freeArea.SetOrigin( areapos );
}
bool findAreaOnly = pass == 0;
moveFootprintsInArea( placementArea, moduleListBySheet,
freeArea, findAreaOnly );
if( pass == 0 )
{
// Populate sheet placement areas list
EDA_RECT sub_area;
sub_area.SetWidth( placementArea.GetW()*scale );
sub_area.SetHeight( placementArea.GetH()*scale );
// Add a margin around the sheet placement area:
sub_area.Inflate( Millimeter2iu( 1.5 ) );
placementSheetAreas.push_back( sub_area );
placementsurface += (double) sub_area.GetWidth()*
sub_area.GetHeight();
}
curr_sheetPath = moduleList[ii]->GetPath().BeforeLast( '/' );
subsurface = 0.0;
moduleListBySheet.clear();
// Enter first module of next sheet
moduleListBySheet.push_back( Module );
subsurface += Module->GetArea();
subareaIdx++;
}
}
// End of pass:
// At the end of the first pass, we have to find position of each sheet
// placement area
if( pass == 0 )
{
int Xsize_allowed = (int) ( sqrt( placementsurface ) * 4.0 / 3.0 );
int Ysize_allowed = (int) ( placementsurface / Xsize_allowed );
CRectPlacement placementArea;
CSubRectArray vecSubRects;
fillRectList( vecSubRects, placementSheetAreas );
spreadRectangles( placementArea, vecSubRects, Xsize_allowed, Ysize_allowed );
for( unsigned it = 0; it < vecSubRects.size(); ++it )
{
TSubRect& srect = vecSubRects[it];
wxPoint pos( srect.x*scale, srect.y*scale );
wxSize size( srect.w*scale, srect.h*scale );
placementSheetAreas[srect.n].SetOrigin( pos );
placementSheetAreas[srect.n].SetSize( size );
}
}
} // End pass
// Undo: commit list
SaveCopyInUndoList( undoList, UR_CHANGED );
OnModify();
m_canvas->Refresh();
}
static bool sortModulesbySheetPath( MODULE* ref, MODULE* compare )
{
return compare->GetPath().Cmp( ref->GetPath() ) < 0;
}

View File

@ -386,25 +386,26 @@ bool PCB_EDIT_FRAME::OnRightClick( const wxPoint& aMousePos, wxMenu* aPopMenu )
{
wxMenu* commands = new wxMenu;
AddMenuItem( aPopMenu, commands, ID_POPUP_PCB_AUTOPLACE_COMMANDS,
_( "Global Move and Place" ), KiBitmap( move_xpm ) );
_( "Global Spread and Place" ), KiBitmap( move_xpm ) );
AddMenuItem( commands, ID_POPUP_PCB_AUTOPLACE_FREE_ALL_MODULES,
_( "Unlock All Modules" ), KiBitmap( unlocked_xpm ) );
_( "Unlock All Footprints" ), KiBitmap( unlocked_xpm ) );
AddMenuItem( commands, ID_POPUP_PCB_AUTOPLACE_FIXE_ALL_MODULES,
_( "Lock All Modules" ), KiBitmap( locked_xpm ) );
_( "Lock All Footprints" ), KiBitmap( locked_xpm ) );
commands->AppendSeparator();
AddMenuItem( commands, ID_POPUP_PCB_AUTOMOVE_ALL_MODULES,
_( "Move All Modules" ), KiBitmap( move_xpm ) );
commands->Append( ID_POPUP_PCB_AUTOMOVE_NEW_MODULES, _( "Move New Modules" ) );
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" ) );
commands->AppendSeparator();
commands->Append( ID_POPUP_PCB_AUTOPLACE_ALL_MODULES,
_( "Automatically Place All Modules" ) );
_( "Automatically Place All Footprints" ) );
commands->Append( ID_POPUP_PCB_AUTOPLACE_NEW_MODULES,
_( "Automatically Place New Modules" ) );
_( "Automatically Place New Footprints" ) );
commands->Append( ID_POPUP_PCB_AUTOPLACE_NEXT_MODULE,
_( "Automatically Place Next Module" ) );
_( "Automatically Place Next Footprints" ) );
commands->AppendSeparator();
AddMenuItem( commands, ID_POPUP_PCB_REORIENT_ALL_MODULES,
_( "Orient All Modules" ), KiBitmap( rotate_module_pos_xpm ) );
_( "Orient All Footprints" ), KiBitmap( rotate_module_pos_xpm ) );
aPopMenu->AppendSeparator();
}

View File

@ -1,10 +1,10 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004-2010 Jean-Pierre Charras, jean-pierre.charras@gpisa-lab.inpg.fr
* Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
* Copyright (C) 2010 KiCad Developers, see change_log.txt for contributors.
* Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2013 Wayne Stambaugh <stambaughw@verizon.net>
* Copyright (C) 2013 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
@ -237,7 +237,7 @@ BEGIN_EVENT_TABLE( PCB_EDIT_FRAME, PCB_BASE_FRAME )
PCB_EDIT_FRAME::ProcessMuWaveFunctions )
EVT_MENU_RANGE( ID_POPUP_PCB_AUTOPLACE_START_RANGE, ID_POPUP_PCB_AUTOPLACE_END_RANGE,
PCB_EDIT_FRAME::AutoPlace )
PCB_EDIT_FRAME::OnPlaceOrRouteFootprints )
EVT_MENU( ID_POPUP_PCB_REORIENT_ALL_MODULES, PCB_EDIT_FRAME::OnOrientFootprints )

View File

@ -216,8 +216,8 @@ enum pcbnew_ids
ID_POPUP_PCB_AUTOPLACE_FREE_ALL_MODULES,
ID_POPUP_PCB_AUTOPLACE_FIXE_ALL_MODULES,
ID_POPUP_PCB_AUTOPLACE_CURRENT_MODULE,
ID_POPUP_PCB_AUTOMOVE_ALL_MODULES,
ID_POPUP_PCB_AUTOMOVE_NEW_MODULES,
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,