diff --git a/pcbnew/dsn.cpp b/pcbnew/dsn.cpp index ec4250fa9c..7da1a6f326 100644 --- a/pcbnew/dsn.cpp +++ b/pcbnew/dsn.cpp @@ -737,7 +737,7 @@ L_read: goto exit; } - // else it was something like +5V, reset head back + // else it was something like +5V, fall through below } // a quoted string diff --git a/pcbnew/specctra.cpp b/pcbnew/specctra.cpp index f56d24603f..aadcd0c413 100644 --- a/pcbnew/specctra.cpp +++ b/pcbnew/specctra.cpp @@ -212,7 +212,7 @@ void SPECCTRA_DB::readTIME( time_t* time_stamp ) throw( IOError ) const char* ptok = lexer->CurText(); - mytime.tm_mon = 0; // remains of we don't find a month match. + mytime.tm_mon = 0; // remains if we don't find a month match. for( int m=0; months[m]; ++m ) { if( !stricmp( months[m], ptok ) ) @@ -1985,7 +1985,7 @@ void SPECCTRA_DB::doSHAPE( SHAPE* growth ) throw( IOError ) case T_polygon: case T_qarc: L_done_that: - if( growth->rectangle || growth->circle || growth->path || growth->qarc ) + if( growth->Length() ) unexpected( tok ); break; default: @@ -2000,24 +2000,32 @@ L_done_that: switch( tok ) { case T_rect: - growth->rectangle = new RECTANGLE( growth ); - doRECTANGLE( growth->rectangle ); + RECTANGLE* rectangle; + rectangle = new RECTANGLE( growth ); + growth->Append( rectangle ); + doRECTANGLE( rectangle ); break; case T_circle: - growth->circle = new CIRCLE( growth ); - doCIRCLE( growth->circle ); + CIRCLE* circle; + circle = new CIRCLE( growth ); + growth->Append( circle ); + doCIRCLE( circle ); break; case T_path: case T_polygon: - growth->path = new PATH( growth, tok ); - doPATH( growth->path ); + PATH* path; + path = new PATH( growth, tok ); + growth->Append( path ); + doPATH( path ); break; case T_qarc: - growth->qarc = new QARC( growth ); - doQARC( growth->qarc ); + QARC* qarc; + qarc = new QARC( growth ); + growth->Append( qarc ); + doQARC( qarc ); break; case T_connect: @@ -3650,16 +3658,16 @@ int main( int argc, char** argv ) { // wxString filename( wxT("/tmp/fpcroute/Sample_1sided/demo_1sided.dsn") ); // wxString filename( wxT("/tmp/testdesigns/test.dsn") ); - wxString filename( wxT("/tmp/testdesigns/test.ses") ); -// wxString filename( wxT("/tmp/specctra_big.dsn") ); +// wxString filename( wxT("/tmp/testdesigns/test.ses") ); + wxString filename( wxT("/tmp/specctra_big.dsn") ); SPECCTRA_DB db; bool failed = false; try { -// db.LoadPCB( filename ); - db.LoadSESSION( filename ); + db.LoadPCB( filename ); +// db.LoadSESSION( filename ); } catch( IOError ioe ) { @@ -3672,8 +3680,8 @@ int main( int argc, char** argv ) // export what we read in, making this test program basically a beautifier - db.ExportSESSION( wxT("/tmp/export.ses") ); -// db.ExportPCB( wxT("/tmp/export.dsn") ); +// db.ExportSESSION( wxT("/tmp/export.ses") ); + db.ExportPCB( wxT("/tmp/export.dsn") ); } diff --git a/pcbnew/specctra.h b/pcbnew/specctra.h index 109947b895..5b9bf41364 100644 --- a/pcbnew/specctra.h +++ b/pcbnew/specctra.h @@ -111,6 +111,11 @@ struct POINT POINT() { x=0.0; y=0.0; } + POINT( double aX, double aY ) : + x(aX), y(aY) + { + } + bool operator==( const POINT& other ) const { return x==other.x && y==other.y; @@ -406,6 +411,17 @@ public: ELEM( T_rect, aParent ) { } + + void SetLayerId( const char* aLayerId ) + { + layer_id = aLayerId; + } + + void SetCorners( const POINT& aPoint0, const POINT& aPoint1 ) + { + point0 = aPoint0; + point1 = aPoint1; + } void Format( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError ) { @@ -439,21 +455,22 @@ public: void Format( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError ) { - out->Print( nestLevel, "(%s ", LEXER::GetTokenText( Type() ) ); + out->Print( nestLevel, "(%s", LEXER::GetTokenText( Type() ) ); bool singleLine; if( rules.size() == 1 ) { singleLine = true; - out->Print( 0, "%s)", rules.begin()->c_str() ); + out->Print( 0, " %s)", rules.begin()->c_str() ); } else { + out->Print( 0, "\n" ); singleLine = false; for( STRINGS::const_iterator i = rules.begin(); i!=rules.end(); ++i ) - out->Print( nestLevel, "%s\n", i->c_str() ); + out->Print( nestLevel+1, "%s\n", i->c_str() ); out->Print( nestLevel, ")" ); } @@ -671,6 +688,16 @@ public: quote, layer_id.c_str(), quote, diameter, vertex.x, vertex.y ); } + + void SetLayerId( const char* aLayerId ) + { + layer_id = aLayerId; + } + + void SetDiameter( double aDiameter ) + { + diameter = aDiameter; + } }; @@ -870,28 +897,42 @@ public: void Format( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError ) { - out->Print( nestLevel, "(%s\n", LEXER::GetTokenText( Type() ) ); + const int RIGHTMARGIN = 80; + int perLine = out->Print( nestLevel, "(%s", LEXER::GetTokenText( Type() ) ); for( STRINGS::iterator i=padstacks.begin(); i!=padstacks.end(); ++i ) { + if( perLine > RIGHTMARGIN ) + { + out->Print( 0, "\n" ); + perLine = out->Print( nestLevel+1, "%s", ""); + } + const char* quote = out->GetQuoteChar( i->c_str() ); - out->Print( nestLevel+1, "%s%s%s\n", quote, i->c_str(), quote ); + perLine += out->Print( 0, " %s%s%s", quote, i->c_str(), quote ); } if( spares.size() ) { - out->Print( nestLevel+1, "(spare\n" ); + out->Print( 0, "\n" ); + + perLine = out->Print( nestLevel+1, "(spare" ); for( STRINGS::iterator i=spares.begin(); i!=spares.end(); ++i ) { + if( perLine > RIGHTMARGIN ) + { + out->Print( 0, "\n" ); + perLine = out->Print( nestLevel+2, "%s", ""); + } const char* quote = out->GetQuoteChar( i->c_str() ); - out->Print( nestLevel+2, "%s%s%s\n", quote, i->c_str(), quote ); + perLine += out->Print( 0, " %s%s%s", quote, i->c_str(), quote ); } - out->Print( nestLevel+1, ")\n" ); + out->Print( 0, ")" ); } - out->Print( nestLevel, ")\n" ); + out->Print( 0, ")\n" ); } }; @@ -1600,55 +1641,34 @@ public: }; -class SHAPE : public ELEM +class SHAPE : public ELEM_HOLDER { friend class SPECCTRA_DB; DSN_T connect; - //----- only one of these is used, like a union ----- + /*----- only one of these is used, like a union ----- + single item, but now in the kids list + PATH* path; ///< used for both path and polygon RECTANGLE* rectangle; CIRCLE* circle; QARC* qarc; - //--------------------------------------------------- + //--------------------------------------------------- */ WINDOWS windows; public: SHAPE( ELEM* aParent, DSN_T aType = T_shape ) : - ELEM( aType, aParent ) + ELEM_HOLDER( aType, aParent ) { connect = T_on; - - path = 0; - rectangle = 0; - circle = 0; - qarc = 0; - } - - ~SHAPE() - { - delete path; - delete rectangle; - delete circle; - delete qarc; } void FormatContents( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError ) { - if( path ) - path->Format( out, nestLevel ); - - else if( rectangle ) - rectangle->Format( out, nestLevel ); - - else if( circle ) - circle->Format( out, nestLevel ); - - else if( qarc ) - qarc->Format( out, nestLevel ); + ELEM_HOLDER::FormatContents( out, nestLevel ); if( connect == T_off ) out->Print( nestLevel, "(connect %s)\n", LEXER::GetTokenText( connect ) ); @@ -1817,6 +1837,11 @@ public: delete rules; } + void SetPadstackId( const char* aPadstackId ) + { + padstack_id = aPadstackId; + } + void Format( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError ) { const char* quote = out->GetQuoteChar( padstack_id.c_str() ); @@ -1865,6 +1890,7 @@ public: return ELEM::GetUnits(); } }; +typedef boost::ptr_vector PADSTACKS; /** @@ -1882,7 +1908,6 @@ class LIBRARY : public ELEM typedef boost::ptr_vector IMAGES; IMAGES images; - typedef boost::ptr_vector PADSTACKS; PADSTACKS padstacks; public: @@ -1916,6 +1941,11 @@ public: return ELEM::GetUnits(); } + + void AddPadstack( PADSTACK* aPadstack ) + { + padstacks.push_back( aPadstack ); + } }; @@ -2215,35 +2245,32 @@ public: void Format( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError ) { - const char* quote = out->GetQuoteChar( class_id.c_str() ); - out->Print( nestLevel, "(%s %s%s%s", LEXER::GetTokenText( Type() ), - quote, class_id.c_str(), quote ); - - const int NETGAP = 2; - const int RIGHTMARGIN = 92; + const int RIGHTMARGIN = 80; - int perRow=RIGHTMARGIN; + const char* quote = out->GetQuoteChar( class_id.c_str() ); + + int perLine = out->Print( nestLevel, "(%s %s%s%s", + LEXER::GetTokenText( Type() ), + quote, class_id.c_str(), quote ); + for( STRINGS::iterator i=net_ids.begin(); i!=net_ids.end(); ++i ) { - quote = out->GetQuoteChar( i->c_str() ); - int slength = strlen( i->c_str() ); - if( *quote!='\0' ) - slength += 2; - - if( perRow + slength + NETGAP > RIGHTMARGIN ) + if( perLine > RIGHTMARGIN ) { out->Print( 0, "\n" ); - perRow = 0; - perRow += out->Print( nestLevel+1, "%s%s%s", - quote, i->c_str(), quote ); - } - else - { - perRow += out->Print( 0, "%*c%s%s%s", NETGAP, ' ', - quote, i->c_str(), quote ); + perLine = out->Print( nestLevel+1, "%s", "" ); } + + quote = out->GetQuoteChar( i->c_str() ); + perLine += out->Print( 0, " %s%s%s", quote, i->c_str(), quote ); + } + + bool newLine = false; + if( circuit.size() || layer_rules.size() || topology ) + { + out->Print( 0, "\n" ); + newLine = true; } - out->Print( 0, "\n" ); for( STRINGS::iterator i=circuit.begin(); i!=circuit.end(); ++i ) out->Print( nestLevel+1, "%s\n", i->c_str() ); @@ -2254,7 +2281,7 @@ public: if( topology ) topology->Format( out, nestLevel+1 ); - out->Print( nestLevel, ")\n" ); + out->Print( newLine ? nestLevel : 0, ")\n" ); } }; @@ -2444,6 +2471,9 @@ public: perLine += out->Print( 0, "%.6g %.6g", i->x, i->y ); } + if( net_id.size() || via_number!=-1 || type!=T_NONE || attr!=T_NONE || supply) + out->Print( 0, " " ); + if( net_id.size() ) { if( perLine > RIGHTMARGIN ) @@ -3183,13 +3213,6 @@ class SPECCTRA_DB : public OUTPUTFORMATTER void doSUPPLY_PIN( SUPPLY_PIN* growth ) throw( IOError ); - /** - * Function exportEdges - * exports the EDGES_N layer of the board. - */ - void exportEdges( BOARD* aBoard ) throw( IOError ); - - public: SPECCTRA_DB() @@ -3288,7 +3311,17 @@ public: /** * Function FromBOARD - * adds the entire BOARD to the PCB but does not write it out. + * adds the entire BOARD to the PCB but does not write it out. Note that + * the BOARD given to this function must have all the MODULEs on the component + * side of the BOARD. + * + * See void WinEDA_PcbFrame::ExportToSPECCTRA( wxCommandEvent& event ) + * for how this can be done before calling this function. + * @todo + * I would have liked to put the flipping logic into the ExportToSPECCTRA() + * function directly, but for some strange reason, + * void Change_Side_Module( MODULE* Module, wxDC* DC ) is a member of + * of class WinEDA_BasePcbFrame rather than class BOARD. * * @param aBoard The BOARD to convert to a PCB. * @throw IOError, if the BOARD cannot be converted, and the text of the diff --git a/pcbnew/specctra_export.cpp b/pcbnew/specctra_export.cpp index 9d71d83836..da6f5389bc 100644 --- a/pcbnew/specctra_export.cpp +++ b/pcbnew/specctra_export.cpp @@ -34,6 +34,7 @@ #include "specctra.h" #include "collectors.h" +#include "wxPcbStruct.h" // Change_Side_Module() using namespace DSN; @@ -61,29 +62,61 @@ void WinEDA_PcbFrame::ExportToSPECCTRA( wxCommandEvent& event ) SPECCTRA_DB db; + bool ok = true; + wxString errorText; db.SetPCB( SPECCTRA_DB::MakePCB() ); + + + // DSN Images (=Kicad MODULES and pads) must be presented from the + // top view. So we temporarily flip any modules which are on the back + // side of the board to the front, and record this in the MODULE's flag field. + for( MODULE* module = m_Pcb->m_Modules; module; module = module->Next() ) + { + module->flag = 0; + if( module->GetLayer() == COPPER_LAYER_N ) + { + Change_Side_Module( module, NULL ); + module->flag = 1; + } + } try { db.FromBOARD( m_Pcb ); db.ExportPCB( fullFileName, true ); - - // if an exception is thrown by FromBOARD or Export(), then + + // if an exception is thrown by FromBOARD or ExportPCB(), then // ~SPECCTRA_DB() will close the file. } catch ( IOError ioe ) { - DisplayError( this, ioe.errorText ); - return; + ok = false; + + // display no messages until we flip back the modules below. + errorText = ioe.errorText; + } + + // DSN Images (=Kicad MODULES and pads) must be presented from the + // top view. Restore those that were flipped. + for( MODULE* module = m_Pcb->m_Modules; module; module = module->Next() ) + { + if( module->flag ) + { + Change_Side_Module( module, NULL ); + module->flag = 0; + } } - // @todo display a message saying the export is complete. + if( ok ) + { + // @todo display a message saying the export is complete. + } + else + DisplayError( this, errorText ); } - - namespace DSN { struct POINT_PAIR @@ -103,6 +136,22 @@ static inline void swap( POINT_PAIR& pair ) } +static inline double scale( int kicadDist ) +{ + return kicadDist/10.0; +} + +static inline double mapX( int x ) +{ + return scale(x); +} + +static inline double mapY( int y ) +{ + return -scale(y); // make y negative, since it is increasing going down. +} + + /** * Function mapPt * converts a Kicad point into a DSN file point. Kicad's BOARD coordinates @@ -112,8 +161,8 @@ static inline void swap( POINT_PAIR& pair ) static POINT mapPt( const wxPoint& pt ) { POINT ret; - ret.x = pt.x / 10.0; - ret.y = -pt.y /10.0; // make y negative, since it is increasing going down. + ret.x = mapX( pt.x ); + ret.y = mapY( pt.y ); return ret; } @@ -172,8 +221,199 @@ static bool isRectangle( POINT_PAIRS& aList ) } -void SPECCTRA_DB::exportEdges( BOARD* aBoard ) throw( IOError ) +/**************************************************************************/ +static int Pad_list_Sort_by_Shapes( const void* refptr, const void* objptr ) +/**************************************************************************/ { + const D_PAD* padref = *(D_PAD**)refptr; + const D_PAD* padcmp = *(D_PAD**)objptr; + + return D_PAD::Compare( padref, padcmp ); +} + + +/** + * Function makePADSTACKs + * makes all the PADSTACKs, and marks each D_PAD with the index into the + * LIBRARY::padstacks list that it matches. + */ +static void makePADSTACKs( BOARD* aBoard, TYPE_COLLECTOR& aPads, + LIBRARY* aLibrary, PADSTACKS& aPadstacks ) +{ + if( aPads.GetCount() ) + { + qsort( (void*) aPads.BasePtr(), aPads.GetCount(), sizeof(D_PAD*), Pad_list_Sort_by_Shapes ); + } + + D_PAD* old_pad = NULL; + int padstackNdx = 0; + + for( int i=0; im_logical_connexion = padstackNdx; + + if( old_pad && 0==D_PAD::Compare( old_pad, pad ) ) + { + continue; + } + + old_pad = pad; + + // this is the index into the library->padstacks, be careful. + pad->m_logical_connexion = padstackNdx++; + + PADSTACK* padstack = new PADSTACK( aLibrary ); + SHAPE* shape = new SHAPE( padstack ); + padstack->Append( shape ); + + switch( pad->m_PadShape ) + { + default: + case PAD_CIRCLE: + { + CIRCLE* circle; + double diameter = scale(pad->m_Size.x); + int coppers = 0; + + if( pad->IsOnLayer( COPPER_LAYER_N ) ) + { + circle = new CIRCLE( shape ); + circle->SetLayerId( CONV_TO_UTF8(aBoard->GetLayerName( COPPER_LAYER_N )) ); + circle->SetDiameter( diameter ); + shape->Append( circle ); + ++coppers; + } + + if( pad->IsOnLayer( LAYER_CMP_N ) ) + { + circle = new CIRCLE( shape ); + circle->SetLayerId( CONV_TO_UTF8(aBoard->GetLayerName( LAYER_CMP_N )) ); + circle->SetDiameter( diameter ); + shape->Append( circle ); + ++coppers; + } + + char name[50]; + + snprintf( name, sizeof(name), "Round%dPad_%.6g_mil", coppers, scale(pad->m_Size.x) ); + + name[ sizeof(name)-1 ] = 0; + + // @todo verify that all pad names are unique, there is a chance that + // D_PAD::Compare() could say two pads are different, yet the get the same + // name here. If so, blend in the padNdx into the name. + + padstack->SetPadstackId( name ); + + aLibrary->AddPadstack( padstack ); + } + break; + + case PAD_RECT: + { + double dx = scale( pad->m_Size.x ) / 2.0; + double dy = scale( pad->m_Size.y ) / 2.0; + + RECTANGLE* rect; + int coppers = 0; + + if( pad->IsOnLayer( COPPER_LAYER_N ) ) + { + rect = new RECTANGLE( shape ); + rect->SetLayerId( CONV_TO_UTF8(aBoard->GetLayerName( COPPER_LAYER_N )) ); + rect->SetCorners( POINT(-dx,-dy), POINT(dx,dy) ); + shape->Append( rect ); + ++coppers; + } + + if( pad->IsOnLayer( LAYER_CMP_N ) ) + { + rect = new RECTANGLE( shape ); + rect->SetLayerId( CONV_TO_UTF8(aBoard->GetLayerName( LAYER_CMP_N )) ); + rect->SetCorners( POINT(-dx,-dy), POINT(dx,dy) ); + shape->Append( rect ); + ++coppers; + } + + char name[50]; + + snprintf( name, sizeof(name), "Rect%dPad_%.6gx%.6g_mil", + coppers, scale(pad->m_Size.x), scale(pad->m_Size.y) ); + + name[ sizeof(name)-1 ] = 0; + + // @todo verify that all pad names are unique, there is a chance that + // D_PAD::Compare() could say two pads are different, yet the get the same + // name here. If so, blend in the padNdx into the name. + + padstack->SetPadstackId( name ); + + aLibrary->AddPadstack( padstack ); + } + break; +#if 0 + pad_type = "RECTANGULAR"; + fprintf( file, " %s %d\n", pad_type, pad->m_Drill.x ); + fprintf( file, "RECTANGLE %d %d %d %d\n", + -dx + pad->m_Offset.x, -dy - pad->m_Offset.y, + dx + pad->m_Offset.x, -pad->m_Offset.y + dy ); + break; + + case PAD_OVAL: /* description du contour par 2 linges et 2 arcs */ + { + pad_type = "FINGER"; + fprintf( file, " %s %d\n", pad_type, pad->m_Drill.x ); + int dr = dx - dy; + if( dr >= 0 ) // ovale horizontal + { + int rayon = dy; + fprintf( file, "LINE %d %d %d %d\n", + -dr + pad->m_Offset.x, -pad->m_Offset.y - rayon, + dr + pad->m_Offset.x, -pad->m_Offset.y - rayon ); + fprintf( file, "ARC %d %d %d %d %d %d\n", + dr + pad->m_Offset.x, -pad->m_Offset.y - rayon, + dr + pad->m_Offset.x, -pad->m_Offset.y + rayon, + dr + pad->m_Offset.x, -pad->m_Offset.y ); + + fprintf( file, "LINE %d %d %d %d\n", + dr + pad->m_Offset.x, -pad->m_Offset.y + rayon, + -dr + pad->m_Offset.x, -pad->m_Offset.y + rayon ); + fprintf( file, "ARC %d %d %d %d %d %d\n", + -dr + pad->m_Offset.x, -pad->m_Offset.y + rayon, + -dr + pad->m_Offset.x, -pad->m_Offset.y - rayon, + -dr + pad->m_Offset.x, -pad->m_Offset.y ); + } + else // ovale vertical + { + dr = -dr; + int rayon = dx; + fprintf( file, "LINE %d %d %d %d\n", + -rayon + pad->m_Offset.x, -pad->m_Offset.y - dr, + -rayon + pad->m_Offset.x, -pad->m_Offset.y + dr ); + fprintf( file, "ARC %d %d %d %d %d %d\n", + -rayon + pad->m_Offset.x, -pad->m_Offset.y + dr, + rayon + pad->m_Offset.x, -pad->m_Offset.y + dr, + pad->m_Offset.x, -pad->m_Offset.y + dr ); + + fprintf( file, "LINE %d %d %d %d\n", + rayon + pad->m_Offset.x, -pad->m_Offset.y + dr, + rayon + pad->m_Offset.x, -pad->m_Offset.y - dr ); + fprintf( file, "ARC %d %d %d %d %d %d\n", + rayon + pad->m_Offset.x, -pad->m_Offset.y - dr, + -rayon + pad->m_Offset.x, -pad->m_Offset.y - dr, + pad->m_Offset.x, -pad->m_Offset.y - dr ); + } + break; + } + + case PAD_TRAPEZOID: + pad_type = "POLYGON"; + break; +#endif + } + } } @@ -223,13 +463,13 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) { swapEnds( ppairs ); - #if defined(DEBUG) +#if defined(DEBUG) for( unsigned i=0; iitem->Show( 0, std::cout ); } - #endif +#endif BOUNDARY* boundary = new BOUNDARY(0); @@ -303,6 +543,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) pcb->structure->layers.push_back( layer ); } } + //------------------------------------------------- { @@ -333,21 +574,50 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) pcb->structure->planes.push_back( plane ); } } + + // keepouts could go here, there are none in Kicad at this time. + // although COPPER_PLANEs probably will need them for the thru holes, etc. + // but in that case they are WINDOWs within the COPPER_PLANEs. + //----------------------------- { - // get all the DRAWSEGMENTS into 'items', then look for layer == EDGE_N, - // and those segments comprize the board's perimeter. - static const KICAD_T scanPADnVIAs[] = { TYPEPAD, TYPEVIA, EOT }; - items.Collect( aBoard, scanPADnVIAs ); + static const KICAD_T scanPADs[] = { TYPEPAD, EOT }; - for( int i=0; ilibrary, pcb->library->padstacks ); + + for( int p=0; pShow( 0, std::cout ); + +/* + static const KICAD_T scanMODULEs[] = { TYPEMODULE, EOT }; + + items.Collect( aBoard, scanMODULEs ); + + for( int m=0; mShow( 0, std::cout ); + MODULE* module = (MODULE*) items[m]; + + + // collate all the pads, and make a component. + for( int p=0; pShow( 0, std::cout );) + + // lookup and maybe add this pad to the padstack. + wxString padName = lookupPad( pcb->library->padstacks, pad ); + } } +*/ } - //------------------------------------------------------ { // Output the vias in the padstack list here, by name