ADDED: Pack and Move Footprints, improved footprint spread algorithm.
This commit is contained in:
parent
ec6cb49570
commit
4095172259
|
@ -406,6 +406,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
||||||
aBitmapInfoCache[BITMAPS::options_schematic].emplace_back( BITMAPS::options_schematic, wxT( "options_schematic_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::options_schematic].emplace_back( BITMAPS::options_schematic, wxT( "options_schematic_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::opt_show_polygon].emplace_back( BITMAPS::opt_show_polygon, wxT( "opt_show_polygon_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::opt_show_polygon].emplace_back( BITMAPS::opt_show_polygon, wxT( "opt_show_polygon_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::ortho].emplace_back( BITMAPS::ortho, wxT( "ortho_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::ortho].emplace_back( BITMAPS::ortho, wxT( "ortho_24.png" ), 24, wxT( "light" ) );
|
||||||
|
aBitmapInfoCache[BITMAPS::pack_footprints].emplace_back( BITMAPS::pack_footprints, wxT( "pack_footprints_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pad_sketch].emplace_back( BITMAPS::pad_sketch, wxT( "pad_sketch_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::pad_sketch].emplace_back( BITMAPS::pad_sketch, wxT( "pad_sketch_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pad].emplace_back( BITMAPS::pad, wxT( "pad_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::pad].emplace_back( BITMAPS::pad, wxT( "pad_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pad_enumerate].emplace_back( BITMAPS::pad_enumerate, wxT( "pad_enumerate_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::pad_enumerate].emplace_back( BITMAPS::pad_enumerate, wxT( "pad_enumerate_24.png" ), 24, wxT( "light" ) );
|
||||||
|
@ -786,6 +787,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
||||||
aBitmapInfoCache[BITMAPS::options_schematic].emplace_back( BITMAPS::options_schematic, wxT( "options_schematic_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::options_schematic].emplace_back( BITMAPS::options_schematic, wxT( "options_schematic_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::opt_show_polygon].emplace_back( BITMAPS::opt_show_polygon, wxT( "opt_show_polygon_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::opt_show_polygon].emplace_back( BITMAPS::opt_show_polygon, wxT( "opt_show_polygon_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::ortho].emplace_back( BITMAPS::ortho, wxT( "ortho_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::ortho].emplace_back( BITMAPS::ortho, wxT( "ortho_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
|
aBitmapInfoCache[BITMAPS::pack_footprints].emplace_back( BITMAPS::pack_footprints, wxT( "pack_footprints_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pad_sketch].emplace_back( BITMAPS::pad_sketch, wxT( "pad_sketch_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::pad_sketch].emplace_back( BITMAPS::pad_sketch, wxT( "pad_sketch_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pad].emplace_back( BITMAPS::pad, wxT( "pad_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::pad].emplace_back( BITMAPS::pad, wxT( "pad_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pad_enumerate].emplace_back( BITMAPS::pad_enumerate, wxT( "pad_enumerate_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::pad_enumerate].emplace_back( BITMAPS::pad_enumerate, wxT( "pad_enumerate_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
|
|
|
@ -373,6 +373,7 @@ enum class BITMAPS : unsigned int
|
||||||
options_pad,
|
options_pad,
|
||||||
options_schematic,
|
options_schematic,
|
||||||
ortho,
|
ortho,
|
||||||
|
pack_footprints,
|
||||||
pad,
|
pad,
|
||||||
pad_enumerate,
|
pad_enumerate,
|
||||||
pad_number,
|
pad_number,
|
||||||
|
|
|
@ -277,7 +277,6 @@ set( PCBNEW_CLASS_SRCS
|
||||||
${PCBNEW_NETLIST_SRCS}
|
${PCBNEW_NETLIST_SRCS}
|
||||||
${PCBNEW_BRDSTACKUP_MGR}
|
${PCBNEW_BRDSTACKUP_MGR}
|
||||||
|
|
||||||
autorouter/rect_placement/rect_placement.cpp
|
|
||||||
autorouter/spread_footprints.cpp
|
autorouter/spread_footprints.cpp
|
||||||
autorouter/ar_autoplacer.cpp
|
autorouter/ar_autoplacer.cpp
|
||||||
autorouter/ar_matrix.cpp
|
autorouter/ar_matrix.cpp
|
||||||
|
@ -639,6 +638,7 @@ target_link_libraries( pcbnew
|
||||||
gal
|
gal
|
||||||
scripting
|
scripting
|
||||||
nlohmann_json
|
nlohmann_json
|
||||||
|
rectpack2d
|
||||||
${wxWidgets_LIBRARIES}
|
${wxWidgets_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -673,6 +673,7 @@ target_link_libraries( pcbnew_kiface_objects
|
||||||
nanosvg
|
nanosvg
|
||||||
tinyspline_lib
|
tinyspline_lib
|
||||||
nlohmann_json
|
nlohmann_json
|
||||||
|
rectpack2d
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories( pcbnew_kiface_objects PRIVATE
|
target_include_directories( pcbnew_kiface_objects PRIVATE
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
||||||
* Copyright (C) 2013 Wayne Stambaugh <stambaughw@verizon.net>
|
* Copyright (C) 2013 Wayne Stambaugh <stambaughw@verizon.net>
|
||||||
*
|
*
|
||||||
* Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
|
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -34,309 +34,291 @@
|
||||||
* their selection.
|
* their selection.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <spread_footprints.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <refdes_utils.h>
|
||||||
|
#include <string_utils.h>
|
||||||
#include <confirm.h>
|
#include <confirm.h>
|
||||||
#include <pcb_edit_frame.h>
|
#include <pcb_edit_frame.h>
|
||||||
#include <board.h>
|
#include <board.h>
|
||||||
#include <footprint.h>
|
#include <rectpack2d/finders_interface.h>
|
||||||
#include <rect_placement/rect_placement.h>
|
|
||||||
|
|
||||||
struct TSubRect : public CRectPlacement::TRect
|
|
||||||
{
|
|
||||||
int n; // Original index of this subrect, before sorting
|
|
||||||
|
|
||||||
TSubRect() : TRect(),
|
constexpr bool allow_flip = true;
|
||||||
n( 0 )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TSubRect( int _w, int _h, int _n ) :
|
using spaces_type = rectpack2D::empty_spaces<allow_flip, rectpack2D::default_empty_spaces>;
|
||||||
TRect( 0, 0, _w, _h ), n( _n ) { }
|
using rect_type = rectpack2D::output_rect_t<spaces_type>;
|
||||||
};
|
using rect_ptr = rect_type*;
|
||||||
|
using rect_vector = std::vector<rect_type>;
|
||||||
typedef std::vector<TSubRect> CSubRectArray;
|
|
||||||
|
|
||||||
// Use 0.01 mm units to calculate placement, to avoid long calculation time
|
// Use 0.01 mm units to calculate placement, to avoid long calculation time
|
||||||
const int scale = (int) ( 0.01 * pcbIUScale.IU_PER_MM );
|
const int scale = (int) ( 0.01 * pcbIUScale.IU_PER_MM );
|
||||||
|
|
||||||
const int PADDING = (int) ( 1 * pcbIUScale.IU_PER_MM );
|
|
||||||
|
|
||||||
// Populates a list of rectangles, from a list of footprints
|
static bool compareFootprintsbyRef( FOOTPRINT* ref, FOOTPRINT* compare )
|
||||||
void fillRectList( CSubRectArray& vecSubRects, std::vector <FOOTPRINT*>& aFootprintList )
|
|
||||||
{
|
{
|
||||||
vecSubRects.clear();
|
const wxString& refPrefix = UTIL::GetRefDesPrefix( ref->GetReference() );
|
||||||
|
const wxString& cmpPrefix = UTIL::GetRefDesPrefix( compare->GetReference() );
|
||||||
|
|
||||||
for( unsigned ii = 0; ii < aFootprintList.size(); ii++ )
|
if( refPrefix != cmpPrefix )
|
||||||
{
|
{
|
||||||
BOX2I fpBox = aFootprintList[ii]->GetBoundingBox( false, false );
|
return refPrefix < cmpPrefix;
|
||||||
TSubRect fpRect( ( fpBox.GetWidth() + PADDING ) / scale,
|
|
||||||
( fpBox.GetHeight() + PADDING ) / scale, ii );
|
|
||||||
vecSubRects.push_back( fpRect );
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const int refInt = GetTrailingInt( ref->GetReference() );
|
||||||
|
const int cmpInt = GetTrailingInt( compare->GetReference() );
|
||||||
|
|
||||||
|
return refInt < cmpInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populates a list of rectangles, from a list of BOX2I
|
return false;
|
||||||
void fillRectList( CSubRectArray& vecSubRects, std::vector<BOX2I>& aRectList )
|
|
||||||
{
|
|
||||||
vecSubRects.clear();
|
|
||||||
|
|
||||||
for( unsigned ii = 0; ii < aRectList.size(); ii++ )
|
|
||||||
{
|
|
||||||
BOX2I& 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
|
// Spread a list of rectangles inside a placement area
|
||||||
void spreadRectangles( CRectPlacement& aPlacementArea,
|
rectpack2D::rect_wh spreadRectangles( rect_vector& vecSubRects, int areaSizeX, int areaSizeY )
|
||||||
CSubRectArray& vecSubRects,
|
|
||||||
int areaSizeX, int areaSizeY )
|
|
||||||
{
|
{
|
||||||
areaSizeX /= scale;
|
areaSizeX /= scale;
|
||||||
areaSizeY /= scale;
|
areaSizeY /= scale;
|
||||||
|
|
||||||
// Sort the subRects based on dimensions, larger dimension goes first.
|
rectpack2D::rect_wh result;
|
||||||
std::sort( vecSubRects.begin(), vecSubRects.end(), CRectPlacement::TRect::Greater );
|
|
||||||
|
|
||||||
// gives the initial size to the area
|
int max_side = std::max( areaSizeX, areaSizeY );
|
||||||
aPlacementArea.Init( areaSizeX, areaSizeY );
|
|
||||||
|
|
||||||
// Add all subrects
|
while( true )
|
||||||
CSubRectArray::iterator it;
|
|
||||||
|
|
||||||
for( it = vecSubRects.begin(); it != vecSubRects.end(); )
|
|
||||||
{
|
{
|
||||||
CRectPlacement::TRect r( 0, 0, it->w, it->h );
|
bool anyUnsuccessful = false;
|
||||||
|
const int discard_step = 1;
|
||||||
|
|
||||||
bool bPlaced = aPlacementArea.AddAtEmptySpotAutoGrow( &r, areaSizeX, areaSizeY );
|
auto report_successful = [&]( rect_type& )
|
||||||
|
|
||||||
if( !bPlaced ) // No room to place the rectangle: enlarge area and retry
|
|
||||||
{
|
{
|
||||||
bool retry = false;
|
return rectpack2D::callback_result::CONTINUE_PACKING;
|
||||||
|
};
|
||||||
|
|
||||||
if( areaSizeX < INT_MAX/2 )
|
auto report_unsuccessful = [&]( rect_type& r )
|
||||||
{
|
{
|
||||||
retry = true;
|
anyUnsuccessful = true;
|
||||||
areaSizeX = areaSizeX * 1.2;
|
return rectpack2D::callback_result::ABORT_PACKING;
|
||||||
}
|
};
|
||||||
|
|
||||||
if( areaSizeX < INT_MAX/2 )
|
result = rectpack2D::find_best_packing<spaces_type>(
|
||||||
{
|
vecSubRects,
|
||||||
retry = true;
|
make_finder_input( max_side, discard_step, report_successful, report_unsuccessful,
|
||||||
areaSizeY = areaSizeY * 1.2;
|
rectpack2D::flipping_option::DISABLED ) );
|
||||||
}
|
|
||||||
|
|
||||||
if( retry )
|
if( anyUnsuccessful )
|
||||||
{
|
{
|
||||||
aPlacementArea.Init( areaSizeX, areaSizeY );
|
max_side = (int) ( max_side * 1.2 );
|
||||||
it = vecSubRects.begin();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When correctly placed in a placement area, the coords are returned in r.x and r.y
|
return result;
|
||||||
// Store them.
|
|
||||||
it->x = r.x;
|
|
||||||
it->y = r.y;
|
|
||||||
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void moveFootprintsInArea( CRectPlacement& aPlacementArea, std::vector<FOOTPRINT*>& aFootprintList,
|
void SpreadFootprints( std::vector<FOOTPRINT*>* aFootprints, VECTOR2I aTargetBoxPosition,
|
||||||
const BOX2I& aFreeArea, bool aFindAreaOnly )
|
bool aGroupBySheet, int aComponentGap, int aGroupGap )
|
||||||
{
|
{
|
||||||
CSubRectArray vecSubRects;
|
using FpBBoxToFootprintsPair = std::pair<BOX2I, std::vector<FOOTPRINT*>>;
|
||||||
|
using SheetBBoxToFootprintsMapPair =
|
||||||
|
std::pair<BOX2I, std::map<VECTOR2I, FpBBoxToFootprintsPair>>;
|
||||||
|
|
||||||
fillRectList( vecSubRects, aFootprintList );
|
std::map<wxString, SheetBBoxToFootprintsMapPair> sheetsMap;
|
||||||
spreadRectangles( aPlacementArea, vecSubRects, aFreeArea.GetWidth(), aFreeArea.GetHeight() );
|
std::vector<BOX2I> blockMap;
|
||||||
|
|
||||||
if( aFindAreaOnly )
|
|
||||||
return;
|
|
||||||
|
|
||||||
for( unsigned it = 0; it < vecSubRects.size(); ++it )
|
|
||||||
{
|
|
||||||
VECTOR2I pos( vecSubRects[it].x, vecSubRects[it].y );
|
|
||||||
pos.x *= scale;
|
|
||||||
pos.y *= scale;
|
|
||||||
|
|
||||||
FOOTPRINT* footprint = aFootprintList[vecSubRects[it].n];
|
|
||||||
|
|
||||||
BOX2I fpBBox = footprint->GetBoundingBox( false, false );
|
|
||||||
VECTOR2I mod_pos = pos + ( footprint->GetPosition() - fpBBox.GetOrigin() )
|
|
||||||
+ aFreeArea.GetOrigin();
|
|
||||||
|
|
||||||
footprint->Move( mod_pos - footprint->GetPosition() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sortFootprintsbySheetPath( FOOTPRINT* ref, FOOTPRINT* compare );
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 aBoard is the board to edit.
|
|
||||||
* @param aFootprints: a list of footprints to be spread out.
|
|
||||||
* @param aSpreadAreaPosition the position of the upper left corner of the
|
|
||||||
* area allowed to spread footprints
|
|
||||||
*/
|
|
||||||
void SpreadFootprints( std::vector<FOOTPRINT*>* aFootprints, VECTOR2I aSpreadAreaPosition )
|
|
||||||
{
|
|
||||||
// Build candidate list
|
|
||||||
// calculate also the area needed by these footprints
|
|
||||||
std::vector <FOOTPRINT*> footprintList;
|
|
||||||
|
|
||||||
|
// Fill in the maps
|
||||||
for( FOOTPRINT* footprint : *aFootprints )
|
for( FOOTPRINT* footprint : *aFootprints )
|
||||||
{
|
{
|
||||||
if( footprint->IsLocked() )
|
wxString path =
|
||||||
continue;
|
aGroupBySheet ? footprint->GetPath().AsString().BeforeLast( '/' ) : wxS( "" );
|
||||||
|
|
||||||
footprintList.push_back( footprint );
|
VECTOR2I size = footprint->GetBoundingBox( false, false ).GetSize();
|
||||||
|
size.x += aComponentGap;
|
||||||
|
size.y += aComponentGap;
|
||||||
|
|
||||||
|
sheetsMap[path].second[size].second.push_back( footprint );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( footprintList.empty() )
|
for( auto& [sheetPath, sheetPair] : sheetsMap )
|
||||||
return;
|
|
||||||
|
|
||||||
// sort footprints by sheet path. we group them later by sheet
|
|
||||||
sort( footprintList.begin(), footprintList.end(), sortFootprintsbySheetPath );
|
|
||||||
|
|
||||||
// Extract and place footprints by sheet
|
|
||||||
std::vector<FOOTPRINT*> footprintListBySheet;
|
|
||||||
std::vector<BOX2I> placementSheetAreas;
|
|
||||||
double subsurface;
|
|
||||||
double placementsurface = 0.0;
|
|
||||||
|
|
||||||
// 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;
|
auto& [sheet_bbox, sizeToFpMap] = sheetPair;
|
||||||
footprintListBySheet.clear();
|
|
||||||
subsurface = 0.0;
|
|
||||||
|
|
||||||
int fp_max_width = 0;
|
for( auto& [fpSize, fpPair] : sizeToFpMap )
|
||||||
int fp_max_height = 0;
|
|
||||||
|
|
||||||
for( unsigned ii = 0; ii < footprintList.size(); ii++ )
|
|
||||||
{
|
{
|
||||||
FOOTPRINT* footprint = footprintList[ii];
|
auto& [block_bbox, footprints] = fpPair;
|
||||||
bool islastItem = false;
|
|
||||||
|
|
||||||
if( ii == footprintList.size() - 1 ||
|
// Find optimal arrangement of same-size footprints
|
||||||
( footprintList[ii]->GetPath().AsString().BeforeLast( '/' ) !=
|
|
||||||
footprintList[ii+1]->GetPath().AsString().BeforeLast( '/' ) ) )
|
|
||||||
islastItem = true;
|
|
||||||
|
|
||||||
footprintListBySheet.push_back( footprint );
|
double blockEstimateArea = (double) fpSize.x * fpSize.y * footprints.size();
|
||||||
subsurface += footprint->GetArea( PADDING );
|
double initialSide = std::sqrt( blockEstimateArea );
|
||||||
|
bool vertical = fpSize.x >= fpSize.y;
|
||||||
|
|
||||||
// Calculate min size of placement area:
|
int initialCountPerLine = footprints.size();
|
||||||
BOX2I bbox = footprint->GetBoundingBox( false, false );
|
|
||||||
fp_max_width = std::max( fp_max_width, bbox.GetWidth() );
|
|
||||||
fp_max_height = std::max( fp_max_height, bbox.GetHeight() );
|
|
||||||
|
|
||||||
if( islastItem )
|
const int singleLineRatio = 5;
|
||||||
|
|
||||||
|
// Wrap the line if the ratio is not satisfied
|
||||||
|
if( vertical )
|
||||||
{
|
{
|
||||||
// end of the footprint sublist relative to the same sheet path
|
if( ( fpSize.y * footprints.size() / fpSize.x ) > singleLineRatio )
|
||||||
// calculate placement of the current sublist
|
initialCountPerLine = initialSide / fpSize.y;
|
||||||
BOX2I freeArea;
|
}
|
||||||
int Xsize_allowed = (int) ( sqrt( subsurface ) * 4.0 / 3.0 );
|
else
|
||||||
Xsize_allowed = std::max( fp_max_width, Xsize_allowed );
|
|
||||||
|
|
||||||
int Ysize_allowed = (int) ( subsurface / Xsize_allowed );
|
|
||||||
Ysize_allowed = std::max( fp_max_height, Ysize_allowed );
|
|
||||||
|
|
||||||
freeArea.SetWidth( Xsize_allowed );
|
|
||||||
freeArea.SetHeight( Ysize_allowed );
|
|
||||||
CRectPlacement placementArea;
|
|
||||||
|
|
||||||
if( pass == 1 )
|
|
||||||
{
|
{
|
||||||
VECTOR2I areapos =
|
if( ( fpSize.x * footprints.size() / fpSize.y ) > singleLineRatio )
|
||||||
placementSheetAreas[subareaIdx].GetOrigin()
|
initialCountPerLine = initialSide / fpSize.x;
|
||||||
+ aSpreadAreaPosition;
|
|
||||||
freeArea.SetOrigin( areapos );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool findAreaOnly = pass == 0;
|
int optimalCountPerLine = initialCountPerLine;
|
||||||
moveFootprintsInArea( placementArea, footprintListBySheet, freeArea, findAreaOnly );
|
int optimalRemainder = footprints.size() % optimalCountPerLine;
|
||||||
|
|
||||||
if( pass == 0 )
|
if( optimalRemainder != 0 )
|
||||||
{
|
{
|
||||||
// Populate sheet placement areas list
|
for( int i = std::max( 2, initialCountPerLine - 2 );
|
||||||
BOX2I sub_area;
|
i <= std::min( (int) footprints.size() - 2, initialCountPerLine + 2 ); i++ )
|
||||||
sub_area.SetWidth( placementArea.GetW()*scale );
|
{
|
||||||
sub_area.SetHeight( placementArea.GetH()*scale );
|
int r = footprints.size() % i;
|
||||||
// Add a margin around the sheet placement area:
|
|
||||||
sub_area.Inflate( pcbIUScale.mmToIU( 1.5 ) );
|
|
||||||
|
|
||||||
placementSheetAreas.push_back( sub_area );
|
if( r == 0 || r >= optimalRemainder )
|
||||||
|
{
|
||||||
placementsurface += (double) sub_area.GetWidth()*
|
optimalCountPerLine = i;
|
||||||
sub_area.GetHeight();
|
optimalRemainder = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare buffers for next sheet
|
|
||||||
subsurface = 0.0;
|
|
||||||
footprintListBySheet.clear();
|
|
||||||
subareaIdx++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// End of pass:
|
std::sort( footprints.begin(), footprints.end(), compareFootprintsbyRef );
|
||||||
// At the end of the first pass, we have to find position of each sheet
|
|
||||||
// placement area
|
// Arrange footprints in rows or columns (blocks)
|
||||||
if( pass == 0 )
|
for( unsigned i = 0; i < footprints.size(); i++ )
|
||||||
{
|
{
|
||||||
int Xsize_allowed = (int) ( sqrt( placementsurface ) * 4.0 / 3.0 );
|
FOOTPRINT* footprint = footprints[i];
|
||||||
|
|
||||||
if( Xsize_allowed <= 0 || Xsize_allowed > INT_MAX/2 )
|
VECTOR2I position = fpSize / 2;
|
||||||
Xsize_allowed = INT_MAX/2;
|
|
||||||
|
|
||||||
int Ysize_allowed = (int) ( placementsurface / Xsize_allowed );
|
if( vertical )
|
||||||
|
|
||||||
if( Ysize_allowed <= 0 || Ysize_allowed > INT_MAX/2 )
|
|
||||||
Ysize_allowed = INT_MAX/2;
|
|
||||||
|
|
||||||
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];
|
position.x += fpSize.x * ( i / optimalCountPerLine );
|
||||||
VECTOR2I pos( srect.x * scale, srect.y * scale );
|
position.y += fpSize.y * ( i % optimalCountPerLine );
|
||||||
VECTOR2I size( srect.w * scale, srect.h * scale );
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
position.x += fpSize.x * ( i % optimalCountPerLine );
|
||||||
|
position.y += fpSize.y * ( i / optimalCountPerLine );
|
||||||
|
}
|
||||||
|
|
||||||
|
BOX2I old_fp_bbox = footprint->GetBoundingBox( false, false );
|
||||||
|
footprint->Move( position - old_fp_bbox.GetOrigin() );
|
||||||
|
|
||||||
|
BOX2I new_fp_bbox = footprint->GetBoundingBox( false, false );
|
||||||
|
new_fp_bbox.Inflate( aComponentGap / 2 );
|
||||||
|
block_bbox.Merge( new_fp_bbox );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rect_vector vecSubRects;
|
||||||
|
long long blocksArea = 0;
|
||||||
|
|
||||||
|
// Fill in arrays for packing of blocks
|
||||||
|
for( auto& [fpSize, fpPair] : sizeToFpMap )
|
||||||
|
{
|
||||||
|
auto& [block_bbox, footprints] = fpPair;
|
||||||
|
|
||||||
|
vecSubRects.emplace_back( 0, 0, block_bbox.GetWidth() / scale,
|
||||||
|
block_bbox.GetHeight() / scale, false );
|
||||||
|
|
||||||
|
blocksArea += block_bbox.GetArea();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack the blocks
|
||||||
|
int areaSide = std::sqrt( blocksArea );
|
||||||
|
spreadRectangles( vecSubRects, areaSide, areaSide );
|
||||||
|
|
||||||
|
unsigned block_i = 0;
|
||||||
|
|
||||||
|
// Move footprints to the new block locations
|
||||||
|
for( auto& [fpSize, pair] : sizeToFpMap )
|
||||||
|
{
|
||||||
|
auto& [src_bbox, footprints] = pair;
|
||||||
|
|
||||||
|
rect_type srect = vecSubRects[block_i];
|
||||||
|
|
||||||
|
VECTOR2I target_pos( srect.x * scale, srect.y * scale );
|
||||||
|
VECTOR2I target_size( srect.w * scale, srect.h * scale );
|
||||||
|
|
||||||
// Avoid too large coordinates: Overlapping components
|
// Avoid too large coordinates: Overlapping components
|
||||||
// are better than out of screen components
|
// are better than out of screen components
|
||||||
if( (uint64_t)pos.x + (uint64_t)size.x > INT_MAX/2 )
|
if( (uint64_t) target_pos.x + (uint64_t) target_size.x > INT_MAX / 2 )
|
||||||
pos.x = 0;
|
target_pos.x -= INT_MAX / 2;
|
||||||
|
|
||||||
if( (uint64_t)pos.y + (uint64_t)size.y > INT_MAX/2 )
|
if( (uint64_t) target_pos.y + (uint64_t) target_size.y > INT_MAX / 2 )
|
||||||
pos.y = 0;
|
target_pos.y -= INT_MAX / 2;
|
||||||
|
|
||||||
placementSheetAreas[srect.n].SetOrigin( pos );
|
for( FOOTPRINT* footprint : footprints )
|
||||||
placementSheetAreas[srect.n].SetSize( size );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // End pass
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Sort function, used to group footprints by sheet.
|
|
||||||
// Footprints are sorted by their sheet path.
|
|
||||||
// (the full sheet path restricted to the time stamp of the sheet itself,
|
|
||||||
// without the time stamp of the footprint ).
|
|
||||||
static bool sortFootprintsbySheetPath( FOOTPRINT* ref, FOOTPRINT* compare )
|
|
||||||
{
|
{
|
||||||
return ref->GetPath() < compare->GetPath();
|
footprint->Move( target_pos - src_bbox.GetPosition() );
|
||||||
|
sheet_bbox.Merge( footprint->GetBoundingBox( false, false ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
block_i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rect_vector vecSubRects;
|
||||||
|
long long sheetsArea = 0;
|
||||||
|
|
||||||
|
// Fill in arrays for packing of hierarchical sheet groups
|
||||||
|
for( auto& [sheetPath, sheetPair] : sheetsMap )
|
||||||
|
{
|
||||||
|
auto& [sheet_bbox, sizeToFpMap] = sheetPair;
|
||||||
|
BOX2I rect = sheet_bbox;
|
||||||
|
|
||||||
|
// Add a margin around the sheet placement area:
|
||||||
|
rect.Inflate( aGroupGap );
|
||||||
|
|
||||||
|
vecSubRects.emplace_back( 0, 0, rect.GetWidth() / scale, rect.GetHeight() / scale, false );
|
||||||
|
|
||||||
|
sheetsArea += sheet_bbox.GetArea();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack the hierarchical sheet groups
|
||||||
|
int areaSide = std::sqrt( sheetsArea );
|
||||||
|
spreadRectangles( vecSubRects, areaSide, areaSide );
|
||||||
|
|
||||||
|
unsigned srect_i = 0;
|
||||||
|
|
||||||
|
// Move footprints to the new hierarchical sheet group locations
|
||||||
|
for( auto& [sheetPath, sheetPair] : sheetsMap )
|
||||||
|
{
|
||||||
|
auto& [src_bbox, sizeToFpMap] = sheetPair;
|
||||||
|
|
||||||
|
rect_type srect = vecSubRects[srect_i];
|
||||||
|
|
||||||
|
VECTOR2I target_pos( srect.x * scale + aTargetBoxPosition.x,
|
||||||
|
srect.y * scale + aTargetBoxPosition.y );
|
||||||
|
VECTOR2I target_size( srect.w * scale, srect.h * scale );
|
||||||
|
|
||||||
|
// Avoid too large coordinates: Overlapping components
|
||||||
|
// are better than out of screen components
|
||||||
|
if( (uint64_t) target_pos.x + (uint64_t) target_size.x > INT_MAX / 2 )
|
||||||
|
target_pos.x -= INT_MAX / 2;
|
||||||
|
|
||||||
|
if( (uint64_t) target_pos.y + (uint64_t) target_size.y > INT_MAX / 2 )
|
||||||
|
target_pos.y -= INT_MAX / 2;
|
||||||
|
|
||||||
|
for( auto& [fpSize, fpPair] : sizeToFpMap )
|
||||||
|
{
|
||||||
|
auto& [block_bbox, footprints] = fpPair;
|
||||||
|
for( FOOTPRINT* footprint : footprints )
|
||||||
|
{
|
||||||
|
footprint->Move( target_pos - src_bbox.GetPosition() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
srect_i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 1992-2022 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __SPREAD_FOOTPRINTS_H
|
||||||
|
#define __SPREAD_FOOTPRINTS_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <footprint.h>
|
||||||
|
#include <base_units.h>
|
||||||
|
#include <math/vector2d.h>
|
||||||
|
#include <board.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 aBoard is the board to edit.
|
||||||
|
* @param aFootprints: a list of footprints to be spread out.
|
||||||
|
* @param aTargetBoxPosition the position of the upper left corner of the
|
||||||
|
* area allowed to spread footprints
|
||||||
|
*/
|
||||||
|
void SpreadFootprints( std::vector<FOOTPRINT*>* aFootprints, VECTOR2I aTargetBoxPosition,
|
||||||
|
bool aGroupBySheet = true, int aComponentGap = pcbIUScale.mmToIU( 1 ),
|
||||||
|
int aGroupGap = pcbIUScale.mmToIU( 1.5 ) );
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -36,6 +36,7 @@ using namespace std::placeholders;
|
||||||
#include <fp_lib_table.h>
|
#include <fp_lib_table.h>
|
||||||
#include <board.h>
|
#include <board.h>
|
||||||
#include <footprint.h>
|
#include <footprint.h>
|
||||||
|
#include <spread_footprints.h>
|
||||||
#include <ratsnest/ratsnest_data.h>
|
#include <ratsnest/ratsnest_data.h>
|
||||||
#include <io_mgr.h>
|
#include <io_mgr.h>
|
||||||
#include "board_netlist_updater.h"
|
#include "board_netlist_updater.h"
|
||||||
|
@ -46,9 +47,6 @@ using namespace std::placeholders;
|
||||||
#include <wx/msgdlg.h>
|
#include <wx/msgdlg.h>
|
||||||
|
|
||||||
|
|
||||||
extern void SpreadFootprints( std::vector<FOOTPRINT*>* aFootprints, VECTOR2I aSpreadAreaPosition );
|
|
||||||
|
|
||||||
|
|
||||||
bool PCB_EDIT_FRAME::ReadNetlistFromFile( const wxString &aFilename, NETLIST& aNetlist,
|
bool PCB_EDIT_FRAME::ReadNetlistFromFile( const wxString &aFilename, NETLIST& aNetlist,
|
||||||
REPORTER& aReporter )
|
REPORTER& aReporter )
|
||||||
{
|
{
|
||||||
|
@ -96,11 +94,10 @@ void PCB_EDIT_FRAME::OnNetlistChanged( BOARD_NETLIST_UPDATER& aUpdater, bool* aR
|
||||||
|
|
||||||
// Spread new footprints.
|
// Spread new footprints.
|
||||||
std::vector<FOOTPRINT*> newFootprints = aUpdater.GetAddedFootprints();
|
std::vector<FOOTPRINT*> newFootprints = aUpdater.GetAddedFootprints();
|
||||||
VECTOR2I areaPosition = GetCanvas()->GetViewControls()->GetCursorPosition();
|
|
||||||
|
|
||||||
GetToolManager()->RunAction( PCB_ACTIONS::selectionClear, true );
|
GetToolManager()->RunAction( PCB_ACTIONS::selectionClear, true );
|
||||||
|
|
||||||
SpreadFootprints( &newFootprints, areaPosition );
|
SpreadFootprints( &newFootprints, { 0, 0 }, true );
|
||||||
|
|
||||||
// Start drag command for new footprints
|
// Start drag command for new footprints
|
||||||
if( !newFootprints.empty() )
|
if( !newFootprints.empty() )
|
||||||
|
@ -109,13 +106,6 @@ void PCB_EDIT_FRAME::OnNetlistChanged( BOARD_NETLIST_UPDATER& aUpdater, bool* aR
|
||||||
GetToolManager()->RunAction( PCB_ACTIONS::selectItem, true, footprint );
|
GetToolManager()->RunAction( PCB_ACTIONS::selectItem, true, footprint );
|
||||||
|
|
||||||
*aRunDragCommand = true;
|
*aRunDragCommand = true;
|
||||||
|
|
||||||
// Now fix a reference point to move the footprints.
|
|
||||||
// We use the first footprint in list as reference point
|
|
||||||
// The graphic cursor will be on this fp when moving the footprints.
|
|
||||||
PCB_SELECTION_TOOL* selTool = GetToolManager()->GetTool<PCB_SELECTION_TOOL>();
|
|
||||||
PCB_SELECTION& selection = selTool->GetSelection();
|
|
||||||
selection.SetReferencePoint( newFootprints[0]->GetPosition() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Compile_Ratsnest( true );
|
Compile_Ratsnest( true );
|
||||||
|
|
|
@ -204,6 +204,8 @@ bool EDIT_TOOL::Init()
|
||||||
menu.AddItem( PCB_ACTIONS::mirrorH, SELECTION_CONDITIONS::NotEmpty );
|
menu.AddItem( PCB_ACTIONS::mirrorH, SELECTION_CONDITIONS::NotEmpty );
|
||||||
menu.AddItem( PCB_ACTIONS::mirrorV, SELECTION_CONDITIONS::NotEmpty );
|
menu.AddItem( PCB_ACTIONS::mirrorV, SELECTION_CONDITIONS::NotEmpty );
|
||||||
menu.AddItem( PCB_ACTIONS::swap, SELECTION_CONDITIONS::MoreThan( 1 ) );
|
menu.AddItem( PCB_ACTIONS::swap, SELECTION_CONDITIONS::MoreThan( 1 ) );
|
||||||
|
menu.AddItem( PCB_ACTIONS::packAndMoveFootprints, SELECTION_CONDITIONS::MoreThan( 1 )
|
||||||
|
&& SELECTION_CONDITIONS::HasType( PCB_FOOTPRINT_T ) );
|
||||||
|
|
||||||
menu.AddItem( PCB_ACTIONS::properties, propertiesCondition );
|
menu.AddItem( PCB_ACTIONS::properties, propertiesCondition );
|
||||||
|
|
||||||
|
@ -2258,6 +2260,7 @@ void EDIT_TOOL::setTransitions()
|
||||||
Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorH.MakeEvent() );
|
Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorH.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorV.MakeEvent() );
|
Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorV.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::Swap, PCB_ACTIONS::swap.MakeEvent() );
|
Go( &EDIT_TOOL::Swap, PCB_ACTIONS::swap.MakeEvent() );
|
||||||
|
Go( &EDIT_TOOL::PackAndMoveFootprints, PCB_ACTIONS::packAndMoveFootprints.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::ChangeTrackWidth, PCB_ACTIONS::changeTrackWidth.MakeEvent() );
|
Go( &EDIT_TOOL::ChangeTrackWidth, PCB_ACTIONS::changeTrackWidth.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::FilletTracks, PCB_ACTIONS::filletTracks.MakeEvent() );
|
Go( &EDIT_TOOL::FilletTracks, PCB_ACTIONS::filletTracks.MakeEvent() );
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,11 @@ public:
|
||||||
*/
|
*/
|
||||||
int Swap( const TOOL_EVENT& aEvent );
|
int Swap( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to fit selected footprints inside a minimal area and start movement.
|
||||||
|
*/
|
||||||
|
int PackAndMoveFootprints( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
int ChangeTrackWidth( const TOOL_EVENT& aEvent );
|
int ChangeTrackWidth( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <kiway.h>
|
#include <kiway.h>
|
||||||
#include <array_creator.h>
|
#include <array_creator.h>
|
||||||
#include <pcbnew_settings.h>
|
#include <pcbnew_settings.h>
|
||||||
|
#include <spread_footprints.h>
|
||||||
#include <status_popup.h>
|
#include <status_popup.h>
|
||||||
#include <tool/selection_conditions.h>
|
#include <tool/selection_conditions.h>
|
||||||
#include <tool/tool_manager.h>
|
#include <tool/tool_manager.h>
|
||||||
|
@ -380,6 +381,49 @@ int EDIT_TOOL::Swap( const TOOL_EVENT& aEvent )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int EDIT_TOOL::PackAndMoveFootprints( const TOOL_EVENT& aEvent )
|
||||||
|
{
|
||||||
|
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
|
||||||
|
|
||||||
|
PCB_SELECTION& selection = m_selectionTool->RequestSelection(
|
||||||
|
[]( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
|
||||||
|
{
|
||||||
|
// Iterate from the back so we don't have to worry about removals.
|
||||||
|
for( int i = aCollector.GetCount() - 1; i >= 0; --i )
|
||||||
|
{
|
||||||
|
BOARD_ITEM* item = aCollector[i];
|
||||||
|
|
||||||
|
if( !dynamic_cast<FOOTPRINT*>( item ) )
|
||||||
|
aCollector.Remove( item );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true /* prompt user regarding locked items */ );
|
||||||
|
|
||||||
|
std::vector<FOOTPRINT*> footprintsToPack;
|
||||||
|
|
||||||
|
for( EDA_ITEM* item : selection )
|
||||||
|
footprintsToPack.push_back( static_cast<FOOTPRINT*>( item ) );
|
||||||
|
|
||||||
|
if( footprintsToPack.empty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
BOARD_COMMIT commit( editFrame );
|
||||||
|
BOX2I footprintsBbox;
|
||||||
|
|
||||||
|
for( FOOTPRINT* item : footprintsToPack )
|
||||||
|
{
|
||||||
|
commit.Modify( item );
|
||||||
|
footprintsBbox.Merge( item->GetBoundingBox( false, false ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
SpreadFootprints( &footprintsToPack, footprintsBbox.Normalize().GetOrigin(), false );
|
||||||
|
|
||||||
|
commit.Push( _( "Pack footprints" ) );
|
||||||
|
|
||||||
|
return doMoveSelection( aEvent );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int EDIT_TOOL::Move( const TOOL_EVENT& aEvent )
|
int EDIT_TOOL::Move( const TOOL_EVENT& aEvent )
|
||||||
{
|
{
|
||||||
if( isRouterActive() )
|
if( isRouterActive() )
|
||||||
|
|
|
@ -342,6 +342,13 @@ TOOL_ACTION PCB_ACTIONS::swap( "pcbnew.InteractiveEdit.swap",
|
||||||
_( "Swap" ), _( "Swaps selected items' positions" ),
|
_( "Swap" ), _( "Swaps selected items' positions" ),
|
||||||
BITMAPS::swap );
|
BITMAPS::swap );
|
||||||
|
|
||||||
|
TOOL_ACTION PCB_ACTIONS::packAndMoveFootprints( "pcbnew.InteractiveEdit.packAndMoveFootprints",
|
||||||
|
AS_GLOBAL,
|
||||||
|
'P', "",
|
||||||
|
_( "Pack and Move Footprints" ),
|
||||||
|
_( "Sorts selected footprints by reference, packs based on size and initiates movement" ),
|
||||||
|
BITMAPS::pack_footprints );
|
||||||
|
|
||||||
TOOL_ACTION PCB_ACTIONS::changeTrackWidth( "pcbnew.InteractiveEdit.changeTrackWidth",
|
TOOL_ACTION PCB_ACTIONS::changeTrackWidth( "pcbnew.InteractiveEdit.changeTrackWidth",
|
||||||
AS_GLOBAL, 0, "",
|
AS_GLOBAL, 0, "",
|
||||||
_( "Change Track Width" ), _( "Updates selected track & via sizes" ) );
|
_( "Change Track Width" ), _( "Updates selected track & via sizes" ) );
|
||||||
|
|
|
@ -132,6 +132,9 @@ public:
|
||||||
/// Swapping of selected items
|
/// Swapping of selected items
|
||||||
static TOOL_ACTION swap;
|
static TOOL_ACTION swap;
|
||||||
|
|
||||||
|
/// Pack and start moving selected footprints
|
||||||
|
static TOOL_ACTION packAndMoveFootprints;
|
||||||
|
|
||||||
/// Update selected tracks & vias to the current track & via dimensions
|
/// Update selected tracks & vias to the current track & via dimensions
|
||||||
static TOOL_ACTION changeTrackWidth;
|
static TOOL_ACTION changeTrackWidth;
|
||||||
|
|
||||||
|
|
|
@ -383,6 +383,7 @@ set( BMAPS_MID
|
||||||
options_schematic
|
options_schematic
|
||||||
opt_show_polygon
|
opt_show_polygon
|
||||||
ortho
|
ortho
|
||||||
|
pack_footprints
|
||||||
pad_sketch
|
pad_sketch
|
||||||
pad
|
pad
|
||||||
pad_enumerate
|
pad_enumerate
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 131 B |
Binary file not shown.
After Width: | Height: | Size: 132 B |
|
@ -0,0 +1,188 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
id="Слой_1"
|
||||||
|
data-name="Слой 1"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
version="1.1"
|
||||||
|
sodipodi:docname="pack_footprints.svg"
|
||||||
|
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1380"
|
||||||
|
id="namedview30"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="22.627417"
|
||||||
|
inkscape:cx="3.1897126"
|
||||||
|
inkscape:cy="14.798326"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
inkscape:current-layer="Слой_1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid_kicad"
|
||||||
|
spacingx="0.5"
|
||||||
|
spacingy="0.5"
|
||||||
|
color="#9999ff"
|
||||||
|
opacity="0.13"
|
||||||
|
empspacing="2" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata43">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title>array</dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs115922">
|
||||||
|
<style
|
||||||
|
id="style115920">.cls-1{fill:#1a81c4;}.cls-2{fill:#545454;}</style>
|
||||||
|
</defs>
|
||||||
|
<title
|
||||||
|
id="title115924">array</title>
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="2"
|
||||||
|
y="2"
|
||||||
|
width="8"
|
||||||
|
height="6"
|
||||||
|
id="rect115932"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.76980036;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="2"
|
||||||
|
y="9"
|
||||||
|
width="8"
|
||||||
|
height="6"
|
||||||
|
id="rect115932-3"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.76980036;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="2"
|
||||||
|
y="16"
|
||||||
|
width="8"
|
||||||
|
height="6"
|
||||||
|
id="rect115932-3-6"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.76980036;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="11"
|
||||||
|
y="2"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.4303315;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="11"
|
||||||
|
y="6"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-5"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.4303315;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="11"
|
||||||
|
y="10"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-3"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.4303315;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="11"
|
||||||
|
y="14"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-5-5"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.4303315;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="11"
|
||||||
|
y="18"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-3-6"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.4303315;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="17"
|
||||||
|
y="2"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-2"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.4303315;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="17"
|
||||||
|
y="6"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-5-9"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.4303315;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="17"
|
||||||
|
y="10"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-3-1"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.4303315;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="17"
|
||||||
|
y="14"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-5-5-2"
|
||||||
|
style="fill:#ded3dd;stroke-width:0.4303315;stroke:none;fill-opacity:1" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="0.49999997"
|
||||||
|
y="0.49999997"
|
||||||
|
width="23"
|
||||||
|
height="23"
|
||||||
|
id="rect157605"
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#42b8eb;stroke-width:1.00156999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
inkscape:transform-center-x="-37.586826"
|
||||||
|
inkscape:transform-center-y="-34.435834" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.1 KiB |
|
@ -0,0 +1,188 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
id="Слой_1"
|
||||||
|
data-name="Слой 1"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
version="1.1"
|
||||||
|
sodipodi:docname="pack_footprints.svg"
|
||||||
|
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1380"
|
||||||
|
id="namedview30"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="22.627417"
|
||||||
|
inkscape:cx="10.658528"
|
||||||
|
inkscape:cy="14.798326"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
inkscape:current-layer="Слой_1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid_kicad"
|
||||||
|
spacingx="0.5"
|
||||||
|
spacingy="0.5"
|
||||||
|
color="#9999ff"
|
||||||
|
opacity="0.13"
|
||||||
|
empspacing="2" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata43">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title>array</dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs115922">
|
||||||
|
<style
|
||||||
|
id="style115920">.cls-1{fill:#1a81c4;}.cls-2{fill:#545454;}</style>
|
||||||
|
</defs>
|
||||||
|
<title
|
||||||
|
id="title115924">array</title>
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="2"
|
||||||
|
y="2"
|
||||||
|
width="8"
|
||||||
|
height="6"
|
||||||
|
id="rect115932"
|
||||||
|
style="fill:#545454;stroke-width:0.76980036" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="2"
|
||||||
|
y="9"
|
||||||
|
width="8"
|
||||||
|
height="6"
|
||||||
|
id="rect115932-3"
|
||||||
|
style="fill:#545454;stroke-width:0.76980036" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="2"
|
||||||
|
y="16"
|
||||||
|
width="8"
|
||||||
|
height="6"
|
||||||
|
id="rect115932-3-6"
|
||||||
|
style="fill:#545454;stroke-width:0.76980036" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="11"
|
||||||
|
y="2"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7"
|
||||||
|
style="fill:#545454;stroke-width:0.4303315" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="11"
|
||||||
|
y="6"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-5"
|
||||||
|
style="fill:#545454;stroke-width:0.4303315" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="11"
|
||||||
|
y="10"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-3"
|
||||||
|
style="fill:#545454;stroke-width:0.4303315" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="11"
|
||||||
|
y="14"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-5-5"
|
||||||
|
style="fill:#545454;stroke-width:0.4303315" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="11"
|
||||||
|
y="18"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-3-6"
|
||||||
|
style="fill:#545454;stroke-width:0.4303315" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="17"
|
||||||
|
y="2"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-2"
|
||||||
|
style="fill:#545454;stroke-width:0.4303315" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="17"
|
||||||
|
y="6"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-5-9"
|
||||||
|
style="fill:#545454;stroke-width:0.4303315" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="17"
|
||||||
|
y="10"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-3-1"
|
||||||
|
style="fill:#545454;stroke-width:0.4303315" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="17"
|
||||||
|
y="14"
|
||||||
|
width="5"
|
||||||
|
height="3"
|
||||||
|
id="rect115932-7-5-5-2"
|
||||||
|
style="fill:#545454;stroke-width:0.4303315" />
|
||||||
|
<rect
|
||||||
|
class="cls-2"
|
||||||
|
x="0.49999997"
|
||||||
|
y="0.49999997"
|
||||||
|
width="23"
|
||||||
|
height="23"
|
||||||
|
id="rect157605"
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#1a81c4;stroke-width:1.00156999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
inkscape:transform-center-x="-37.586826"
|
||||||
|
inkscape:transform-center-y="-34.435834" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.8 KiB |
Loading…
Reference in New Issue