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::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::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].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" ) );
|
||||
|
@ -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::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::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].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" ) );
|
||||
|
|
|
@ -373,6 +373,7 @@ enum class BITMAPS : unsigned int
|
|||
options_pad,
|
||||
options_schematic,
|
||||
ortho,
|
||||
pack_footprints,
|
||||
pad,
|
||||
pad_enumerate,
|
||||
pad_number,
|
||||
|
|
|
@ -277,7 +277,6 @@ set( PCBNEW_CLASS_SRCS
|
|||
${PCBNEW_NETLIST_SRCS}
|
||||
${PCBNEW_BRDSTACKUP_MGR}
|
||||
|
||||
autorouter/rect_placement/rect_placement.cpp
|
||||
autorouter/spread_footprints.cpp
|
||||
autorouter/ar_autoplacer.cpp
|
||||
autorouter/ar_matrix.cpp
|
||||
|
@ -639,6 +638,7 @@ target_link_libraries( pcbnew
|
|||
gal
|
||||
scripting
|
||||
nlohmann_json
|
||||
rectpack2d
|
||||
${wxWidgets_LIBRARIES}
|
||||
)
|
||||
|
||||
|
@ -673,6 +673,7 @@ target_link_libraries( pcbnew_kiface_objects
|
|||
nanosvg
|
||||
tinyspline_lib
|
||||
nlohmann_json
|
||||
rectpack2d
|
||||
)
|
||||
|
||||
target_include_directories( pcbnew_kiface_objects PRIVATE
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -34,309 +34,291 @@
|
|||
* their selection.
|
||||
*/
|
||||
|
||||
#include <spread_footprints.h>
|
||||
#include <algorithm>
|
||||
#include <refdes_utils.h>
|
||||
#include <string_utils.h>
|
||||
#include <confirm.h>
|
||||
#include <pcb_edit_frame.h>
|
||||
#include <board.h>
|
||||
#include <footprint.h>
|
||||
#include <rect_placement/rect_placement.h>
|
||||
#include <rectpack2d/finders_interface.h>
|
||||
|
||||
struct TSubRect : public CRectPlacement::TRect
|
||||
{
|
||||
int n; // Original index of this subrect, before sorting
|
||||
|
||||
TSubRect() : TRect(),
|
||||
n( 0 )
|
||||
{
|
||||
}
|
||||
constexpr bool allow_flip = true;
|
||||
|
||||
TSubRect( int _w, int _h, int _n ) :
|
||||
TRect( 0, 0, _w, _h ), n( _n ) { }
|
||||
};
|
||||
|
||||
typedef std::vector<TSubRect> CSubRectArray;
|
||||
using spaces_type = rectpack2D::empty_spaces<allow_flip, rectpack2D::default_empty_spaces>;
|
||||
using rect_type = rectpack2D::output_rect_t<spaces_type>;
|
||||
using rect_ptr = rect_type*;
|
||||
using rect_vector = std::vector<rect_type>;
|
||||
|
||||
// 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 PADDING = (int) ( 1 * pcbIUScale.IU_PER_MM );
|
||||
|
||||
// Populates a list of rectangles, from a list of footprints
|
||||
void fillRectList( CSubRectArray& vecSubRects, std::vector <FOOTPRINT*>& aFootprintList )
|
||||
static bool compareFootprintsbyRef( FOOTPRINT* ref, FOOTPRINT* compare )
|
||||
{
|
||||
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 );
|
||||
TSubRect fpRect( ( fpBox.GetWidth() + PADDING ) / scale,
|
||||
( fpBox.GetHeight() + PADDING ) / scale, ii );
|
||||
vecSubRects.push_back( fpRect );
|
||||
return refPrefix < cmpPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
// Populates a list of rectangles, from a list of BOX2I
|
||||
void fillRectList( CSubRectArray& vecSubRects, std::vector<BOX2I>& aRectList )
|
||||
{
|
||||
vecSubRects.clear();
|
||||
|
||||
for( unsigned ii = 0; ii < aRectList.size(); ii++ )
|
||||
else
|
||||
{
|
||||
BOX2I& rect = aRectList[ii];
|
||||
TSubRect fpRect( rect.GetWidth()/scale, rect.GetHeight()/scale, ii );
|
||||
vecSubRects.push_back( fpRect );
|
||||
}
|
||||
}
|
||||
const int refInt = GetTrailingInt( ref->GetReference() );
|
||||
const int cmpInt = GetTrailingInt( compare->GetReference() );
|
||||
|
||||
return refInt < cmpInt;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Spread a list of rectangles inside a placement area
|
||||
void spreadRectangles( CRectPlacement& aPlacementArea,
|
||||
CSubRectArray& vecSubRects,
|
||||
int areaSizeX, int areaSizeY )
|
||||
rectpack2D::rect_wh spreadRectangles( rect_vector& vecSubRects, int areaSizeX, int areaSizeY )
|
||||
{
|
||||
areaSizeX/= scale;
|
||||
areaSizeY/= scale;
|
||||
areaSizeX /= scale;
|
||||
areaSizeY /= scale;
|
||||
|
||||
// Sort the subRects based on dimensions, larger dimension goes first.
|
||||
std::sort( vecSubRects.begin(), vecSubRects.end(), CRectPlacement::TRect::Greater );
|
||||
rectpack2D::rect_wh result;
|
||||
|
||||
// gives the initial size to the area
|
||||
aPlacementArea.Init( areaSizeX, areaSizeY );
|
||||
int max_side = std::max( areaSizeX, areaSizeY );
|
||||
|
||||
// Add all subrects
|
||||
CSubRectArray::iterator it;
|
||||
|
||||
for( it = vecSubRects.begin(); it != vecSubRects.end(); )
|
||||
while( true )
|
||||
{
|
||||
CRectPlacement::TRect r( 0, 0, it->w, it->h );
|
||||
bool anyUnsuccessful = false;
|
||||
const int discard_step = 1;
|
||||
|
||||
bool bPlaced = aPlacementArea.AddAtEmptySpotAutoGrow( &r, areaSizeX, areaSizeY );
|
||||
|
||||
if( !bPlaced ) // No room to place the rectangle: enlarge area and retry
|
||||
auto report_successful = [&]( rect_type& )
|
||||
{
|
||||
bool retry = false;
|
||||
return rectpack2D::callback_result::CONTINUE_PACKING;
|
||||
};
|
||||
|
||||
if( areaSizeX < INT_MAX/2 )
|
||||
auto report_unsuccessful = [&]( rect_type& r )
|
||||
{
|
||||
retry = true;
|
||||
areaSizeX = areaSizeX * 1.2;
|
||||
}
|
||||
anyUnsuccessful = true;
|
||||
return rectpack2D::callback_result::ABORT_PACKING;
|
||||
};
|
||||
|
||||
if( areaSizeX < INT_MAX/2 )
|
||||
{
|
||||
retry = true;
|
||||
areaSizeY = areaSizeY * 1.2;
|
||||
}
|
||||
result = rectpack2D::find_best_packing<spaces_type>(
|
||||
vecSubRects,
|
||||
make_finder_input( max_side, discard_step, report_successful, report_unsuccessful,
|
||||
rectpack2D::flipping_option::DISABLED ) );
|
||||
|
||||
if( retry )
|
||||
if( anyUnsuccessful )
|
||||
{
|
||||
aPlacementArea.Init( areaSizeX, areaSizeY );
|
||||
it = vecSubRects.begin();
|
||||
max_side = (int) ( max_side * 1.2 );
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// 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++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void moveFootprintsInArea( CRectPlacement& aPlacementArea, std::vector<FOOTPRINT*>& aFootprintList,
|
||||
const BOX2I& aFreeArea, bool aFindAreaOnly )
|
||||
void SpreadFootprints( std::vector<FOOTPRINT*>* aFootprints, VECTOR2I aTargetBoxPosition,
|
||||
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 );
|
||||
spreadRectangles( aPlacementArea, vecSubRects, aFreeArea.GetWidth(), aFreeArea.GetHeight() );
|
||||
|
||||
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;
|
||||
std::map<wxString, SheetBBoxToFootprintsMapPair> sheetsMap;
|
||||
std::vector<BOX2I> blockMap;
|
||||
|
||||
// Fill in the maps
|
||||
for( FOOTPRINT* footprint : *aFootprints )
|
||||
{
|
||||
if( footprint->IsLocked() )
|
||||
continue;
|
||||
wxString path =
|
||||
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() )
|
||||
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++ )
|
||||
for( auto& [sheetPath, sheetPair] : sheetsMap )
|
||||
{
|
||||
int subareaIdx = 0;
|
||||
footprintListBySheet.clear();
|
||||
subsurface = 0.0;
|
||||
auto& [sheet_bbox, sizeToFpMap] = sheetPair;
|
||||
|
||||
int fp_max_width = 0;
|
||||
int fp_max_height = 0;
|
||||
|
||||
for( unsigned ii = 0; ii < footprintList.size(); ii++ )
|
||||
for( auto& [fpSize, fpPair] : sizeToFpMap )
|
||||
{
|
||||
FOOTPRINT* footprint = footprintList[ii];
|
||||
bool islastItem = false;
|
||||
auto& [block_bbox, footprints] = fpPair;
|
||||
|
||||
if( ii == footprintList.size() - 1 ||
|
||||
( footprintList[ii]->GetPath().AsString().BeforeLast( '/' ) !=
|
||||
footprintList[ii+1]->GetPath().AsString().BeforeLast( '/' ) ) )
|
||||
islastItem = true;
|
||||
// Find optimal arrangement of same-size footprints
|
||||
|
||||
footprintListBySheet.push_back( footprint );
|
||||
subsurface += footprint->GetArea( PADDING );
|
||||
double blockEstimateArea = (double) fpSize.x * fpSize.y * footprints.size();
|
||||
double initialSide = std::sqrt( blockEstimateArea );
|
||||
bool vertical = fpSize.x >= fpSize.y;
|
||||
|
||||
// Calculate min size of placement area:
|
||||
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() );
|
||||
int initialCountPerLine = footprints.size();
|
||||
|
||||
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
|
||||
// calculate placement of the current sublist
|
||||
BOX2I freeArea;
|
||||
int Xsize_allowed = (int) ( sqrt( subsurface ) * 4.0 / 3.0 );
|
||||
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 )
|
||||
if( ( fpSize.y * footprints.size() / fpSize.x ) > singleLineRatio )
|
||||
initialCountPerLine = initialSide / fpSize.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
VECTOR2I areapos =
|
||||
placementSheetAreas[subareaIdx].GetOrigin()
|
||||
+ aSpreadAreaPosition;
|
||||
freeArea.SetOrigin( areapos );
|
||||
if( ( fpSize.x * footprints.size() / fpSize.y ) > singleLineRatio )
|
||||
initialCountPerLine = initialSide / fpSize.x;
|
||||
}
|
||||
|
||||
bool findAreaOnly = pass == 0;
|
||||
moveFootprintsInArea( placementArea, footprintListBySheet, freeArea, findAreaOnly );
|
||||
int optimalCountPerLine = initialCountPerLine;
|
||||
int optimalRemainder = footprints.size() % optimalCountPerLine;
|
||||
|
||||
if( pass == 0 )
|
||||
if( optimalRemainder != 0 )
|
||||
{
|
||||
// Populate sheet placement areas list
|
||||
BOX2I 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( pcbIUScale.mmToIU( 1.5 ) );
|
||||
for( int i = std::max( 2, initialCountPerLine - 2 );
|
||||
i <= std::min( (int) footprints.size() - 2, initialCountPerLine + 2 ); i++ )
|
||||
{
|
||||
int r = footprints.size() % i;
|
||||
|
||||
placementSheetAreas.push_back( sub_area );
|
||||
|
||||
placementsurface += (double) sub_area.GetWidth()*
|
||||
sub_area.GetHeight();
|
||||
if( r == 0 || r >= optimalRemainder )
|
||||
{
|
||||
optimalCountPerLine = i;
|
||||
optimalRemainder = r;
|
||||
}
|
||||
|
||||
// Prepare buffers for next sheet
|
||||
subsurface = 0.0;
|
||||
footprintListBySheet.clear();
|
||||
subareaIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
// End of pass:
|
||||
// At the end of the first pass, we have to find position of each sheet
|
||||
// placement area
|
||||
if( pass == 0 )
|
||||
std::sort( footprints.begin(), footprints.end(), compareFootprintsbyRef );
|
||||
|
||||
// Arrange footprints in rows or columns (blocks)
|
||||
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 )
|
||||
Xsize_allowed = INT_MAX/2;
|
||||
VECTOR2I position = fpSize / 2;
|
||||
|
||||
int Ysize_allowed = (int) ( placementsurface / Xsize_allowed );
|
||||
|
||||
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 )
|
||||
if( vertical )
|
||||
{
|
||||
TSubRect& srect = vecSubRects[it];
|
||||
VECTOR2I pos( srect.x * scale, srect.y * scale );
|
||||
VECTOR2I size( srect.w * scale, srect.h * scale );
|
||||
position.x += fpSize.x * ( i / optimalCountPerLine );
|
||||
position.y += fpSize.y * ( i % optimalCountPerLine );
|
||||
}
|
||||
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
|
||||
// are better than out of screen components
|
||||
if( (uint64_t)pos.x + (uint64_t)size.x > INT_MAX/2 )
|
||||
pos.x = 0;
|
||||
if( (uint64_t) target_pos.x + (uint64_t) target_size.x > INT_MAX / 2 )
|
||||
target_pos.x -= INT_MAX / 2;
|
||||
|
||||
if( (uint64_t)pos.y + (uint64_t)size.y > INT_MAX/2 )
|
||||
pos.y = 0;
|
||||
if( (uint64_t) target_pos.y + (uint64_t) target_size.y > INT_MAX / 2 )
|
||||
target_pos.y -= INT_MAX / 2;
|
||||
|
||||
placementSheetAreas[srect.n].SetOrigin( pos );
|
||||
placementSheetAreas[srect.n].SetSize( size );
|
||||
for( FOOTPRINT* footprint : footprints )
|
||||
{
|
||||
footprint->Move( target_pos - src_bbox.GetPosition() );
|
||||
sheet_bbox.Merge( footprint->GetBoundingBox( false, false ) );
|
||||
}
|
||||
|
||||
block_i++;
|
||||
}
|
||||
}
|
||||
} // End pass
|
||||
}
|
||||
|
||||
rect_vector vecSubRects;
|
||||
long long sheetsArea = 0;
|
||||
|
||||
// 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();
|
||||
// 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 <board.h>
|
||||
#include <footprint.h>
|
||||
#include <spread_footprints.h>
|
||||
#include <ratsnest/ratsnest_data.h>
|
||||
#include <io_mgr.h>
|
||||
#include "board_netlist_updater.h"
|
||||
|
@ -46,9 +47,6 @@ using namespace std::placeholders;
|
|||
#include <wx/msgdlg.h>
|
||||
|
||||
|
||||
extern void SpreadFootprints( std::vector<FOOTPRINT*>* aFootprints, VECTOR2I aSpreadAreaPosition );
|
||||
|
||||
|
||||
bool PCB_EDIT_FRAME::ReadNetlistFromFile( const wxString &aFilename, NETLIST& aNetlist,
|
||||
REPORTER& aReporter )
|
||||
{
|
||||
|
@ -96,11 +94,10 @@ void PCB_EDIT_FRAME::OnNetlistChanged( BOARD_NETLIST_UPDATER& aUpdater, bool* aR
|
|||
|
||||
// Spread new footprints.
|
||||
std::vector<FOOTPRINT*> newFootprints = aUpdater.GetAddedFootprints();
|
||||
VECTOR2I areaPosition = GetCanvas()->GetViewControls()->GetCursorPosition();
|
||||
|
||||
GetToolManager()->RunAction( PCB_ACTIONS::selectionClear, true );
|
||||
|
||||
SpreadFootprints( &newFootprints, areaPosition );
|
||||
SpreadFootprints( &newFootprints, { 0, 0 }, true );
|
||||
|
||||
// Start drag command for new footprints
|
||||
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 );
|
||||
|
||||
*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 );
|
||||
|
|
|
@ -204,6 +204,8 @@ bool EDIT_TOOL::Init()
|
|||
menu.AddItem( PCB_ACTIONS::mirrorH, SELECTION_CONDITIONS::NotEmpty );
|
||||
menu.AddItem( PCB_ACTIONS::mirrorV, SELECTION_CONDITIONS::NotEmpty );
|
||||
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 );
|
||||
|
||||
|
@ -2258,6 +2260,7 @@ void EDIT_TOOL::setTransitions()
|
|||
Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorH.MakeEvent() );
|
||||
Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorV.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::FilletTracks, PCB_ACTIONS::filletTracks.MakeEvent() );
|
||||
|
||||
|
|
|
@ -121,6 +121,11 @@ public:
|
|||
*/
|
||||
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 );
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <kiway.h>
|
||||
#include <array_creator.h>
|
||||
#include <pcbnew_settings.h>
|
||||
#include <spread_footprints.h>
|
||||
#include <status_popup.h>
|
||||
#include <tool/selection_conditions.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 )
|
||||
{
|
||||
if( isRouterActive() )
|
||||
|
|
|
@ -342,6 +342,13 @@ TOOL_ACTION PCB_ACTIONS::swap( "pcbnew.InteractiveEdit.swap",
|
|||
_( "Swap" ), _( "Swaps selected items' positions" ),
|
||||
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",
|
||||
AS_GLOBAL, 0, "",
|
||||
_( "Change Track Width" ), _( "Updates selected track & via sizes" ) );
|
||||
|
|
|
@ -132,6 +132,9 @@ public:
|
|||
/// Swapping of selected items
|
||||
static TOOL_ACTION swap;
|
||||
|
||||
/// Pack and start moving selected footprints
|
||||
static TOOL_ACTION packAndMoveFootprints;
|
||||
|
||||
/// Update selected tracks & vias to the current track & via dimensions
|
||||
static TOOL_ACTION changeTrackWidth;
|
||||
|
||||
|
|
|
@ -383,6 +383,7 @@ set( BMAPS_MID
|
|||
options_schematic
|
||||
opt_show_polygon
|
||||
ortho
|
||||
pack_footprints
|
||||
pad_sketch
|
||||
pad
|
||||
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