GenCAD exporter: generate shapes per footprint and reuse them

This commit is contained in:
Maciej Suminski 2017-10-09 11:50:06 +02:00
parent 944c5481fe
commit 019dca38c4
3 changed files with 84 additions and 9 deletions

View File

@ -128,7 +128,8 @@ void DIALOG_GENCAD_EXPORT_OPTIONS::createOptCheckboxes()
std::map<GENCAD_EXPORT_OPT, wxString> opts =
{
{ FLIP_BOTTOM_PADS, _( "Flip bottom components padstacks" ) },
{ UNIQUE_PIN_NAMES, _( "Generate unique pin names" ) }
{ UNIQUE_PIN_NAMES, _( "Generate unique pin names" ) },
{ INDIVIDUAL_SHAPES, _( "Generate a new shape for each component (do not reuse shapes)" ) }
};
for( const auto& option : opts )

View File

@ -34,7 +34,8 @@ class wxTextCtrl;
enum GENCAD_EXPORT_OPT
{
FLIP_BOTTOM_PADS, // flip bottom components padstacks geometry
UNIQUE_PIN_NAMES // generate unique pin names
UNIQUE_PIN_NAMES, // generate unique pin names
INDIVIDUAL_SHAPES // generate a shape for each component
};

View File

@ -47,6 +47,7 @@
#include <class_track.h>
#include <class_edge_mod.h>
#include <hash_eda.h>
static bool CreateHeaderInfoData( FILE* aFile, PCB_EDIT_FRAME* frame );
static void CreateArtworksSection( FILE* aFile );
@ -58,7 +59,7 @@ static void CreateRoutesSection( FILE* aFile, BOARD* aPcb );
static void CreateSignalsSection( FILE* aFile, BOARD* aPcb );
static void CreateShapesSection( FILE* aFile, BOARD* aPcb );
static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb );
static void FootprintWriteShape( FILE* File, MODULE* module );
static void FootprintWriteShape( FILE* File, MODULE* module, const wxString& aShapeName );
// layer names for Gencad export
@ -221,12 +222,16 @@ static std::string fmt_mask( LSET aSet )
// These are the export origin (the auxiliary axis)
static int GencadOffsetX, GencadOffsetY;
// Association between shapes and components
static std::map<MODULE*, wxString> m_componentShapes;
// GerbTool chokes on units different than INCH so this is the conversion factor
const static double SCALE_FACTOR = 1000.0 * IU_PER_MILS;
// Export options
bool flipBottomPads;
bool uniquePins;
bool individualShapes;
/* Two helper functions to calculate coordinates of modules in gencad values
* (GenCAD Y axis from bottom to top)
@ -263,6 +268,7 @@ void PCB_EDIT_FRAME::ExportToGenCAD( wxCommandEvent& aEvent )
// Get options
flipBottomPads = optionsDialog.GetOption( FLIP_BOTTOM_PADS );
uniquePins = optionsDialog.GetOption( UNIQUE_PIN_NAMES );
individualShapes = optionsDialog.GetOption( INDIVIDUAL_SHAPES );
// Switch the locale to standard C (needed to print floating point numbers)
LOCALE_IO toggle;
@ -679,6 +685,23 @@ static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb )
}
/// Compute hashes for modules without taking into account their position, rotation or layer
static size_t hashModule( const MODULE* aModule )
{
size_t ret = 0x11223344;
constexpr int flags = HASH_FLAGS::POSITION | HASH_FLAGS::REL_COORD
| HASH_FLAGS::ROTATION | HASH_FLAGS::LAYER;
for( const BOARD_ITEM* i = aModule->GraphicalItemsList(); i; i = i->Next() )
ret ^= hash_eda( i, flags );
for( const D_PAD* i = aModule->PadsList(); i; i = i->Next() )
ret ^= hash_eda( i, flags );
return ret;
}
/* Creates the footprint shape list.
* Since module shape is customizable after the placement we cannot share them;
* instead we opt for the one-module-one-shape-one-component-one-device approach
@ -690,15 +713,65 @@ static void CreateShapesSection( FILE* aFile, BOARD* aPcb )
const char* layer;
wxString pinname;
const char* mirror = "0";
std::map<wxString, size_t> shapes;
fputs( "$SHAPES\n", aFile );
for( module = aPcb->m_Modules; module; module = module->Next() )
{
// already emitted pins to check for duplicates
std::set<wxString> pins;
if( !individualShapes )
{
// Check if such shape has been already generated, and if so - reuse it
// It is necessary to compute hash (i.e. check all children objects) as
// certain components instances might have been modified on the board.
// In such case the shape will be different despite the same LIB_ID.
wxString shapeName = module->GetFPID().Format();
shapeName.Replace( " ", "_" );
FootprintWriteShape( aFile, module );
auto shapeIt = shapes.find( shapeName );
size_t modHash = hashModule( module );
if( shapeIt != shapes.end() )
{
if( modHash != shapeIt->second )
{
// there is an entry for this footprint, but it has a modified shape,
// so we need to create a new entry
wxString newShapeName;
int suffix = 0;
// find an unused name or matching entry
do
{
newShapeName = wxString::Format( "%s_%d", shapeName, suffix );
shapeIt = shapes.find( newShapeName );
++suffix;
}
while( shapeIt != shapes.end() && shapeIt->second != modHash );
shapeName = newShapeName;
}
if( shapeIt != shapes.end() && modHash == shapeIt->second )
{
// shape found, so reuse it
m_componentShapes[module] = shapeName;
continue;
}
}
// output and store the hashed shape in the map
m_componentShapes[module] = shapeName;
shapes[shapeName] = modHash;
FootprintWriteShape( aFile, module, shapeName );
}
else // individual shape for each component
{
FootprintWriteShape( aFile, module, module->GetReference() );
}
// set of already emitted pins to check for duplicates
std::set<wxString> pins;
for( pad = module->PadsList(); pad; pad = pad->Next() )
{
@ -788,7 +861,7 @@ static void CreateComponentsSection( FILE* aFile, BOARD* aPcb )
fprintf( aFile, "ROTATION %g\n",
fp_orient / 10.0 );
fprintf( aFile, "SHAPE %s %s %s\n",
TO_UTF8( module->GetReference() ),
TO_UTF8( individualShapes ? module->GetReference() : m_componentShapes[module] ),
mirror, flip );
// Text on silk layer: RefDes and value (are they actually useful?)
@ -1182,13 +1255,13 @@ static void CreateTracksInfoData( FILE* aFile, BOARD* aPcb )
* It's almost guaranteed that the silk layer will be imported wrong but
* the shape also contains the pads!
*/
static void FootprintWriteShape( FILE* aFile, MODULE* module )
static void FootprintWriteShape( FILE* aFile, MODULE* module, const wxString& aShapeName )
{
EDGE_MODULE* PtEdge;
EDA_ITEM* PtStruct;
/* creates header: */
fprintf( aFile, "\nSHAPE %s\n", TO_UTF8( module->GetReference() ) );
fprintf( aFile, "\nSHAPE %s\n", TO_UTF8( aShapeName ) );
if( module->GetAttributes() & MOD_VIRTUAL )
{