kicad/pcbnew/specctra_export.cpp

1164 lines
40 KiB
C++
Raw Normal View History

2008-01-21 21:24:39 +00:00
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2007-2008 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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"
2008-01-22 20:48:02 +00:00
#include "collectors.h"
2008-01-24 21:47:54 +00:00
#include "wxPcbStruct.h" // Change_Side_Module()
2008-01-26 02:02:27 +00:00
#include "pcbstruct.h" // HISTORY_NUMBER
2008-02-01 01:09:39 +00:00
#include "autorout.h" // NET_CODES_OK
2008-01-21 21:24:39 +00:00
2008-01-21 22:16:45 +00:00
using namespace DSN;
2008-01-21 21:24:39 +00:00
// see wxPcbStruct.h
void WinEDA_PcbFrame::ExportToSPECCTRA( wxCommandEvent& event )
{
2008-01-22 20:48:02 +00:00
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;
2008-01-21 22:16:45 +00:00
2008-02-01 01:09:39 +00:00
// prepare the EQUIPOTs
if( !( m_Pcb->m_Status_Pcb & NET_CODES_OK ) )
{
//m_Pcb->m_Status_Pcb &= ~(LISTE_PAD_OK);
recalcule_pad_net_code();
}
2008-01-22 20:48:02 +00:00
SPECCTRA_DB db;
2008-01-24 21:47:54 +00:00
bool ok = true;
wxString errorText;
2008-01-22 20:48:02 +00:00
2008-01-21 22:16:45 +00:00
db.SetPCB( SPECCTRA_DB::MakePCB() );
2008-01-24 21:47:54 +00:00
2008-01-21 22:16:45 +00:00
try
{
db.FromBOARD( m_Pcb );
2008-01-22 20:48:02 +00:00
db.ExportPCB( fullFileName, true );
2008-01-24 21:47:54 +00:00
// if an exception is thrown by FromBOARD or ExportPCB(), then
2008-01-23 01:52:49 +00:00
// ~SPECCTRA_DB() will close the file.
2008-01-21 22:16:45 +00:00
}
catch( IOError ioe )
2008-01-21 22:16:45 +00:00
{
2008-01-24 21:47:54 +00:00
ok = false;
// copy the error string to safe place, ioe is in this scope only.
2008-01-24 21:47:54 +00:00
errorText = ioe.errorText;
}
if( ok )
{
// @todo display a message saying the export is complete.
}
else
DisplayError( this, errorText );
2008-01-21 21:24:39 +00:00
}
namespace DSN {
2008-01-22 20:48:02 +00:00
struct POINT_PAIR
{
POINT p1; ///< start
POINT p2; ///< end
BOARD_ITEM* item; ///< the item which has these points, TRACK or DRAWSEGMENT
};
typedef std::vector<POINT_PAIR> POINT_PAIRS;
2008-01-21 22:16:45 +00:00
2008-01-22 20:48:02 +00:00
static inline void swap( POINT_PAIR& pair )
{
POINT temp = pair.p1;
pair.p1 = pair.p2;
pair.p2 = 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.
*/
2008-01-24 21:47:54 +00:00
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.
}
2008-01-23 01:52:49 +00:00
/**
* 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.
*/
2008-01-22 20:48:02 +00:00
static POINT mapPt( const wxPoint& pt )
2008-01-21 22:16:45 +00:00
{
2008-01-22 20:48:02 +00:00
POINT ret;
2008-01-24 21:47:54 +00:00
ret.x = mapX( pt.x );
ret.y = mapY( pt.y );
ret.FixNegativeZero();
2008-01-22 20:48:02 +00:00
return ret;
}
2008-01-21 22:16:45 +00:00
2008-01-22 20:48:02 +00:00
/**
* Function swapEnds
* will swap ends of any POINT_PAIR in the POINT_PAIRS list in order to
* make the consecutive POINT_PAIRs be "connected" at their ends.
*/
static void swapEnds( POINT_PAIRS& aList )
{
POINT temp;
2008-01-21 22:16:45 +00:00
2008-01-22 20:48:02 +00:00
if( aList.size() <= 1 )
return;
for( unsigned i=0; i<aList.size(); ++i )
{
if( aList[i].p1 == aList[i+1].p1 )
swap( aList[i] );
else if( aList[i].p1 == aList[i+1].p2 )
{
swap( aList[i] );
swap( aList[i+1] );
++i; // skip next one, we swapped i+1 here
}
}
}
/**
* Function isRectangle
2008-01-23 01:52:49 +00:00
* tests to see if the POINT_PAIRS list makes up a vertically/horizontally
2008-01-22 20:48:02 +00:00
* oriented rectangle.
* @return bool - true if there are 4 point pairs making a rectangle.
*/
static bool isRectangle( POINT_PAIRS& aList )
{
if( aList.size() == 4 )
{
for( unsigned i=0; i<aList.size(); ++i )
{
if( i < aList.size()-1 )
if( aList[i].p2 != aList[i+1].p1 )
return false;
if( aList[i].p1.x != aList[i].p2.x
&& aList[i].p1.y != aList[i].p2.y )
return false;
}
return ( aList[0].p1 == aList[3].p2 );
}
return false;
}
2008-01-24 21:47:54 +00:00
/**************************************************************************/
static int Pad_list_Sort_by_Shapes( const void* refptr, const void* objptr )
/**************************************************************************/
2008-01-22 20:48:02 +00:00
{
2008-01-24 21:47:54 +00:00
const D_PAD* padref = *(D_PAD**)refptr;
const D_PAD* padcmp = *(D_PAD**)objptr;
return D_PAD::Compare( padref, padcmp );
}
2008-01-31 01:30:52 +00:00
2008-01-31 06:46:31 +00:00
/*
2008-01-31 01:30:52 +00:00
static int Track_list_Sort_by_Netcode( const void* o1, const void* o2 )
{
TRACK* t1 = *(TRACK**) o1;
TRACK* t2 = *(TRACK**) o2;
int diff;
if( (diff = t1->GetNet() - t2->GetNet()) )
return diff;
if( (diff = t1->m_Width - t2->m_Width) )
return diff;
if( (diff = t1->GetLayer() - t2->GetLayer()) )
return diff;
return diff; // zero here
}
2008-01-31 06:46:31 +00:00
*/
2008-01-31 01:30:52 +00:00
2008-01-25 22:03:36 +00:00
/**
* Function makePath
* creates a PATH element with a single straight line, a pair of vertices.
*/
static PATH* makePath( const POINT& aStart, const POINT& aEnd, const std::string& aLayerName )
{
PATH* path = new PATH( 0, T_path );
path->AppendPoint( aStart );
path->AppendPoint( aEnd );
path->SetLayerId( aLayerName.c_str() );
return path;
}
static QARC* makeArc( const POINT& aStart, const POINT& aEnd,
const POINT& aCenter, const std::string& aLayerName )
{
QARC* qarc = new QARC(0);
qarc->SetStart( aStart );
qarc->SetEnd( aEnd );
qarc->SetCenter( aCenter );
qarc->SetLayerId( aLayerName.c_str() );
return qarc;
}
2008-01-29 16:45:14 +00:00
IMAGE* SPECCTRA_DB::makeIMAGE( MODULE* aModule )
{
PADSTACKS& padstacks = pcb->library->padstacks;
TYPE_COLLECTOR pads;
static const KICAD_T scanPADs[] = { TYPEPAD, EOT };
2008-01-29 16:45:14 +00:00
// get all the MODULE's pads.
pads.Collect( aModule, scanPADs );
2008-01-29 16:45:14 +00:00
IMAGE* image = new IMAGE(0);
2008-01-29 16:45:14 +00:00
image->image_id = CONV_TO_UTF8( aModule->m_LibRef );
// from the pads, and make an IMAGE using collated padstacks.
for( int p=0; p<pads.GetCount(); ++p )
2008-01-29 16:45:14 +00:00
{
D_PAD* pad = (D_PAD*) pads[p];
2008-01-29 16:45:14 +00:00
// see if this pad is a through hole with no copper on its perimeter
if( !pad->IsOnLayer( LAYER_CMP_N ) && !pad->IsOnLayer( COPPER_LAYER_N ) )
{
2008-01-30 19:16:46 +00:00
if( pad->m_Drill.x != 0 )
{
KEEPOUT* keepout = new KEEPOUT(image, T_keepout);
image->keepouts.push_back( keepout );
2008-01-30 19:16:46 +00:00
CIRCLE* circle = new CIRCLE(keepout);
keepout->SetShape( circle );
circle->SetDiameter( scale(pad->m_Drill.x) );
circle->SetVertex( POINT( mapPt( pad->m_Pos0 ) ) );
circle->layer_id = "signal";
// ?? the keepout is not affecting the power layers?
}
}
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() );
pin->SetVertex( mapPt( pad->m_Pos0 ) );
}
2008-01-29 16:45:14 +00:00
}
return image;
}
2008-02-01 01:09:39 +00:00
PADSTACK* SPECCTRA_DB::makeVia( const SEGVIA* aVia )
{
char name[48];
PADSTACK* padstack = new PADSTACK( pcb->library );
SHAPE* shape = new SHAPE( padstack );
padstack->Append( shape );
// @todo: handle the aVia->Shape() differently for each type of via: MICROVIA, etc.
CIRCLE* circle = new CIRCLE( shape );
shape->SetShape( circle );
double 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 );
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;
}
2008-01-29 16:45:14 +00:00
void SPECCTRA_DB::makePADSTACKs( BOARD* aBoard, TYPE_COLLECTOR& aPads )
2008-01-24 21:47:54 +00:00
{
2008-01-26 02:02:27 +00:00
char name[80]; // padstack name builder
2008-01-24 21:47:54 +00:00
if( aPads.GetCount() )
{
2008-01-25 15:13:57 +00:00
qsort( (void*) aPads.BasePtr(), aPads.GetCount(), sizeof(D_PAD*), Pad_list_Sort_by_Shapes );
2008-01-24 21:47:54 +00:00
}
D_PAD* old_pad = NULL;
2008-01-25 22:03:36 +00:00
2008-01-24 21:47:54 +00:00
for( int i=0; i<aPads.GetCount(); ++i )
{
D_PAD* pad = (D_PAD*) aPads[i];
2008-01-31 06:46:31 +00:00
bool doLayer[2] = { // top and bottom layers only
2008-01-26 02:02:27 +00:00
pad->IsOnLayer( LAYER_CMP_N ),
pad->IsOnLayer( COPPER_LAYER_N )
};
if( old_pad && 0==D_PAD::Compare( old_pad, pad ) )
{
2008-01-29 16:45:14 +00:00
// 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.
2008-01-26 02:02:27 +00:00
continue;
}
2008-01-24 21:47:54 +00:00
2008-01-26 02:02:27 +00:00
// 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] )
2008-01-24 21:47:54 +00:00
{
2008-01-29 16:45:14 +00:00
// padstacks.size()-1 is the index of the matching padstack in LIBRARY::padstacks
pad->m_logical_connexion = pcb->library->padstacks.size()-1;
2008-01-24 21:47:54 +00:00
continue;
}
old_pad = pad;
2008-01-29 16:45:14 +00:00
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;
2008-01-25 22:03:36 +00:00
// paddOfset is the offset of copper shape relative to hole position,
// and pad->m_Pos is hole position. All shapes must be shifted by
// this distance, normally (0,0).
// Note that the y correction here is set negative.
POINT padOffset( scale(pad->m_Offset.x), -scale(pad->m_Offset.y) );
2008-01-31 01:30:52 +00:00
2008-01-31 06:46:31 +00:00
// 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 <reserved_layer_name> "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.
2008-01-31 01:30:52 +00:00
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
2008-01-24 21:47:54 +00:00
switch( pad->m_PadShape )
{
default:
case PAD_CIRCLE:
{
double diameter = scale(pad->m_Size.x);
2008-01-26 02:02:27 +00:00
for( int layer=0; layer<reportedLayers; ++layer )
2008-01-24 21:47:54 +00:00
{
2008-01-26 02:02:27 +00:00
if( doLayer[layer] )
2008-01-25 22:03:36 +00:00
{
SHAPE* shape = new SHAPE( padstack );
padstack->Append( shape );
2008-01-29 16:45:14 +00:00
CIRCLE* circle = new CIRCLE( shape );
2008-01-25 22:03:36 +00:00
shape->SetShape( circle );
2008-01-31 01:30:52 +00:00
circle->SetLayerId( layerName );
2008-01-25 22:03:36 +00:00
circle->SetDiameter( diameter );
circle->SetVertex( padOffset );
++coppers;
}
2008-01-24 21:47:54 +00:00
}
2008-01-25 22:03:36 +00:00
snprintf( name, sizeof(name), "Round%dPad_%.6g_mil", coppers, scale(pad->m_Size.x) );
2008-01-24 21:47:54 +00:00
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;
2008-01-25 22:03:36 +00:00
POINT lowerLeft( -dx, -dy );
POINT upperRight( dx, dy );
2008-01-24 21:47:54 +00:00
2008-01-25 22:03:36 +00:00
lowerLeft += padOffset;
upperRight += padOffset;
2008-01-24 21:47:54 +00:00
2008-01-26 02:02:27 +00:00
for( int layer=0; layer<reportedLayers; ++layer )
2008-01-24 21:47:54 +00:00
{
2008-01-26 02:02:27 +00:00
if( doLayer[layer] )
2008-01-25 22:03:36 +00:00
{
SHAPE* shape = new SHAPE( padstack );
padstack->Append( shape );
RECTANGLE* rect = new RECTANGLE( shape );
shape->SetShape( rect );
2008-01-31 01:30:52 +00:00
rect->SetLayerId( layerName );
2008-01-25 22:03:36 +00:00
rect->SetCorners( lowerLeft, upperRight );
++coppers;
}
2008-01-24 21:47:54 +00:00
}
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
2008-01-25 22:03:36 +00:00
// D_PAD::Compare() could say two pads are different, yet they get the same
2008-01-24 21:47:54 +00:00
// name here. If so, blend in the padNdx into the name.
padstack->SetPadstackId( name );
}
break;
2008-01-25 22:03:36 +00:00
case PAD_OVAL:
2008-01-24 21:47:54 +00:00
{
2008-01-25 22:03:36 +00:00
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;
2008-01-26 02:02:27 +00:00
for( int layer=0; layer<reportedLayers; ++layer )
2008-01-25 22:03:36 +00:00
{
2008-01-26 02:02:27 +00:00
if( doLayer[layer] )
{
// each oval is 2 lines and 4 (quarter circle) qarcs
SHAPE* shape;
PATH* path;
QARC* qarc;
shape = new SHAPE( padstack );
padstack->Append( shape );
path = makePath(
POINT( -dr + padOffset.x, padOffset.y - radius ), // aStart
POINT( dr + padOffset.x, padOffset.y - radius ), // aEnd
2008-01-31 01:30:52 +00:00
layerName );
2008-01-26 02:02:27 +00:00
shape->SetShape( path );
shape = new SHAPE( padstack );
padstack->Append( shape );
// @todo: this 1/2 circle arc needs to be split into two quarter circle arcs
qarc = makeArc(
POINT( dr + padOffset.x, padOffset.y - radius), // aStart
POINT( dr + padOffset.x, padOffset.y + radius), // aEnd
POINT( dr + padOffset.x, padOffset.y ), // aCenter
2008-01-31 01:30:52 +00:00
layerName );
2008-01-26 02:02:27 +00:00
shape->SetShape( qarc );
2008-01-24 21:47:54 +00:00
2008-01-26 02:02:27 +00:00
shape = new SHAPE( padstack );
padstack->Append( shape );
path = makePath(
POINT( dr + padOffset.x, padOffset.y + radius ), // aStart
POINT( -dr + padOffset.x, padOffset.y + radius ), // aEnd
2008-01-31 01:30:52 +00:00
layerName );
2008-01-26 02:02:27 +00:00
shape->SetShape( path );
shape = new SHAPE( padstack );
padstack->Append( shape );
// @todo: this 1/2 circle arc needs to be split into two quarter circle arcs
qarc = makeArc(
POINT( -dr + padOffset.x, padOffset.y + radius), // aStart
POINT( -dr + padOffset.x, padOffset.y - radius), // aEnd
POINT( -dr + padOffset.x, padOffset.y ), // aCenter
2008-01-31 01:30:52 +00:00
layerName );
2008-01-26 02:02:27 +00:00
shape->SetShape( qarc );
++coppers;
}
2008-01-25 22:03:36 +00:00
}
}
else // oval is vertical
{
double radius = dx;
dr = -dr;
2008-01-26 02:02:27 +00:00
for( int layer=0; layer<reportedLayers; ++layer )
2008-01-25 22:03:36 +00:00
{
2008-01-26 02:02:27 +00:00
if( doLayer[layer] )
{
// each oval is 2 lines and 2 qarcs
SHAPE* shape;
PATH* path;
QARC* qarc;
shape = new SHAPE( padstack );
padstack->Append( shape );
path = makePath(
POINT( -radius + padOffset.x, padOffset.y - dr ), // aStart
POINT( -radius + padOffset.x, padOffset.y + dr ), // aEnd
2008-01-31 01:30:52 +00:00
layerName );
2008-01-26 02:02:27 +00:00
shape->SetShape( path );
2008-01-24 21:47:54 +00:00
2008-01-26 02:02:27 +00:00
shape = new SHAPE( padstack );
padstack->Append( shape );
// @todo: this 1/2 circle arc needs to be split into two quarter circle arcs
qarc = makeArc(
POINT( -radius + padOffset.x, padOffset.y + dr ), // aStart
POINT( radius + padOffset.x, padOffset.y + dr), // aEnd
POINT( padOffset.x, padOffset.y +dr ), // aCenter
2008-01-31 01:30:52 +00:00
layerName );
2008-01-26 02:02:27 +00:00
shape->SetShape( qarc );
shape = new SHAPE( padstack );
padstack->Append( shape );
path = makePath(
POINT( radius + padOffset.x, padOffset.y + dr ), // aStart
POINT( radius + padOffset.x, padOffset.y - dr ), // aEnd
2008-01-31 01:30:52 +00:00
layerName );
2008-01-26 02:02:27 +00:00
shape->SetShape( path );
shape = new SHAPE( padstack );
padstack->Append( shape );
// @todo: this 1/2 circle arc needs to be split into two quarter circle arcs
qarc = makeArc(
POINT( radius + padOffset.x, padOffset.y - dr), // aStart
POINT( -radius + padOffset.x, padOffset.y - dr), // aEnd
POINT( padOffset.x, padOffset.y - dr ), // aCenter
2008-01-31 01:30:52 +00:00
layerName );
2008-01-26 02:02:27 +00:00
shape->SetShape( qarc );
++coppers;
}
2008-01-25 22:03:36 +00:00
}
}
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 );
2008-01-24 21:47:54 +00:00
}
break;
2008-01-25 22:03:36 +00:00
/*
2008-01-24 21:47:54 +00:00
case PAD_TRAPEZOID:
break;
2008-01-25 22:03:36 +00:00
*/
2008-01-24 21:47:54 +00:00
}
}
2008-01-26 02:02:27 +00:00
// unique pads are now in the padstack list.
// next we add the via's which may be used.
2008-01-26 02:02:27 +00:00
int defaultViaSize = aBoard->m_BoardSettings->m_CurrentViaSize;
if( defaultViaSize )
{
2008-02-01 01:09:39 +00:00
PADSTACK* padstack = makeVia( defaultViaSize );
2008-01-29 16:45:14 +00:00
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 );
2008-01-26 02:02:27 +00:00
2008-02-01 01:09:39 +00:00
// padstack->SetPadstackId( "Via_Default" ); I like the padstack_id with the size in it.
2008-01-26 02:02:27 +00:00
}
for( int i=0; i<HISTORY_NUMBER; ++i )
{
int viaSize = aBoard->m_BoardSettings->m_ViaSizeHistory[i];
if( !viaSize )
break;
if( viaSize == defaultViaSize )
continue;
2008-02-01 01:09:39 +00:00
PADSTACK* padstack = makeVia( viaSize );
2008-01-29 16:45:14 +00:00
pcb->library->AddPadstack( padstack );
2008-01-26 02:02:27 +00:00
}
2008-01-21 22:16:45 +00:00
}
2008-01-22 20:48:02 +00:00
2008-01-25 22:03:36 +00:00
void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
2008-01-22 20:48:02 +00:00
{
TYPE_COLLECTOR items;
POINT_PAIRS ppairs;
POINT_PAIR pair;
if( !pcb )
pcb = SPECCTRA_DB::MakePCB();
2008-01-25 22:03:36 +00:00
// 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;
}
}
2008-01-31 01:30:52 +00:00
// 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.
//-----<layer_descriptor>-----------------------------------------------
{
// 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();
2008-01-31 06:46:31 +00:00
pcbLayer2kicad.resize( layerCount );
kicadLayer2pcb.resize( LAYER_CMP_N+1 );
2008-01-31 01:30:52 +00:00
2008-01-31 06:46:31 +00:00
for( int kiNdx=layerCount-1, pcbNdx=0; kiNdx >= 0; --kiNdx, ++pcbNdx )
2008-01-31 01:30:52 +00:00
{
2008-01-31 06:46:31 +00:00
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;
2008-01-31 01:30:52 +00:00
// save the specctra layer name in SPECCTRA_DB::layerIds for later.
2008-01-31 06:46:31 +00:00
layerIds.push_back( CONV_TO_UTF8( aBoard->GetLayerName( kilayer ) ) );
2008-01-31 01:30:52 +00:00
LAYER* layer = new LAYER( pcb->structure );
pcb->structure->layers.push_back( layer );
layer->name = layerIds.back();
// layer->type = @todo need this, the export would be better.
}
}
2008-01-25 22:03:36 +00:00
2008-01-31 01:30:52 +00:00
// for now, report on only the top and bottom layers with respect to the copper
2008-01-31 06:46:31 +00:00
// within a pad's padstack. this is usually correct, but not rigorous.
2008-01-31 01:30:52 +00:00
// a space in a quoted token is NOT a terminator, true establishes this.
pcb->parser->space_in_quoted_tokens = true;
2008-01-25 22:03:36 +00:00
2008-01-23 01:52:49 +00:00
//-----<unit_descriptor> & <resolution_descriptor>--------------------
{
pcb->unit->units = T_mil;
pcb->resolution->units = T_mil;
pcb->resolution->value = 100;
}
2008-01-22 20:48:02 +00:00
2008-01-23 01:52:49 +00:00
//-----<boundary_descriptor>------------------------------------------
2008-01-22 20:48:02 +00:00
{
2008-01-23 01:52:49 +00:00
// get all the DRAWSEGMENTS into 'items', then look for layer == EDGE_N,
// and those segments comprise the board's perimeter.
2008-01-23 22:36:37 +00:00
static const KICAD_T scanDRAWSEGMENTS[] = { TYPEDRAWSEGMENT, EOT };
2008-01-23 01:52:49 +00:00
items.Collect( aBoard, scanDRAWSEGMENTS );
bool haveEdges = false;
ppairs.clear();
for( int i=0; i<items.GetCount(); ++i )
2008-01-22 20:48:02 +00:00
{
2008-01-23 01:52:49 +00:00
DRAWSEGMENT* item = (DRAWSEGMENT*) items[i];
wxASSERT( item->Type() == TYPEDRAWSEGMENT );
if( item->GetLayer() == EDGE_N )
{
pair.p1 = mapPt( item->m_Start );
pair.p2 = mapPt( item->m_End );
pair.item = item;
ppairs.push_back( pair );
haveEdges = true;
}
2008-01-22 20:48:02 +00:00
}
2008-01-23 01:52:49 +00:00
if( haveEdges )
2008-01-22 20:48:02 +00:00
{
2008-01-23 01:52:49 +00:00
swapEnds( ppairs );
2008-01-25 22:03:36 +00:00
#if 0 && defined(DEBUG)
2008-01-23 01:52:49 +00:00
for( unsigned i=0; i<ppairs.size(); ++i )
{
POINT_PAIR* p = &ppairs[i];
p->item->Show( 0, std::cout );
}
2008-01-24 21:47:54 +00:00
#endif
2008-01-23 01:52:49 +00:00
BOUNDARY* boundary = new BOUNDARY(0);
if( isRectangle( ppairs ) )
{
RECTANGLE* rect = new RECTANGLE( boundary );
rect->layer_id = "pcb";
// opposite corners
2008-01-29 16:45:14 +00:00
rect->SetCorners( ppairs[0].p1, ppairs[2].p1 );
2008-01-23 01:52:49 +00:00
boundary->rectangle = rect;
}
else
{
PATH* path = new PATH( boundary );
path->layer_id = "pcb";
for( unsigned i=0; i<ppairs.size(); ++i )
{
// unless its a closed polygon, this probably won't work,
// otherwise it will.
path->points.push_back( ppairs[i].p1 );
}
boundary->paths.push_back( path );
}
pcb->structure->SetBOUNDARY( boundary );
2008-01-22 20:48:02 +00:00
}
2008-01-23 01:52:49 +00:00
else
2008-01-22 20:48:02 +00:00
{
2008-01-23 01:52:49 +00:00
aBoard->ComputeBoundaryBox();
2008-01-22 20:48:02 +00:00
2008-01-23 01:52:49 +00:00
BOUNDARY* boundary = new BOUNDARY(0);
RECTANGLE* rect = new RECTANGLE( boundary );
2008-01-22 20:48:02 +00:00
rect->layer_id = "pcb";
2008-01-23 01:52:49 +00:00
2008-01-22 20:48:02 +00:00
// opposite corners
2008-01-23 01:52:49 +00:00
wxPoint bottomRight;
bottomRight.x = aBoard->m_BoundaryBox.GetRight();
bottomRight.y = aBoard->m_BoundaryBox.GetBottom();
2008-01-22 20:48:02 +00:00
rect->SetCorners( mapPt( aBoard->m_BoundaryBox.GetOrigin() ),
mapPt( bottomRight ) );
2008-01-22 20:48:02 +00:00
2008-01-23 01:52:49 +00:00
boundary->rectangle = rect;
2008-01-22 20:48:02 +00:00
2008-01-23 01:52:49 +00:00
pcb->structure->SetBOUNDARY( boundary );
2008-01-22 20:48:02 +00:00
}
}
2008-01-23 01:52:49 +00:00
2008-01-31 06:46:31 +00:00
//-----<rules>--------------------------------------------------------
{
// 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 );
}
//-----<zone containers become planes>--------------------------------
2008-01-23 22:36:37 +00:00
{
static const KICAD_T scanZONEs[] = { TYPEZONE_CONTAINER, EOT };
items.Collect( aBoard, scanZONEs );
for( int i=0; i<items.GetCount(); ++i )
{
ZONE_CONTAINER* item = (ZONE_CONTAINER*) items[i];
COPPER_PLANE* plane = new COPPER_PLANE( pcb->structure );
PATH* polygon = new PATH( plane, T_polygon );
2008-01-30 19:16:46 +00:00
plane->SetShape( polygon );
2008-01-23 22:36:37 +00:00
2008-01-31 01:30:52 +00:00
plane->name = CONV_TO_UTF8( item->m_Netname );
wxString layerName = aBoard->GetLayerName( item->GetLayer() );
2008-01-23 22:36:37 +00:00
polygon->layer_id = CONV_TO_UTF8( layerName );
int count = item->m_Poly->corner.size();
for( int j=0; j<count; ++j )
{
wxPoint point( item->m_Poly->corner[j].x, item->m_Poly->corner[j].y );
polygon->points.push_back( mapPt(point) );
}
pcb->structure->planes.push_back( plane );
}
}
2008-01-24 21:47:54 +00:00
// 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.
2008-01-23 22:36:37 +00:00
2008-01-29 16:45:14 +00:00
//-----<build the initial padstack list>--------------------------------
2008-01-23 22:36:37 +00:00
{
2008-01-24 21:47:54 +00:00
TYPE_COLLECTOR pads;
2008-01-29 16:45:14 +00:00
static const KICAD_T scanPADs[] = { TYPEPAD, EOT };
// get all the D_PADs into 'pads'.
2008-01-24 21:47:54 +00:00
pads.Collect( aBoard, scanPADs );
2008-01-29 16:45:14 +00:00
makePADSTACKs( aBoard, pads );
2008-01-26 02:02:27 +00:00
2008-01-25 22:03:36 +00:00
#if 0 && defined(DEBUG)
2008-01-24 21:47:54 +00:00
for( int p=0; p<pads.GetCount(); ++p )
pads[p]->Show( 0, std::cout );
#endif
2008-01-26 02:02:27 +00:00
}
2008-01-29 16:45:14 +00:00
2008-01-26 02:02:27 +00:00
//-----<build the images and components>---------------------------------
2008-01-26 02:02:27 +00:00
{
2008-01-24 21:47:54 +00:00
static const KICAD_T scanMODULEs[] = { TYPEMODULE, EOT };
items.Collect( aBoard, scanMODULEs );
2008-01-29 16:45:14 +00:00
2008-01-24 21:47:54 +00:00
for( int m=0; m<items.GetCount(); ++m )
2008-01-23 22:36:37 +00:00
{
2008-01-24 21:47:54 +00:00
MODULE* module = (MODULE*) items[m];
2008-01-29 16:45:14 +00:00
IMAGE* image = makeIMAGE( module );
IMAGE* registered = pcb->library->LookupIMAGE( image );
if( registered != image )
2008-01-24 21:47:54 +00:00
{
// If our new 'image' is not a unique IMAGE, delete it.
// In either case, 'registered' is the one we'll work with henceforth.
2008-01-29 16:45:14 +00:00
delete image;
}
2008-02-01 01:09:39 +00:00
COMPONENT* comp = pcb->placement->LookupCOMPONENT( registered->image_id );
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() );
2008-01-30 19:16:46 +00:00
2008-02-01 01:09:39 +00:00
// not supported by freerouting.net yet:
2008-01-30 19:16:46 +00:00
place->part_number = CONV_TO_UTF8( module->GetValue() );
// module is flipped from bottom side, set side to T_back
if( module->flag )
place->side = T_back;
2008-01-29 16:45:14 +00:00
}
}
//-----<create the nets>------------------------------------------------
{
NETWORK* network = pcb->network;
static const KICAD_T scanNETs[] = { PCB_EQUIPOT_STRUCT_TYPE, EOT };
items.Collect( aBoard, scanNETs );
PIN_REF emptypin(0);
for( int i=0; i<items.GetCount(); ++i )
{
2008-01-30 19:16:46 +00:00
EQUIPOT* kinet = (EQUIPOT*) items[i];
if( kinet->GetNet() == 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();
D_PAD** ppad = kinet->m_PadzoneStart;
for( ; ppad < kinet->m_PadzoneEnd; ++ppad )
{
D_PAD* pad = *ppad;
wxASSERT( pad->Type() == TYPEPAD );
// 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*)pad->m_Parent)->GetReference() );;
pin_ref->pin_id = CONV_TO_UTF8( pad->ReturnStringPadName() );
}
}
}
2008-01-30 19:16:46 +00:00
//-----<create the wires from tracks>-----------------------------------
{
// export all of them for now, later we'll decide what controls we need
// on this.
2008-02-01 01:09:39 +00:00
static const KICAD_T scanTRACKs[] = { TYPETRACK, EOT };
2008-01-30 19:16:46 +00:00
items.Collect( aBoard, scanTRACKs );
2008-01-31 01:30:52 +00:00
2008-01-31 06:46:31 +00:00
/*
2008-01-31 01:30:52 +00:00
if( items.GetCount() )
qsort( (void*) items.BasePtr(), items.GetCount(),
sizeof(TRACK*), Track_list_Sort_by_Netcode );
2008-01-31 06:46:31 +00:00
*/
2008-01-31 01:30:52 +00:00
2008-01-31 06:46:31 +00:00
std::string netname;
WIRING* wiring = pcb->wiring;
PATH* path = 0;
2008-01-31 01:30:52 +00:00
2008-01-31 06:46:31 +00:00
int old_netcode = -1;
int old_width = -1;
int old_layer = -1;
2008-01-31 01:30:52 +00:00
2008-01-30 19:16:46 +00:00
for( int i=0; i<items.GetCount(); ++i )
{
2008-01-31 06:46:31 +00:00
TRACK* track = (TRACK*) items[i];
2008-01-30 19:16:46 +00:00
2008-01-31 01:30:52 +00:00
if( track->GetNet() == 0 )
continue;
2008-01-31 06:46:31 +00:00
if( old_netcode != track->GetNet()
|| old_width != track->m_Width
|| old_layer != track->GetLayer()
|| (path && path->points.back() != mapPt(track->m_Start) )
)
2008-01-31 01:30:52 +00:00
{
2008-01-31 06:46:31 +00:00
old_width = track->m_Width;
old_layer = track->GetLayer();
2008-01-31 01:30:52 +00:00
2008-01-31 06:46:31 +00:00
if( old_netcode != track->GetNet() )
2008-01-31 01:30:52 +00:00
{
2008-01-31 06:46:31 +00:00
old_netcode = track->GetNet();
EQUIPOT* equipot = aBoard->FindNet( track->GetNet() );
wxASSERT( equipot );
netname = CONV_TO_UTF8( equipot->m_Netname );
2008-01-31 01:30:52 +00:00
}
2008-01-31 06:46:31 +00:00
WIRE* wire = new WIRE( wiring );
wiring->wires.push_back( wire );
2008-02-01 01:09:39 +00:00
wire->net_id = netname;
wire->wire_type = T_protect; // @todo, this should be configurable
2008-01-31 06:46:31 +00:00
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 ) );
2008-01-31 01:30:52 +00:00
}
2008-01-31 06:46:31 +00:00
path->AppendPoint( mapPt( track->m_End ) );
2008-01-30 19:16:46 +00:00
}
2008-02-01 01:09:39 +00:00
}
//-----<export the existing real instantiated vias>---------------------
{
// export all of them for now, later we'll decide what controls we need
// on this.
static const KICAD_T scanVIAs[] = { TYPEVIA, EOT };
2008-01-31 06:46:31 +00:00
2008-02-01 01:09:39 +00:00
items.Collect( aBoard, scanVIAs );
2008-01-31 06:46:31 +00:00
2008-02-01 01:09:39 +00:00
for( int i=0; i<items.GetCount(); ++i )
{
SEGVIA* via = (SEGVIA*) items[i];
wxASSERT( via->Type() == 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_protect; // @todo, this should be configurable
}
}
//-----<via_descriptor>-------------------------------------------------
{
// 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() );
}
}
2008-01-30 19:16:46 +00:00
}
2008-02-01 01:09:39 +00:00
2008-01-29 16:45:14 +00:00
//-----<restore MODULEs>------------------------------------------------
2008-01-22 20:48:02 +00:00
2008-01-25 22:03:36 +00:00
// 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;
}
}
2008-01-22 20:48:02 +00:00
}
2008-01-21 21:24:39 +00:00
} // namespace DSN