/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2007-2008 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2007 Kicad Developers, see change_log.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 */ /* This source is a complement to specctra.cpp and implements the export to specctra dsn file format. The specification for the grammar of the specctra dsn file used to develop this code is given here: http://www.autotraxeda.com/docs/SPECCTRA/SPECCTRA.pdf Also see the comments at the top of the specctra.cpp file itself. */ #include "specctra.h" #include "collectors.h" #include "wxPcbStruct.h" // Change_Side_Module() #include "pcbstruct.h" // HISTORY_NUMBER #include "autorout.h" // NET_CODES_OK using namespace DSN; // see wxPcbStruct.h void WinEDA_PcbFrame::ExportToSPECCTRA( wxCommandEvent& event ) { wxString fullFileName = GetScreen()->m_FileName; wxString std_ext = wxT( ".dsn" ); wxString mask = wxT( "*" ) + std_ext; ChangeFileNameExt( fullFileName, std_ext ); fullFileName = EDA_FileSelector( _( "Specctra DSN file:" ), wxEmptyString, /* Chemin par defaut */ fullFileName, /* nom fichier par defaut */ std_ext, /* extension par defaut */ mask, /* Masque d'affichage */ this, wxFD_SAVE, FALSE ); if( fullFileName == wxEmptyString ) return; // prepare the EQUIPOTs if( !( m_Pcb->m_Status_Pcb & NET_CODES_OK ) ) { //m_Pcb->m_Status_Pcb &= ~(LISTE_PAD_OK); recalcule_pad_net_code(); } SPECCTRA_DB db; bool ok = true; wxString errorText; BASE_SCREEN* screen = GetScreen(); bool wasModified = screen->IsModify() && !screen->IsSave(); db.SetPCB( SPECCTRA_DB::MakePCB() ); try { db.FromBOARD( m_Pcb ); db.ExportPCB( fullFileName, true ); // if an exception is thrown by FromBOARD or ExportPCB(), then // ~SPECCTRA_DB() will close the file. } catch( IOError ioe ) { ok = false; // copy the error string to safe place, ioe is in this scope only. errorText = ioe.errorText; } // The two calls below to BOARD::Change_Side_Module(), both set the // modified flag, yet their actions cancel each other out, so it should // be ok to clear the modify flag. if( !wasModified ) screen->ClrModify(); if( ok ) { // @todo display a message saying the export is complete. } else DisplayError( this, errorText ); } namespace DSN { struct POINT_PAIR { POINT start; POINT end; BOARD_ITEM* item; ///< the item which has these points, TRACK or DRAWSEGMENT }; typedef std::vector POINT_PAIRS; const KICAD_T SPECCTRA_DB::scanPADs[] = { TYPEPAD, EOT }; static inline void swap( POINT_PAIR& pair ) { POINT temp = pair.start; pair.start = pair.end; pair.end = temp; } /** * Function scale * converts a distance from kicad units to our reported specctra dsn units: * 1/10000 inches (deci-mils) to mils. So the factor of 10 comes in. */ 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 * are in deci-mils (i.e. 1/10,000th of an inch) and we are exporting in units * of mils, so we have to divide by 10. */ static POINT mapPt( const wxPoint& pt ) { POINT ret; ret.x = mapX( pt.x ); ret.y = mapY( pt.y ); ret.FixNegativeZero(); return ret; } /** * Function findPOINT * searches the list of POINT_PAIRS for a matching end to the given POINT. * @return int - 0 if no match, or + one based index of a POINT_PAIR with a matching ".start", * or a - one based index of a POINT_PAIR with a matching ".end". */ static int findPOINT( const POINT& pt, const POINT_PAIR source[], int count ) { for( int i=0; istart, &source[1], source.size()-1 ) != 0 ) swap( *source.begin() ); // swap start and end of first PAIR while( source.size() ) { sorted.push_back( *source.begin() ); source.erase( source.begin() ); // keep looping through the source list looking for a match to the end of the last sorted. int result; while( (result = findPOINT( sorted.back().end, &source[0], source.size() ) ) != 0 ) { int ndx = ABS(result)-1; sorted.push_back( source[ ndx ] ); source.erase( source.begin()+ndx ); if( result < 0 ) swap( sorted.back() ); } } #if 0 && defined(DEBUG) printf( "swapEnds():\n" ); for( unsigned i=0; iAppendPoint( aStart ); path->AppendPoint( aEnd ); path->SetLayerId( aLayerName.c_str() ); return path; } IMAGE* SPECCTRA_DB::makeIMAGE( MODULE* aModule ) { PADSTACKS& padstacks = pcb->library->padstacks; TYPE_COLLECTOR pads; // get all the MODULE's pads. pads.Collect( aModule, scanPADs ); IMAGE* image = new IMAGE(0); image->image_id = CONV_TO_UTF8( aModule->m_LibRef ); // from the pads, and make an IMAGE using collated padstacks. for( int p=0; pm_PadShape==PAD_CIRCLE && pad->m_Drill.x >= pad->m_Size.x ) { KEEPOUT* keepout = new KEEPOUT(image, T_keepout); image->keepouts.push_back( keepout ); CIRCLE* circle = new CIRCLE(keepout); keepout->SetShape( circle ); circle->SetDiameter( scale(pad->m_Drill.x) ); circle->SetVertex( mapPt( pad->m_Pos0 ) ); circle->layer_id = "signal"; } else { PADSTACK* padstack = &padstacks[pad->m_logical_connexion]; PIN* pin = new PIN(image); image->pins.push_back( pin ); pin->padstack_id = padstack->padstack_id; pin->pin_id = CONV_TO_UTF8( pad->ReturnStringPadName() ); // copper shape's position is hole position + offset wxPoint pos = pad->m_Pos0 + pad->m_Offset; pin->SetVertex( mapPt( pos ) ); } } return image; } PADSTACK* SPECCTRA_DB::makeVia( const SEGVIA* aVia ) { CIRCLE* circle; SHAPE* shape; double dsnDiameter; char name[48]; PADSTACK* padstack = new PADSTACK( pcb->library ); switch( aVia->Shape() ) { case VIA_THROUGH: shape = new SHAPE( padstack ); padstack->Append( shape ); circle = new CIRCLE( shape ); shape->SetShape( circle ); dsnDiameter = scale( aVia->m_Width ); circle->SetDiameter( dsnDiameter ); circle->SetLayerId( "signal" ); snprintf( name, sizeof(name), "Via_%.6g_mil", dsnDiameter ); name[ sizeof(name)-1 ] = 0; padstack->SetPadstackId( name ); break; case VIA_BLIND_BURIED: case VIA_MICROVIA: int topLayer; int botLayer; aVia->ReturnLayerPair( &topLayer, &botLayer ); topLayer = kicadLayer2pcb[topLayer]; botLayer = kicadLayer2pcb[botLayer]; if( topLayer > botLayer ) EXCHG( topLayer, botLayer ); dsnDiameter = scale( aVia->m_Width ); for( int layer=topLayer; layer<=botLayer; ++layer ) { shape = new SHAPE( padstack ); padstack->Append( shape ); circle = new CIRCLE( shape ); shape->SetShape( circle ); circle->SetDiameter( dsnDiameter ); circle->SetLayerId( layerIds[layer].c_str() ); } snprintf( name, sizeof(name), "Via[%d-%d]_%.6g_mil", topLayer, botLayer, dsnDiameter ); name[ sizeof(name)-1 ] = 0; padstack->SetPadstackId( name ); break; } return padstack; } PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter ) { char name[48]; PADSTACK* padstack = new PADSTACK( pcb->library ); SHAPE* shape = new SHAPE( padstack ); padstack->Append( shape ); CIRCLE* circle = new CIRCLE( shape ); shape->SetShape( circle ); double dsnDiameter = scale(aCopperDiameter); circle->SetDiameter( dsnDiameter ); circle->SetLayerId( "signal" ); snprintf( name, sizeof(name), "Via_%.6g_mil", dsnDiameter ); name[ sizeof(name)-1 ] = 0; padstack->SetPadstackId( name ); return padstack; } void SPECCTRA_DB::makePADSTACKs( BOARD* aBoard, TYPE_COLLECTOR& aPads ) { char name[80]; // padstack name builder if( aPads.GetCount() ) { qsort( (void*) aPads.BasePtr(), aPads.GetCount(), sizeof(D_PAD*), Pad_list_Sort_by_Shapes ); } D_PAD* old_pad = NULL; for( int i=0; iIsOnLayer( LAYER_CMP_N ), pad->IsOnLayer( COPPER_LAYER_N ) }; if( old_pad && 0==D_PAD::Compare( old_pad, pad ) ) { // padstacks.size()-1 is the index of the matching padstack in LIBRARY::padstacks pad->m_logical_connexion = pcb->library->padstacks.size()-1; // this is the same as the last pad, so do not add it to the padstack list. continue; } // if pad has no copper presence, then it will be made into // an "image->keepout" later. No copper pad here, it is probably a hole. if( (!doLayer[0] && !doLayer[1]) || (pad->m_PadShape==PAD_CIRCLE && pad->m_Drill.x >= pad->m_Size.x) ) { // padstacks.size()-1 is the index of the matching padstack in LIBRARY::padstacks pad->m_logical_connexion = pcb->library->padstacks.size()-1; continue; } old_pad = pad; PADSTACK* padstack = new PADSTACK( pcb->library ); pcb->library->AddPadstack( padstack ); // padstacks.size()-1 is the index of the matching padstack in LIBRARY::padstacks pad->m_logical_connexion = pcb->library->padstacks.size()-1; // For now, we will report only one layer for the pads. SMD pads are reported on the // top layer, and through hole are reported on "signal". // We could do better if there was actually a "layer type" field within // Kicad which would hold one of: T_signal, T_power, T_mixed, T_jumper // See bottom of page 74 of the SECCTRA Design Language Reference, May 2000. int reportedLayers = 1; // how many layers are reported. doLayer[0] = true; const char* layerName = ( pad->m_Attribut == PAD_SMD ) ? layerIds[0].c_str() : "signal"; int coppers = 0; // will always be one for now switch( pad->m_PadShape ) { default: case PAD_CIRCLE: { double diameter = scale(pad->m_Size.x); for( int layer=0; layerAppend( shape ); CIRCLE* circle = new CIRCLE( shape ); shape->SetShape( circle ); circle->SetLayerId( layerName ); circle->SetDiameter( diameter ); ++coppers; } } 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 ); } break; case PAD_RECT: { double dx = scale( pad->m_Size.x ) / 2.0; double dy = scale( pad->m_Size.y ) / 2.0; POINT lowerLeft( -dx, -dy ); POINT upperRight( dx, dy ); for( int layer=0; layerAppend( shape ); RECTANGLE* rect = new RECTANGLE( shape ); shape->SetShape( rect ); rect->SetLayerId( layerName ); rect->SetCorners( lowerLeft, upperRight ); ++coppers; } } 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 they get the same // name here. If so, blend in the padNdx into the name. padstack->SetPadstackId( name ); } break; case PAD_OVAL: { double dx = scale( pad->m_Size.x ) / 2.0; double dy = scale( pad->m_Size.y ) / 2.0; double dr = dx - dy; if( dr >= 0 ) // oval is horizontal { double radius = dy; for( int layer=0; layerAppend( shape ); path = makePath( POINT(-dr, 0.0), POINT(dr, 0.0), layerName ); shape->SetShape( path ); path->aperture_width = 2.0 * radius; ++coppers; } } } else // oval is vertical { double radius = dx; dr = -dr; for( int layer=0; layerAppend( shape ); path = makePath( POINT(0.0, -dr), POINT(0.0, dr), layerName ); shape->SetShape( path ); path->aperture_width = 2.0 * radius; ++coppers; } } } snprintf( name, sizeof(name), "Oval%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 they get the same // name here. If so, blend in the padNdx into the name. padstack->SetPadstackId( name ); } break; /* case PAD_TRAPEZOID: break; */ } } // unique pads are now in the padstack list. // next we add the via's which may be used. int defaultViaSize = aBoard->m_BoardSettings->m_CurrentViaSize; if( defaultViaSize ) { PADSTACK* padstack = makeVia( defaultViaSize ); pcb->library->AddPadstack( padstack ); // remember this index, it is the default via and also the start of the // vias within the padstack list. Before this index are the pads. // At this index and later are the vias. pcb->library->SetViaStartIndex( pcb->library->padstacks.size()-1 ); // padstack->SetPadstackId( "Via_Default" ); I like the padstack_id with the size in it. } for( int i=0; im_BoardSettings->m_ViaSizeHistory[i]; if( !viaSize ) break; if( viaSize == defaultViaSize ) continue; PADSTACK* padstack = makeVia( viaSize ); pcb->library->AddPadstack( padstack ); } } void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) { TYPE_COLLECTOR items; POINT_PAIRS ppairs; POINT_PAIR pair; static const KICAD_T scanMODULEs[] = { TYPEMODULE, EOT }; if( !pcb ) pcb = 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 = aBoard->m_Modules; module; module = module->Next() ) { module->flag = 0; if( module->GetLayer() == COPPER_LAYER_N ) { aBoard->Change_Side_Module( module, NULL ); module->flag = 1; } } //pcb->placement->flip_style = T_rotate_first; // Since none of these statements cause any immediate output, the order // of them is somewhat flexible. The outputting to disk is done at the // end. We start by gathering all the layer information from the board. //---------------------------------------------------- { // specctra wants top physical layer first, then going down to the // bottom most physical layer in physical sequence. // @question : why does Kicad not display layers in that order? int layerCount = aBoard->GetCopperLayerCount(); layerIds.clear(); pcbLayer2kicad.resize( layerCount ); kicadLayer2pcb.resize( LAYER_CMP_N+1 ); for( int kiNdx=layerCount-1, pcbNdx=0; kiNdx >= 0; --kiNdx, ++pcbNdx ) { int kilayer = kiNdx>0 && kiNdx==layerCount-1 ? LAYER_CMP_N : kiNdx; // establish bi-directional mapping between kicad's BOARD layer and PCB layer pcbLayer2kicad[pcbNdx] = kilayer; kicadLayer2pcb[kilayer] = pcbNdx; // save the specctra layer name in SPECCTRA_DB::layerIds for later. layerIds.push_back( CONV_TO_UTF8( aBoard->GetLayerName( kilayer ) ) ); LAYER* layer = new LAYER( pcb->structure ); pcb->structure->layers.push_back( layer ); layer->name = layerIds.back(); layer->properties.push_back( PROPERTY() ); PROPERTY* property = &layer->properties.back(); property->name = "index"; char temp[32]; sprintf( temp, "%d", pcbNdx ); property->value = temp; // layer->type = @todo need this, the export would be better. } } // for now, report on only the top and bottom layers with respect to the copper // within a pad's padstack. this is usually correct, but not rigorous. // a space in a quoted token is NOT a terminator, true establishes this. pcb->parser->space_in_quoted_tokens = true; //----- & -------------------- { pcb->unit->units = T_mil; pcb->resolution->units = T_mil; pcb->resolution->value = 100; } //----------------------------------------------- { // get all the DRAWSEGMENTS into 'items', then look for layer == EDGE_N, // and those segments comprise the board's perimeter. static const KICAD_T scanDRAWSEGMENTS[] = { TYPEDRAWSEGMENT, EOT }; items.Collect( aBoard, scanDRAWSEGMENTS ); bool haveEdges = false; ppairs.clear(); for( int i=0; iType() == TYPEDRAWSEGMENT ); if( item->GetLayer() == EDGE_N ) { pair.start = mapPt( item->m_Start ); pair.end = mapPt( item->m_End ); pair.item = item; ppairs.push_back( pair ); haveEdges = true; } } if( haveEdges ) { swapEnds( ppairs ); #if 0 && defined(DEBUG) for( unsigned i=0; iitem->Show( 0, std::cout ); } #endif BOUNDARY* boundary = new BOUNDARY(0); if( isRectangle( ppairs ) ) { RECTANGLE* rect = new RECTANGLE( boundary ); rect->layer_id = "pcb"; // opposite corners rect->SetCorners( ppairs[0].start, ppairs[2].start ); boundary->rectangle = rect; } else { PATH* path = new PATH( boundary ); boundary->paths.push_back( path ); path->layer_id = "pcb"; for( unsigned i=0; ipoints.push_back( ppairs[i].start ); } } pcb->structure->SetBOUNDARY( boundary ); } else { aBoard->ComputeBoundaryBox(); BOUNDARY* boundary = new BOUNDARY(0); RECTANGLE* rect = new RECTANGLE( boundary ); rect->layer_id = "pcb"; // opposite corners wxPoint bottomRight; bottomRight.x = aBoard->m_BoundaryBox.GetRight(); bottomRight.y = aBoard->m_BoundaryBox.GetBottom(); rect->SetCorners( mapPt( aBoard->m_BoundaryBox.GetOrigin() ), mapPt( bottomRight ) ); boundary->rectangle = rect; pcb->structure->SetBOUNDARY( boundary ); } } //------------------------------------------------------------- { // put out these rules, the user can then edit them with a text editor char rule[80]; // padstack name builder int curTrackWidth = aBoard->m_BoardSettings->m_CurrentTrackWidth; int curTrackClear = aBoard->m_BoardSettings->m_TrackClearence; double clearance = scale(curTrackClear); STRINGS& rules = pcb->structure->rules->rules; sprintf( rule, "(width %.6g)", scale( curTrackWidth ) ); rules.push_back( rule ); sprintf( rule, "(clearance %.6g)", clearance ); rules.push_back( rule ); sprintf( rule, "(clearance %.6g (type pad_to_turn_gap))", clearance ); rules.push_back( rule ); sprintf( rule, "(clearance %.6g (type smd_to_turn_gap))", clearance ); rules.push_back( rule ); sprintf( rule, "(clearance %.6g (type via_via))", clearance ); rules.push_back( rule ); sprintf( rule, "(clearance %.6g (type via_smd))", clearance ); rules.push_back( rule ); sprintf( rule, "(clearance %.6g (type via_pin))", clearance ); rules.push_back( rule ); sprintf( rule, "(clearance %.6g (type pin_pin))", clearance ); rules.push_back( rule ); sprintf( rule, "(clearance %.6g (type smd_pin))", clearance ); rules.push_back( rule ); sprintf( rule, "(clearance %.6g (type smd_smd))", clearance ); rules.push_back( rule ); } //------------------------------------- { static const KICAD_T scanZONEs[] = { TYPEZONE_CONTAINER, EOT }; items.Collect( aBoard, scanZONEs ); for( int i=0; istructure ); PATH* polygon = new PATH( plane, T_polygon ); plane->SetShape( polygon ); plane->name = CONV_TO_UTF8( item->m_Netname ); wxString layerName = aBoard->GetLayerName( item->GetLayer() ); polygon->layer_id = CONV_TO_UTF8( layerName ); int count = item->m_Poly->corner.size(); for( int j=0; jm_Poly->corner[j].x, item->m_Poly->corner[j].y ); polygon->points.push_back( mapPt(point) ); } 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. //------------------------------------- { TYPE_COLLECTOR pads; // get all the D_PADs into 'pads'. pads.Collect( aBoard, scanPADs ); makePADSTACKs( aBoard, pads ); #if 0 && defined(DEBUG) for( int p=0; pShow( 0, std::cout ); #endif } //-------------------------------------- { items.Collect( aBoard, scanMODULEs ); for( int m=0; mlibrary->LookupIMAGE( image ); if( registered != image ) { // If our new 'image' is not a unique IMAGE, delete it. // In either case, 'registered' is the one we'll work with henceforth. delete image; } // @todo: this only works if the user has not modified the MODULE within the PCB // and made it different from what is in the PCBNEW library. Need to test // each image for uniqueness, not just based on name as is done here: COMPONENT* comp = pcb->placement->LookupCOMPONENT( registered->GetImageId() ); PLACE* place = new PLACE( comp ); comp->places.push_back( place ); place->SetRotation( module->m_Orient/10.0 ); place->SetVertex( mapPt( module->m_Pos ) ); place->component_id = CONV_TO_UTF8( module->GetReference() ); place->part_number = CONV_TO_UTF8( module->GetValue() ); // module is flipped from bottom side, set side to T_back if( module->flag ) { int angle = 1800 - module->m_Orient; NORMALIZE_ANGLE_POS(angle); place->SetRotation( angle/10.0 ); place->side = T_back; } } } //----------------------------------------------------- { NETWORK* network = pcb->network; TYPE_COLLECTOR nets; TYPE_COLLECTOR pads; static const KICAD_T scanNETs[] = { PCB_EQUIPOT_STRUCT_TYPE, EOT }; nets.Collect( aBoard, scanNETs ); items.Collect( aBoard, scanMODULEs ); PIN_REF emptypin(0); for( int n=0; nGetNet() == 0 ) continue; NET* net = new NET( network ); network->nets.push_back( net ); net->net_id = CONV_TO_UTF8( kinet->m_Netname ); net->net_number = kinet->GetNet(); for( int m=0; mGetNet() == kinet->GetNet() ) { // push on an empty one, then fill it via 'pin_ref' net->pins.push_back( emptypin ); PIN_REF* pin_ref = &net->pins.back(); pin_ref->SetParent( net ); pin_ref->component_id = CONV_TO_UTF8( module->GetReference() ); pin_ref->pin_id = CONV_TO_UTF8( pad->ReturnStringPadName() ); } } } } } #if 1 // do existing wires and vias //---------------------------------------- { // export all of them for now, later we'll decide what controls we need // on this. static const KICAD_T scanTRACKs[] = { TYPETRACK, EOT }; items.Collect( aBoard, scanTRACKs ); /* if( items.GetCount() ) qsort( (void*) items.BasePtr(), items.GetCount(), sizeof(TRACK*), Track_list_Sort_by_Netcode ); */ std::string netname; WIRING* wiring = pcb->wiring; PATH* path = 0; int old_netcode = -1; int old_width = -1; int old_layer = -1; for( int i=0; iGetNet() == 0 ) continue; if( old_netcode != track->GetNet() || old_width != track->m_Width || old_layer != track->GetLayer() || (path && path->points.back() != mapPt(track->m_Start) ) ) { old_width = track->m_Width; old_layer = track->GetLayer(); if( old_netcode != track->GetNet() ) { old_netcode = track->GetNet(); EQUIPOT* equipot = aBoard->FindNet( track->GetNet() ); wxASSERT( equipot ); netname = CONV_TO_UTF8( equipot->m_Netname ); } WIRE* wire = new WIRE( wiring ); wiring->wires.push_back( wire ); wire->net_id = netname; wire->wire_type = T_normal; // @todo, this should be configurable int kiLayer = track->GetLayer(); int pcbLayer = kicadLayer2pcb[kiLayer]; path = new PATH( wire ); wire->SetShape( path ); path->layer_id = layerIds[pcbLayer]; path->aperture_width = scale( old_width ); path->AppendPoint( mapPt( track->m_Start ) ); } path->AppendPoint( mapPt( track->m_End ) ); } } //-------------------------- { // export all of them for now, later we'll decide what controls we need // on this. static const KICAD_T scanVIAs[] = { TYPEVIA, EOT }; items.Collect( aBoard, scanVIAs ); for( int i=0; iType() == TYPEVIA ); PADSTACK* padstack = makeVia( via ); PADSTACK* registered = pcb->library->LookupVia( padstack ); if( padstack != registered ) { delete padstack; } WIRE_VIA* dsnVia = new WIRE_VIA( pcb->wiring ); pcb->wiring->wire_vias.push_back( dsnVia ); dsnVia->padstack_id = registered->padstack_id; dsnVia->vertexes.push_back( mapPt( via->GetPosition() ) ); int netcode = via->GetNet(); EQUIPOT* equipot = aBoard->FindNet( netcode ); wxASSERT( equipot ); dsnVia->net_id = CONV_TO_UTF8( equipot->m_Netname ); dsnVia->via_type = T_normal; // @todo, this should be configurable } } #endif // do existing wires and vias //------------------------------------------------------ { // Output the vias in the padstack list here, by name. This must // be done after exporting existing vias as WIRE_VIAs. VIA* vias = pcb->structure->via; PADSTACKS& padstacks = pcb->library->padstacks; int viaNdx = pcb->library->via_start_index; if( viaNdx != -1 ) { for( ; viaNdx < (int)padstacks.size(); ++viaNdx ) { vias->AppendVia( padstacks[viaNdx].padstack_id.c_str() ); } } } //----------------------------------------------------- // DSN Images (=Kicad MODULES and pads) must be presented from the // top view. Restore those that were flipped. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { if( module->flag ) { aBoard->Change_Side_Module( module, NULL ); module->flag = 0; } } } } // namespace DSN