Browse Source
Pcbnew: Autoplace functions: renamed spread footprint functions. Rewritten.
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 codepull/1/head
14 changed files with 825 additions and 171 deletions
-
23d-viewer/3d_canvas.cpp
-
20common/drawpanel.cpp
-
14include/wxPcbStruct.h
-
6pcbnew/CMakeLists.txt
-
4pcbnew/autorouter/auto_place_footprints.cpp
-
157pcbnew/autorouter/move_and_route_event_functions.cpp
-
38pcbnew/autorouter/rect_placement/RectanglePlacement.txt
-
259pcbnew/autorouter/rect_placement/rect_placement.cpp
-
104pcbnew/autorouter/rect_placement/rect_placement.h
-
1pcbnew/autorouter/solve.cpp
-
356pcbnew/autorouter/spread_footprints.cpp
-
21pcbnew/onrightclick.cpp
-
10pcbnew/pcbframe.cpp
-
4pcbnew/pcbnew_id.h
@ -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 |
@ -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; |
|||
} |
@ -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_ |
@ -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; |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue