kicad/pcbnew/specctra_export.cpp

1406 lines
44 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.
2008-02-07 20:23:58 +00:00
*
2008-01-21 21:24:39 +00:00
* 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.
2008-02-07 20:23:58 +00:00
*
2008-01-21 21:24:39 +00:00
* 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.
2008-02-07 20:23:58 +00:00
*
2008-01-21 21:24:39 +00:00
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
2008-02-07 20:23:58 +00:00
* 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.,
2008-01-21 21:24:39 +00:00
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
2008-02-07 20:23:58 +00:00
2008-01-21 21:24:39 +00:00
/* 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
2008-02-07 20:23:58 +00:00
2008-01-21 21:24:39 +00:00
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-02-12 01:02:53 +00:00
#include "trigo.h" // RotatePoint()
2008-02-09 08:34:45 +00:00
#include <set> // std::set
2008-02-14 01:07:52 +00:00
#include <map> // std::map
2008-02-01 01:09:39 +00:00
2008-02-14 01:07:52 +00:00
#include <boost/utility.hpp> // boost::addressof()
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
2008-02-06 22:32:15 +00:00
void WinEDA_PcbFrame::ExportToSpecctra( wxCommandEvent& event )
2008-01-21 21:24:39 +00:00
{
2008-01-22 20:48:02 +00:00
wxString fullFileName = GetScreen()->m_FileName;
wxString path;
wxString name;
wxString ext;
2008-02-07 20:23:58 +00:00
wxString dsn_ext = wxT( ".dsn" );
wxString mask = wxT( "*" ) + dsn_ext;
wxFileName::SplitPath( fullFileName, &path, &name, &ext );
name += dsn_ext;
2008-02-07 20:23:58 +00:00
2008-01-22 20:48:02 +00:00
fullFileName = EDA_FileSelector( _( "Specctra DSN file:" ),
path,
name, // name.ext without path!
dsn_ext,
mask,
2008-01-22 20:48:02 +00:00
this,
wxFD_SAVE,
FALSE
);
if( fullFileName == wxEmptyString )
return;
2008-01-21 22:16:45 +00:00
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-02-01 20:32:18 +00:00
BASE_SCREEN* screen = GetScreen();
bool wasModified = screen->IsModify() && !screen->IsSave();
2008-02-07 20:23:58 +00:00
2008-01-21 22:16:45 +00:00
db.SetPCB( SPECCTRA_DB::MakePCB() );
2008-01-24 21:47:54 +00:00
2008-02-07 20:23:58 +00:00
setlocale( LC_NUMERIC, "C" ); // Switch the locale to standard C
2008-02-29 06:49:34 +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.
db.FlipMODULEs( m_Pcb );
2008-02-07 20:23:58 +00:00
try
{
2008-01-21 22:16:45 +00:00
db.FromBOARD( m_Pcb );
2008-01-22 20:48:02 +00:00
db.ExportPCB( fullFileName, true );
2008-02-07 20:23:58 +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-02-07 20:23:58 +00:00
}
catch( IOError ioe )
2008-01-21 22:16:45 +00:00
{
2008-01-24 21:47:54 +00:00
ok = false;
2008-02-07 20:23:58 +00:00
// copy the error string to safe place, ioe is in this scope only.
2008-01-24 21:47:54 +00:00
errorText = ioe.errorText;
}
2008-02-07 20:23:58 +00:00
setlocale( LC_NUMERIC, "" ); // revert to the current locale
2008-02-29 06:49:34 +00:00
// done assuredly, even if an exception was thrown and caught.
2008-02-14 15:34:40 +00:00
db.RevertMODULEs( m_Pcb );
2008-02-07 20:23:58 +00:00
// The two calls below to BOARD::Change_Side_Module(), both set the
// modified flag, yet their actions cancel each other out, so it should
2008-02-01 20:32:18 +00:00
// be ok to clear the modify flag.
if( !wasModified )
screen->ClrModify();
2008-02-07 20:23:58 +00:00
2008-01-24 21:47:54 +00:00
if( ok )
{
2008-02-07 20:23:58 +00:00
Affiche_Message( wxString( _("BOARD exported OK.")) );
2008-01-24 21:47:54 +00:00
}
else
2008-02-09 08:34:45 +00:00
{
errorText += '\n';
errorText += _("Unable to export, please fix and try again.");
2008-01-24 21:47:54 +00:00
DisplayError( this, errorText );
2008-02-09 08:34:45 +00:00
}
2008-01-21 21:24:39 +00:00
}
namespace DSN {
2008-01-22 20:48:02 +00:00
struct POINT_PAIR
{
2008-02-01 20:32:18 +00:00
POINT start;
POINT end;
2008-01-22 20:48:02 +00:00
BOARD_ITEM* item; ///< the item which has these points, TRACK or DRAWSEGMENT
};
2008-02-07 20:23:58 +00:00
typedef std::vector<POINT_PAIR> POINT_PAIRS;
2008-01-21 22:16:45 +00:00
2008-01-22 20:48:02 +00:00
2008-02-03 15:23:00 +00:00
const KICAD_T SPECCTRA_DB::scanPADs[] = { TYPEPAD, EOT };
2008-01-22 20:48:02 +00:00
static inline void swap( POINT_PAIR& pair )
{
2008-02-01 20:32:18 +00:00
POINT temp = pair.start;
pair.start = pair.end;
pair.end = temp;
2008-01-22 20:48:02 +00:00
}
/**
* 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
2008-02-01 20:32:18 +00:00
/**
* Function findPOINT
* searches the list of POINT_PAIRS for a matching end to the given POINT.
2008-02-07 06:49:16 +00:00
* @return int - 0 if no match, or positive one based index of a POINT_PAIR with a matching ".start",
* or a negated one based index of a POINT_PAIR with a matching ".end".
2008-02-01 20:32:18 +00:00
*/
static int findPOINT( const POINT& pt, const POINT_PAIR source[], int count )
{
for( int i=0; i<count; ++i )
{
if( pt == source[i].start )
{
2008-02-07 20:23:58 +00:00
return +( i + 1 );
2008-02-01 20:32:18 +00:00
}
2008-02-07 20:23:58 +00:00
2008-02-01 20:32:18 +00:00
if( pt == source[i].end )
{
2008-02-07 20:23:58 +00:00
return -( i + 1 );
2008-02-01 20:32:18 +00:00
}
}
2008-02-07 20:23:58 +00:00
2008-02-01 20:32:18 +00:00
return 0;
}
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 )
{
2008-02-01 20:32:18 +00:00
if( !aList.size() )
2008-01-22 20:48:02 +00:00
return;
2008-02-07 20:23:58 +00:00
2008-02-01 20:32:18 +00:00
// do an extraction sort based on matching ends here.
POINT_PAIRS sorted;
POINT_PAIRS source( aList );
// try and start the search using a POINT which has at least one match elsewhere.
if( findPOINT( source.begin()->start, &source[1], source.size()-1 ) != 0 )
swap( *source.begin() ); // swap start and end of first PAIR
2008-02-07 20:23:58 +00:00
2008-02-01 20:32:18 +00:00
while( source.size() )
2008-01-22 20:48:02 +00:00
{
2008-02-01 20:32:18 +00:00
sorted.push_back( *source.begin() );
source.erase( source.begin() );
2008-01-22 20:48:02 +00:00
2008-02-07 20:23:58 +00:00
// keep looping through the source list looking for a match to the end of the last sorted.
2008-02-01 20:32:18 +00:00
int result;
while( (result = findPOINT( sorted.back().end, &source[0], source.size() ) ) != 0 )
2008-01-22 20:48:02 +00:00
{
2008-02-07 20:23:58 +00:00
int ndx = ABS(result)-1;
2008-02-01 20:32:18 +00:00
sorted.push_back( source[ ndx ] );
source.erase( source.begin()+ndx );
2008-02-07 20:23:58 +00:00
2008-02-01 20:32:18 +00:00
if( result < 0 )
swap( sorted.back() );
2008-01-22 20:48:02 +00:00
}
}
2008-02-07 20:23:58 +00:00
2008-02-03 21:46:12 +00:00
#if 0 && defined(DEBUG)
2008-02-01 20:32:18 +00:00
printf( "swapEnds():\n" );
for( unsigned i=0; i<sorted.size(); ++i )
{
2008-02-07 20:23:58 +00:00
printf( "(%.6g,%.6g) (%.6g,%.6g)\n",
sorted[i].start.x, sorted[i].start.y,
2008-02-01 20:32:18 +00:00
sorted[i].end.x, sorted[i].end.y );
}
#endif
2008-02-07 20:23:58 +00:00
2008-02-01 20:32:18 +00:00
aList = sorted;
2008-01-22 20:48:02 +00:00
}
/**
* Function isRectangle
2008-02-07 20:23:58 +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.
2008-02-07 20:23:58 +00:00
*/
2008-01-22 20:48:02 +00:00
static bool isRectangle( POINT_PAIRS& aList )
{
if( aList.size() == 4 )
{
for( unsigned i=0; i<aList.size(); ++i )
{
if( i < aList.size()-1 )
2008-02-01 20:32:18 +00:00
if( aList[i].end != aList[i+1].start )
2008-01-22 20:48:02 +00:00
return false;
2008-02-07 20:23:58 +00:00
if( aList[i].start.x != aList[i].end.x
2008-02-01 20:32:18 +00:00
&& aList[i].start.y != aList[i].end.y )
2008-01-22 20:48:02 +00:00
return false;
}
2008-02-07 20:23:58 +00:00
2008-02-01 20:32:18 +00:00
return ( aList[0].start == aList[3].end );
2008-01-22 20:48:02 +00:00
}
return false;
}
2008-02-09 08:34:45 +00:00
/**
* Function isKeepout
2008-02-14 01:59:31 +00:00
* decides if the pad is a copper-less through hole which needs to be made into
2008-02-09 08:34:45 +00:00
* a round keepout.
*/
static bool isKeepout( D_PAD* aPad )
{
return aPad->m_PadShape==PAD_CIRCLE && aPad->m_Drill.x >= aPad->m_Size.x;
}
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;
}
2008-02-14 01:07:52 +00:00
/**
* Struct wxString_less_than
* is used by the std:set<> and std::map<> instantiations below.
2008-02-14 01:07:52 +00:00
* See STRINGSET typedef and PINMAP typedef below.
*/
struct wxString_less_than
2008-01-29 16:45:14 +00:00
{
// a "less than" test on two wxStrings
2008-02-14 01:07:52 +00:00
bool operator()( const wxString& s1, const wxString& s2) const
{
return s1.Cmp( s2 ) < 0; // case specific wxString compare
}
};
2008-01-29 16:45:14 +00:00
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
/**
* Function makePADSTACK
* creates a PADSTACK which matches the given pad. Only pads which do not
* satisfy the function isKeepout() should be passed to this function.
* @param aPad The D_PAD which needs to be made into a PADSTACK.
* @return PADSTACK* - The created padstack, including its padstack_id.
*/
PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, D_PAD* aPad )
{
char name[80]; // padstack name builder
std::string uniqifier;
2008-01-29 16:45:14 +00:00
2008-02-14 01:07:52 +00:00
bool doLayer[2] = { // top and bottom layers only
aPad->IsOnLayer( LAYER_CMP_N ),
aPad->IsOnLayer( COPPER_LAYER_N )
};
2008-02-07 20:23:58 +00:00
// caller must do these checks before calling here.
2008-02-14 01:07:52 +00:00
wxASSERT( !isKeepout( aPad ) );
wxASSERT( doLayer[0] || doLayer[1] );
2008-01-29 16:45:14 +00:00
2008-02-14 01:07:52 +00:00
PADSTACK* padstack = new PADSTACK();
int reportedLayers = 0; // how many in reported padstack
const char* layerName[NB_COPPER_LAYERS];
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
if( aPad->m_Attribut==PAD_SMD || aPad->m_Attribut==PAD_CONN )
{
// PAD_SMD and PAD_CONN are reported on each layer for which
// they are present.
uniqifier = '[';
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
if( doLayer[0] )
{
layerName[reportedLayers++] = layerIds[0].c_str();
uniqifier += 'T'; // T for top, could have used a layer index here alternatively
}
2008-02-14 01:07:52 +00:00
if( doLayer[1] )
{
2008-02-14 01:07:52 +00:00
int pcbLayerNdx = kicadLayer2pcb[COPPER_LAYER_N];
layerName[reportedLayers++] = layerIds[ pcbLayerNdx ].c_str();
uniqifier += 'B'; // B for bottom
}
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
uniqifier += ']';
}
2008-02-12 02:09:39 +00:00
2008-02-14 01:07:52 +00:00
else // through hole pad
{
#if 0
/* Through hole pads are reported on the <reserved_layer_name>
"signal". Reporting through hole pads on the special
"signal" layer may have problems when power layers are in the layer
stack. See bottom of page 74 of the SECCTRA Design Language
Reference, May 2000. 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.
*/
2008-02-12 01:02:53 +00:00
2008-02-14 01:07:52 +00:00
reportedLayers = 1;
layerName[0] = "signal";
2008-02-14 01:07:52 +00:00
uniqifier = "[A]"; // A for all
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
#else
// Through hole pads are reported on *all* copper layers.
int copperLayers = aBoard->GetCopperLayerCount();
2008-02-12 02:09:39 +00:00
2008-02-14 01:07:52 +00:00
for( int layer=0; layer<copperLayers; ++layer )
{
layerName[reportedLayers++] = layerIds[layer].c_str();
}
2008-02-14 01:07:52 +00:00
uniqifier = "[A]"; // A for all
#endif
2008-01-29 16:45:14 +00:00
}
2008-02-07 20:23:58 +00:00
POINT dsnOffset;
if( aPad->m_Offset.x || aPad->m_Offset.y )
{
char offsetTxt[32];
wxPoint offset( aPad->m_Offset.x, aPad->m_Offset.y );
dsnOffset = mapPt( offset );
// using '(' or ')' would cause padstack name to be quote wrapped,
// so use other brackets, and {} locks freerouter.
sprintf( offsetTxt, "[%.6g,%.6g]", dsnOffset.x, dsnOffset.y );
uniqifier += offsetTxt;
}
2008-02-14 01:07:52 +00:00
switch( aPad->m_PadShape )
2008-02-05 02:13:16 +00:00
{
2008-02-14 01:07:52 +00:00
default:
case PAD_CIRCLE:
{
double diameter = scale(aPad->m_Size.x);
2008-02-01 01:09:39 +00:00
2008-02-14 01:07:52 +00:00
for( int ndx=0; ndx<reportedLayers; ++ndx )
{
SHAPE* shape = new SHAPE( padstack );
padstack->Append( shape );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
CIRCLE* circle = new CIRCLE( shape );
shape->SetShape( circle );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
circle->SetLayerId( layerName[ndx] );
circle->SetDiameter( diameter );
circle->SetVertex( dsnOffset );
2008-02-14 01:07:52 +00:00
}
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
snprintf( name, sizeof(name), "Round%sPad_%.6g_mil",
uniqifier.c_str(), scale(aPad->m_Size.x) );
name[ sizeof(name)-1 ] = 0;
2008-02-14 01:07:52 +00:00
padstack->SetPadstackId( name );
}
2008-02-05 02:13:16 +00:00
break;
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
case PAD_RECT:
{
double dx = scale( aPad->m_Size.x ) / 2.0;
double dy = scale( aPad->m_Size.y ) / 2.0;
2008-02-05 02:13:16 +00:00
2008-02-14 01:07:52 +00:00
POINT lowerLeft( -dx, -dy );
POINT upperRight( dx, dy );
2008-02-07 20:23:58 +00:00
lowerLeft += dsnOffset;
upperRight += dsnOffset;
2008-02-14 01:07:52 +00:00
for( int ndx=0; ndx<reportedLayers; ++ndx )
{
SHAPE* shape = new SHAPE( padstack );
padstack->Append( shape );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
RECTANGLE* rect = new RECTANGLE( shape );
shape->SetShape( rect );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
rect->SetLayerId( layerName[ndx] );
rect->SetCorners( lowerLeft, upperRight );
}
snprintf( name, sizeof(name), "Rect%sPad_%.6gx%.6g_mil",
uniqifier.c_str(), scale(aPad->m_Size.x), scale(aPad->m_Size.y) );
name[ sizeof(name)-1 ] = 0;
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
padstack->SetPadstackId( name );
}
2008-02-05 02:13:16 +00:00
break;
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
case PAD_OVAL:
{
double dx = scale( aPad->m_Size.x ) / 2.0;
double dy = scale( aPad->m_Size.y ) / 2.0;
double dr = dx - dy;
double radius;
POINT start;
POINT stop;
2008-02-01 01:09:39 +00:00
2008-02-14 01:07:52 +00:00
if( dr >= 0 ) // oval is horizontal
{
radius = dy;
start = POINT( -dr, 0.0 );
stop = POINT( dr, 0.0 );
2008-02-14 01:07:52 +00:00
}
else // oval is vertical
{
radius = dx;
2008-02-14 01:07:52 +00:00
dr = -dr;
2008-02-01 01:09:39 +00:00
start = POINT( 0.0, -dr );
stop = POINT( 0.0, dr );
}
start += dsnOffset;
stop += dsnOffset;
for( int ndx=0; ndx<reportedLayers; ++ndx )
{
SHAPE* shape;
PATH* path;
// see http://www.freerouting.net/usren/viewtopic.php?f=3&t=317#p408
shape = new SHAPE( padstack );
padstack->Append( shape );
path = makePath( start, stop, layerName[ndx] );
shape->SetShape( path );
path->aperture_width = 2.0 * radius;
2008-02-14 01:07:52 +00:00
}
2008-02-01 01:09:39 +00:00
2008-02-14 01:07:52 +00:00
snprintf( name, sizeof(name), "Oval%sPad_%.6gx%.6g_mil",
uniqifier.c_str(), scale(aPad->m_Size.x), scale(aPad->m_Size.y) );
name[ sizeof(name)-1 ] = 0;
2008-02-01 01:09:39 +00:00
2008-02-14 01:07:52 +00:00
padstack->SetPadstackId( name );
}
break;
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
/*
case PAD_TRAPEZOID:
break;
*/
}
2008-02-07 20:23:58 +00:00
2008-02-01 01:09:39 +00:00
return padstack;
2008-02-07 20:23:58 +00:00
}
2008-02-01 01:09:39 +00:00
2008-02-29 06:49:34 +00:00
/// data type used to ensure unique-ness of pin names, holding (wxString and int)
2008-02-14 01:07:52 +00:00
typedef std::map<wxString, int, wxString_less_than> PINMAP;
2008-02-09 08:34:45 +00:00
2008-02-14 01:07:52 +00:00
IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, MODULE* aModule )
2008-01-24 21:47:54 +00:00
{
2008-02-14 01:07:52 +00:00
PINMAP pinmap;
TYPE_COLLECTOR moduleItems;
2008-02-14 01:07:52 +00:00
wxString padName;
2008-01-24 21:47:54 +00:00
2008-01-25 22:03:36 +00:00
2008-02-14 01:07:52 +00:00
// get all the MODULE's pads.
moduleItems.Collect( aModule, scanPADs );
2008-01-24 21:47:54 +00:00
2008-02-14 01:07:52 +00:00
IMAGE* image = new IMAGE(0);
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
image->image_id = CONV_TO_UTF8( aModule->m_LibRef );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
// from the pads, and make an IMAGE using collated padstacks.
for( int p=0; p<moduleItems.GetCount(); ++p )
2008-02-14 01:07:52 +00:00
{
D_PAD* pad = (D_PAD*) moduleItems[p];
2008-01-24 21:47:54 +00:00
2008-02-14 01:07:52 +00:00
// see if this pad is a through hole with no copper on its perimeter
if( isKeepout( pad ) )
2008-01-24 21:47:54 +00:00
{
2008-02-14 01:07:52 +00:00
double diameter = scale( pad->m_Drill.x );
POINT vertex = mapPt( pad->m_Pos0 );
2008-01-24 21:47:54 +00:00
2008-02-14 01:07:52 +00:00
int layerCount = aBoard->GetCopperLayerCount();
for( int layer=0; layer<layerCount; ++layer )
{
KEEPOUT* keepout = new KEEPOUT(image, T_keepout);
image->keepouts.push_back( keepout );
2008-02-08 15:00:50 +00:00
CIRCLE* circle = new CIRCLE( keepout );
2008-02-14 01:07:52 +00:00
keepout->SetShape( circle );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
circle->SetDiameter( diameter );
circle->SetVertex( vertex );
circle->SetLayerId( layerIds[layer].c_str() );
}
}
else
2008-02-08 15:00:50 +00:00
{
2008-02-14 01:07:52 +00:00
PADSTACK* padstack = makePADSTACK( aBoard, pad );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
PADSTACKSET::iterator iter = padstackset.find( *padstack );
if( iter != padstackset.end() )
2008-02-08 15:00:50 +00:00
{
2008-02-14 01:07:52 +00:00
// padstack is a duplicate, delete it and use the original
delete padstack;
padstack = (PADSTACK*) *iter.base(); // folklore, be careful here
2008-02-08 15:00:50 +00:00
}
2008-02-14 01:07:52 +00:00
else
2008-02-08 15:00:50 +00:00
{
2008-02-14 01:07:52 +00:00
padstackset.insert( padstack );
2008-02-08 15:00:50 +00:00
}
2008-01-31 01:30:52 +00:00
2008-02-14 01:07:52 +00:00
PIN* pin = new PIN(image);
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
padName = pad->ReturnStringPadName();
pin->pin_id = CONV_TO_UTF8( padName );
if( padName!=wxEmptyString && pinmap.find( padName )==pinmap.end() )
2008-01-24 21:47:54 +00:00
{
2008-02-14 01:07:52 +00:00
pinmap[ padName ] = 0;
}
else // pad name is a duplicate within this module
2008-02-14 01:07:52 +00:00
{
char buf[32];
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
int duplicates = ++pinmap[ padName ];
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
sprintf( buf, "@%d", duplicates );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
pin->pin_id += buf; // append "@1" or "@2", etc. to pin name
}
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
pin->kiNetCode = pad->GetNet();
2008-01-24 21:47:54 +00:00
2008-02-14 01:07:52 +00:00
image->pins.push_back( pin );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
pin->padstack_id = padstack->padstack_id;
2008-01-24 21:47:54 +00:00
2008-02-14 01:07:52 +00:00
int angle = pad->m_Orient - aModule->m_Orient; // tenths of degrees
if( angle )
{
NORMALIZE_ANGLE_POS(angle);
pin->SetRotation( angle / 10.0 );
}
2008-02-07 20:23:58 +00:00
wxPoint pos( pad->m_Pos0 );
2008-01-24 21:47:54 +00:00
2008-02-14 01:07:52 +00:00
pin->SetVertex( mapPt( pos ) );
}
}
2008-02-07 20:23:58 +00:00
2008-03-01 13:15:41 +00:00
#if 1 // enable image (outline) scopes.
static const KICAD_T scanEDGEs[] = { TYPEEDGEMODULE, EOT };
// get all the MODULE's EDGE_MODULEs and convert those to DSN outlines.
moduleItems.Collect( aModule, scanEDGEs );
for( int i=0; i<moduleItems.GetCount(); ++i )
{
EDGE_MODULE* graphic = (EDGE_MODULE*) moduleItems[i];
SHAPE* outline;
PATH* path;
switch( graphic->m_Shape )
{
case S_SEGMENT:
outline = new SHAPE( image, T_outline );
image->Append( outline );
path = new PATH( outline );
outline->SetShape( path );
path->SetAperture( scale( graphic->m_Width ) );
path->SetLayerId( "signal" );
path->AppendPoint( mapPt( graphic->m_Start0 ) );
path->AppendPoint( mapPt( graphic->m_End0 ) );
break;
case S_CIRCLE:
{
// this is best done by 4 QARC's but freerouter does not yet support QARCs.
// for now, support by using line segments.
outline = new SHAPE( image, T_outline );
image->Append( outline );
path = new PATH( outline );
outline->SetShape( path );
path->SetAperture( scale( graphic->m_Width ) );
path->SetLayerId( "signal" );
2008-02-29 06:49:34 +00:00
// Do the math using Kicad units, that way we stay out of the
// scientific notation range of floating point numbers in the
// DSN file. We do not parse scientific notation in our own
// lexer/beautifier, and the spec is not clear that this is
// required. Fixed point floats are all that should be needed.
2008-02-29 06:49:34 +00:00
double radius = hypot( double( graphic->m_Start.x - graphic->m_End.x ),
double( graphic->m_Start.y - graphic->m_End.y ) );
// better if evenly divisible into 360
const int DEGREE_INTERVAL = 18; // 18 means 20 line segments
for( double radians = 0.0; radians < 2*M_PI; radians += DEGREE_INTERVAL * M_PI / 180.0 )
{
2008-02-29 06:49:34 +00:00
wxPoint point( int( radius * cos( radians ) ),
int( radius * sin( radians ) ) );
point += graphic->m_Start0; // an offset
path->AppendPoint( mapPt(point) );
}
}
break;
case S_RECT:
case S_ARC:
default:
D( printf("makeIMAGE(): unsupported shape %s\n", EDGE_MODULE::ShowShape(graphic->m_Shape) );)
continue;
}
}
2008-03-01 13:15:41 +00:00
#endif
2008-02-14 01:07:52 +00:00
return image;
}
2008-02-07 20:23:58 +00:00
2008-01-25 22:03:36 +00:00
2008-02-14 01:07:52 +00:00
PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter, int aDrillDiameter,
int aTopLayer, int aBotLayer )
{
char name[48];
PADSTACK* padstack = new PADSTACK();
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
double dsnDiameter = scale(aCopperDiameter);
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
for( int layer=aTopLayer; layer<=aBotLayer; ++layer )
{
SHAPE* shape = new SHAPE( padstack );
padstack->Append( shape );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
CIRCLE* circle = new CIRCLE( shape );
shape->SetShape( circle );
2008-01-25 22:03:36 +00:00
2008-02-14 01:07:52 +00:00
circle->SetDiameter( dsnDiameter );
circle->SetLayerId( layerIds[layer].c_str() );
2008-01-24 21:47:54 +00:00
}
2008-01-26 02:02:27 +00:00
2008-02-14 01:07:52 +00:00
snprintf( name, sizeof(name), "Via[%d-%d]_%.6g:%.6g_mil",
aTopLayer, aBotLayer, dsnDiameter,
// encode the drill value into the name for later import
scale( aDrillDiameter )
);
name[ sizeof(name)-1 ] = 0;
padstack->SetPadstackId( name );
2008-01-26 02:02:27 +00:00
2008-02-14 01:07:52 +00:00
return padstack;
}
2008-01-29 16:45:14 +00:00
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
PADSTACK* SPECCTRA_DB::makeVia( const SEGVIA* aVia )
{
int topLayer;
int botLayer;
2008-01-26 02:02:27 +00:00
2008-02-14 01:07:52 +00:00
aVia->ReturnLayerPair( &topLayer, &botLayer );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
topLayer = kicadLayer2pcb[topLayer];
botLayer = kicadLayer2pcb[botLayer];
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
if( topLayer > botLayer )
EXCHG( topLayer, botLayer );
return makeVia( aVia->m_Width, aVia->GetDrillValue(), topLayer, botLayer );
2008-01-21 22:16:45 +00:00
}
2008-01-22 20:48:02 +00:00
2008-02-29 06:49:34 +00:00
typedef std::set<std::string> STRINGSET;
2008-02-14 01:07:52 +00:00
typedef std::pair<STRINGSET::iterator, bool> STRINGSET_PAIR;
2008-02-09 08:34:45 +00:00
void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError )
2008-01-22 20:48:02 +00:00
{
TYPE_COLLECTOR items;
POINT_PAIRS ppairs;
POINT_PAIR pair;
2008-02-03 15:23:00 +00:00
static const KICAD_T scanMODULEs[] = { TYPEMODULE, EOT };
2008-02-07 20:23:58 +00:00
2008-02-09 08:34:45 +00:00
// Not all boards are exportable. Check that all reference Ids are unique.
// Unless they are unique, we cannot import the session file which comes
2008-02-14 01:07:52 +00:00
// back to us later from the router. Also check that all pad names within
// a part are unique, otherwise Electra and Freerouter will not draw the
// pads properly.
2008-02-09 08:34:45 +00:00
{
2008-02-14 01:07:52 +00:00
TYPE_COLLECTOR padItems;
2008-02-09 08:34:45 +00:00
2008-02-14 01:07:52 +00:00
items.Collect( aBoard, scanMODULEs );
2008-02-09 08:34:45 +00:00
2008-02-14 01:07:52 +00:00
STRINGSET refs; // holds module reference designators
2008-02-09 08:34:45 +00:00
for( int i=0; i<items.GetCount(); ++i )
{
MODULE* module = (MODULE*) items[i];
if( module->GetReference() == wxEmptyString )
{
ThrowIOError( _("Component with value of \"%s\" has empty reference id."),
module->GetValue().GetData() );
}
// if we cannot insert OK, that means the reference has been seen before.
2008-02-29 06:49:34 +00:00
STRINGSET_PAIR refpair = refs.insert( CONV_TO_UTF8( module->GetReference() ) );
2008-02-14 01:07:52 +00:00
if( !refpair.second ) // insert failed
2008-02-09 08:34:45 +00:00
{
ThrowIOError( _("Multiple components have identical reference IDs of \"%s\"."),
module->GetReference().GetData() );
}
}
}
2008-02-07 20:23:58 +00:00
2008-01-22 20:48:02 +00:00
if( !pcb )
pcb = SPECCTRA_DB::MakePCB();
2008-01-31 01:30:52 +00:00
//-----<layer_descriptor>-----------------------------------------------
{
2008-02-07 20:23:58 +00:00
// specctra wants top physical layer first, then going down to the
2008-01-31 01:30:52 +00:00
// bottom most physical layer in physical sequence.
// @question : why does Kicad not display layers in that order?
2008-01-31 06:46:31 +00:00
buildLayerMaps( aBoard );
2008-02-07 20:23:58 +00:00
int layerCount = aBoard->GetCopperLayerCount();
2008-01-31 01:30:52 +00:00
for( int pcbNdx=0; pcbNdx<layerCount; ++pcbNdx )
{
2008-01-31 01:30:52 +00:00
LAYER* layer = new LAYER( pcb->structure );
pcb->structure->layers.push_back( layer );
2008-02-07 20:23:58 +00:00
layer->name = layerIds[pcbNdx];
2008-02-07 20:23:58 +00:00
2008-03-04 04:22:27 +00:00
DSN_T layerType;
switch( aBoard->GetLayerType( pcbLayer2kicad[pcbNdx] ) )
{
default:
case LT_SIGNAL: layerType = T_signal; break;
case LT_POWER: layerType = T_power; break;
case LT_MIXED: layerType = T_mixed; break;
}
layer->layer_type = layerType;
2008-02-05 02:13:16 +00:00
layer->properties.push_back( PROPERTY() );
PROPERTY* property = &layer->properties.back();
property->name = "index";
char temp[32];
sprintf( temp, "%d", pcbNdx );
2008-02-07 20:23:58 +00:00
property->value = temp;
2008-01-31 01:30:52 +00:00
// layer->type = @todo need this, the export would be better.
}
}
// a space in a quoted token is NOT a terminator, true establishes this.
pcb->parser->space_in_quoted_tokens = true;
2008-02-07 20:23:58 +00:00
2008-01-23 01:52:49 +00:00
//-----<unit_descriptor> & <resolution_descriptor>--------------------
2008-02-07 20:23:58 +00:00
{
2008-01-23 01:52:49 +00:00
pcb->unit->units = T_mil;
pcb->resolution->units = T_mil;
pcb->resolution->value = 100;
}
2008-01-22 20:48:02 +00:00
2008-02-07 20:23:58 +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 );
2008-02-07 20:23:58 +00:00
2008-01-23 01:52:49 +00:00
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];
2008-02-07 20:23:58 +00:00
2008-01-23 01:52:49 +00:00
wxASSERT( item->Type() == TYPEDRAWSEGMENT );
2008-02-07 20:23:58 +00:00
2008-01-23 01:52:49 +00:00
if( item->GetLayer() == EDGE_N )
{
2008-02-01 20:32:18 +00:00
pair.start = mapPt( item->m_Start );
pair.end = mapPt( item->m_End );
2008-01-23 01:52:49 +00:00
pair.item = item;
ppairs.push_back( pair );
haveEdges = true;
}
2008-01-22 20:48:02 +00:00
}
2008-02-07 20:23:58 +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-02-07 20:23:58 +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-02-07 20:23:58 +00:00
#endif
2008-01-23 01:52:49 +00:00
BOUNDARY* boundary = new BOUNDARY(0);
2008-02-07 20:23:58 +00:00
2008-01-23 01:52:49 +00:00
if( isRectangle( ppairs ) )
{
RECTANGLE* rect = new RECTANGLE( boundary );
rect->layer_id = "pcb";
2008-02-07 20:23:58 +00:00
2008-01-23 01:52:49 +00:00
// opposite corners
2008-02-01 20:32:18 +00:00
rect->SetCorners( ppairs[0].start, ppairs[2].start );
2008-02-07 20:23:58 +00:00
2008-01-23 01:52:49 +00:00
boundary->rectangle = rect;
}
else
{
PATH* path = new PATH( boundary );
2008-02-01 20:52:49 +00:00
boundary->paths.push_back( path );
2008-02-07 20:23:58 +00:00
2008-01-23 01:52:49 +00:00
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.
2008-02-01 20:32:18 +00:00
path->points.push_back( ppairs[i].start );
2008-01-23 01:52:49 +00:00
}
}
2008-02-07 20:23:58 +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
else
2008-01-22 20:48:02 +00:00
{
2008-01-23 01:52:49 +00:00
aBoard->ComputeBoundaryBox();
2008-02-07 20:23:58 +00:00
2008-01-23 01:52:49 +00:00
BOUNDARY* boundary = new BOUNDARY(0);
RECTANGLE* rect = new RECTANGLE( boundary );
2008-02-07 20:23:58 +00:00
2008-01-22 20:48:02 +00:00
rect->layer_id = "pcb";
2008-02-07 20:23:58 +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-02-07 20:23:58 +00:00
rect->SetCorners( mapPt( aBoard->m_BoundaryBox.GetOrigin() ),
mapPt( bottomRight ) );
2008-02-07 20:23:58 +00:00
2008-01-23 01:52:49 +00:00
boundary->rectangle = rect;
2008-02-07 20:23:58 +00:00
2008-01-23 01:52:49 +00:00
pcb->structure->SetBOUNDARY( boundary );
2008-01-22 20:48:02 +00:00
}
}
2008-02-07 20:23:58 +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
2008-02-09 08:34:45 +00:00
char rule[80];
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
int curTrackWidth = aBoard->m_BoardSettings->m_CurrentTrackWidth;
int curTrackClear = aBoard->m_BoardSettings->m_TrackClearence;
2008-02-09 16:33:03 +00:00
2008-02-29 06:49:34 +00:00
// The +1 is to give freerouter a little extra room, this is 0.1 mils.
2008-02-09 16:33:03 +00:00
// If we export without this, then on import freerouter violates our
2008-02-29 06:49:34 +00:00
// DRC checks with track to via spacing, although this could be a
// result of > testing vs. >= testing in PCBNEW's DRC.
double clearance = scale(curTrackClear+1);
2008-02-09 16:33:03 +00:00
2008-01-31 06:46:31 +00:00
STRINGS& rules = pcb->structure->rules->rules;
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
sprintf( rule, "(width %.6g)", scale( curTrackWidth ) );
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
sprintf( rule, "(clearance %.6g)", clearance );
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
sprintf( rule, "(clearance %.6g (type pad_to_turn_gap))", clearance );
2008-01-31 06:46:31 +00:00
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
sprintf( rule, "(clearance %.6g (type smd_to_turn_gap))", clearance );
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
sprintf( rule, "(clearance %.6g (type via_via))", clearance );
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
sprintf( rule, "(clearance %.6g (type via_smd))", clearance );
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
sprintf( rule, "(clearance %.6g (type via_pin))", clearance );
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
sprintf( rule, "(clearance %.6g (type pin_pin))", clearance );
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
sprintf( rule, "(clearance %.6g (type smd_pin))", clearance );
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-02-09 16:33:03 +00:00
// well, the user is going to text edit these in the DSN file anyway,
// at least until we have an export dialog.
clearance = scale(curTrackClear)/4;
sprintf( rule, "(clearance %.6g (type smd_smd))", clearance );
2008-01-31 06:46:31 +00:00
rules.push_back( rule );
}
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
//-----<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-02-07 20:23:58 +00:00
2008-01-31 01:30:52 +00:00
plane->name = CONV_TO_UTF8( item->m_Netname );
2008-02-29 06:49:34 +00:00
polygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
2008-02-07 20:23:58 +00:00
2008-01-23 22:36:37 +00:00
int count = item->m_Poly->corner.size();
for( int j=0; j<count; ++j )
{
2008-02-09 08:34:45 +00:00
wxPoint point( item->m_Poly->corner[j].x,
item->m_Poly->corner[j].y );
2008-02-29 06:49:34 +00:00
polygon->AppendPoint( mapPt(point) );
2008-01-23 22:36:37 +00:00
}
2008-02-07 20:23:58 +00:00
2008-01-23 22:36:37 +00:00
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.
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
//-----<build the images, components, and netlist>-----------------------
2008-01-23 22:36:37 +00:00
{
2008-02-29 06:49:34 +00:00
PIN_REF empty( pcb->network );
std::string componentId;
2008-02-14 01:07:52 +00:00
// find the highest numbered netCode within the board.
int highestNetCode = -1;
for( EQUIPOT* equipot = aBoard->m_Equipots; equipot; equipot = equipot->Next() )
highestNetCode = MAX( highestNetCode, equipot->GetNet() );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
deleteNETs();
2008-01-24 21:47:54 +00:00
2008-02-14 01:07:52 +00:00
// expand the net vector to highestNetCode+1, setting empty to NULL
nets.resize( highestNetCode+1, NULL );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
// skip netcode = 0
for( unsigned i=1; i<nets.size(); ++i )
nets[i] = new NET( pcb->network );
2008-01-29 16:45:14 +00:00
2008-02-14 01:07:52 +00:00
for( EQUIPOT* equipot = aBoard->m_Equipots; equipot; equipot = equipot->Next() )
{
int netcode = equipot->GetNet();
if( netcode > 0 )
nets[ netcode ]->net_id = CONV_TO_UTF8( equipot->m_Netname );
}
2008-02-07 20:23:58 +00:00
2008-01-24 21:47:54 +00:00
items.Collect( aBoard, scanMODULEs );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
padstackset.clear();
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-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
IMAGE* image = makeIMAGE( aBoard, module );
2008-02-29 06:49:34 +00:00
componentId = CONV_TO_UTF8( module->GetReference() );
2008-02-14 01:07:52 +00:00
// create a net list entry for all the actual pins in the image
// for the current module. location of this code is critical
// because we fabricated some pin names to ensure unique-ness
2008-02-29 06:49:34 +00:00
// of pin names within a module, do not move this code because
// the life of this 'IMAGE* image' is not necessarily long. The
2008-02-14 01:07:52 +00:00
// exported netlist will have some fabricated pin names in it.
// If you don't like fabricated pin names, then make sure all pads
// within your MODULEs are uniquely named!
for( unsigned p=0; p<image->pins.size(); ++p )
{
PIN* pin = &image->pins[p];
int netcode = pin->kiNetCode;
if( netcode > 0 )
{
NET* net = nets[netcode];
net->pins.push_back( empty );
PIN_REF& pin_ref = net->pins.back();
2008-02-29 06:49:34 +00:00
pin_ref.component_id = componentId;
2008-02-14 01:07:52 +00:00
pin_ref.pin_id = pin->pin_id;
}
}
2008-01-29 16:45:14 +00:00
2008-02-07 20:23:58 +00:00
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.
2008-02-14 01:07:52 +00:00
// and use the registered one, known as 'image' after this.
2008-01-29 16:45:14 +00:00
delete image;
2008-02-14 01:07:52 +00:00
image = registered;
2008-01-29 16:45:14 +00:00
}
2008-02-14 01:07:52 +00:00
COMPONENT* comp = pcb->placement->LookupCOMPONENT( image->GetImageId() );
2008-02-07 20:23:58 +00:00
PLACE* place = new PLACE( comp );
comp->places.push_back( place );
2008-02-07 20:23:58 +00:00
place->SetRotation( module->m_Orient/10.0 );
place->SetVertex( mapPt( module->m_Pos ) );
2008-02-29 06:49:34 +00:00
place->component_id = componentId;
2008-01-30 19:16:46 +00:00
place->part_number = CONV_TO_UTF8( module->GetValue() );
2008-02-07 20:23:58 +00:00
// module is flipped from bottom side, set side to T_back
2008-02-05 02:13:16 +00:00
if( module->flag )
{
int angle = 1800 - module->m_Orient;
NORMALIZE_ANGLE_POS(angle);
place->SetRotation( angle/10.0 );
2008-02-07 20:23:58 +00:00
place->side = T_back;
2008-02-05 02:13:16 +00:00
}
2008-01-29 16:45:14 +00:00
}
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
// copy the SPECCTRA_DB::padstackset to the LIBRARY. Since we are
// removing, do not increment the iterator
for( PADSTACKSET::iterator i=padstackset.begin(); i!=padstackset.end();
i=padstackset.begin() )
{
PADSTACKSET::auto_type ps = padstackset.release( i );
PADSTACK* padstack = ps.release();
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
pcb->library->AddPadstack( padstack );
}
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
// copy our SPECCTRA_DB::nets to the pcb->network
for( unsigned n=1; n<nets.size(); ++n )
{
NET* net = nets[n];
2008-02-29 06:49:34 +00:00
if( net->pins.size() )
2008-02-14 01:07:52 +00:00
{
// give ownership to pcb->network
pcb->network->nets.push_back( net );
nets[n] = 0;
}
}
}
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
//-----< output the vias >-----------------------------------------------
{
// ASSUME: unique pads are now in the padstack list! i.e. this code
// must follow the initial padstack construction code.
// Next we add the via's which may be used.
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
int defaultViaSize = aBoard->m_BoardSettings->m_CurrentViaSize;
2008-02-29 06:49:34 +00:00
/* I need at least one via for the (class...) scope below
2008-02-14 01:07:52 +00:00
if( defaultViaSize )
2008-02-29 06:49:34 +00:00
*/
2008-02-14 01:07:52 +00:00
{
PADSTACK* padstack = makeVia( defaultViaSize, g_DesignSettings.m_ViaDrill,
0, aBoard->GetCopperLayerCount()-1 );
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-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
for( int i=0; i<HISTORY_NUMBER; ++i )
{
2008-02-14 01:07:52 +00:00
int viaSize = aBoard->m_BoardSettings->m_ViaSizeHistory[i];
if( !viaSize )
break;
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
if( viaSize == defaultViaSize )
2008-01-30 19:16:46 +00:00
continue;
2008-02-14 01:07:52 +00:00
PADSTACK* padstack = makeVia( viaSize, g_DesignSettings.m_ViaDrill,
0, aBoard->GetCopperLayerCount()-1 );
pcb->library->AddPadstack( padstack );
}
}
2008-01-30 19:16:46 +00:00
2008-02-14 01:07:52 +00:00
2008-02-03 21:46:12 +00:00
#if 1 // do existing wires and vias
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-02-07 20:23:58 +00:00
2008-01-30 19:16:46 +00:00
items.Collect( aBoard, scanTRACKs );
2008-01-31 01:30:52 +00:00
2008-02-07 20:23:58 +00:00
std::string netname;
2008-01-31 06:46:31 +00:00
WIRING* wiring = pcb->wiring;
PATH* path = 0;
2008-01-31 01:30:52 +00:00
2008-02-07 20:23:58 +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-02-07 20:23:58 +00:00
2008-02-29 06:49:34 +00:00
int netcode = track->GetNet();
if( netcode == 0 )
2008-01-31 01:30:52 +00:00
continue;
2008-02-29 06:49:34 +00:00
if( old_netcode != netcode
2008-02-07 20:23:58 +00:00
|| old_width != track->m_Width
2008-01-31 06:46:31 +00:00
|| old_layer != track->GetLayer()
2008-02-07 20:23:58 +00:00
|| (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-02-29 06:49:34 +00:00
if( old_netcode != netcode )
2008-01-31 01:30:52 +00:00
{
2008-02-29 06:49:34 +00:00
old_netcode = netcode;
EQUIPOT* equipot = aBoard->FindNet( netcode );
2008-01-31 06:46:31 +00:00
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;
2008-02-07 20:23:58 +00:00
2008-02-21 06:04:32 +00:00
wire->wire_type = T_protect; // @todo, this should be configurable
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
int kiLayer = track->GetLayer();
int pcbLayer = kicadLayer2pcb[kiLayer];
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
path = new PATH( wire );
wire->SetShape( path );
2008-02-07 20:23:58 +00:00
2008-01-31 06:46:31 +00:00
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-02-07 20:23:58 +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
}
2008-02-07 20:23:58 +00:00
2008-02-09 08:34:45 +00:00
//-----<export the existing real BOARD instantiated vias>-----------------
2008-02-01 01:09:39 +00:00
{
// export all of them for now, later we'll decide what controls we need
// on this.
static const KICAD_T scanVIAs[] = { TYPEVIA, EOT };
2008-02-07 20:23:58 +00:00
2008-02-01 01:09:39 +00:00
items.Collect( aBoard, scanVIAs );
2008-02-07 20:23:58 +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 );
2008-02-07 20:23:58 +00:00
2008-02-21 06:04:32 +00:00
int netcode = via->GetNet();
if( netcode == 0 )
continue;
2008-02-01 01:09:39 +00:00
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 );
2008-02-07 20:23:58 +00:00
2008-02-01 01:09:39 +00:00
dsnVia->padstack_id = registered->padstack_id;
dsnVia->vertexes.push_back( mapPt( via->GetPosition() ) );
2008-02-07 20:23:58 +00:00
2008-02-01 01:09:39 +00:00
EQUIPOT* equipot = aBoard->FindNet( netcode );
wxASSERT( equipot );
2008-02-07 20:23:58 +00:00
2008-02-01 01:09:39 +00:00
dsnVia->net_id = CONV_TO_UTF8( equipot->m_Netname );
2008-02-07 20:23:58 +00:00
2008-02-21 06:04:32 +00:00
dsnVia->via_type = T_protect; // @todo, this should be configurable
2008-02-01 01:09:39 +00:00
}
}
2008-02-07 20:23:58 +00:00
#endif // do existing wires and vias
2008-02-01 01:09:39 +00:00
//-----<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 )
{
2008-02-29 06:49:34 +00:00
#if 1
2008-02-01 01:09:39 +00:00
for( ; viaNdx < (int)padstacks.size(); ++viaNdx )
{
vias->AppendVia( padstacks[viaNdx].padstack_id.c_str() );
}
2008-02-29 06:49:34 +00:00
#else
// output only the default via. Then use class_descriptors to
// override the default. No, this causes free router not to
// output the unmentioned vias into the session file.
vias->AppendVia( padstacks[viaNdx].padstack_id.c_str() );
#endif
2008-02-01 01:09:39 +00:00
}
2008-01-30 19:16:46 +00:00
}
2008-02-01 01:09:39 +00:00
2008-02-07 20:23:58 +00:00
2008-02-29 06:49:34 +00:00
//-----<output a default class with all nets and the via and track size>--
{
char text[80];
STRINGSET netIds; // sort the net names in here
CLASS* clazz = new CLASS( pcb->network );
pcb->network->classes.push_back( clazz );
// freerouter creates a class named 'default' anyway, and if we
// try and use that, we end up with two 'default' via rules so use
// something else as the name of our default class. Someday we may support
// additional classes. Until then the user can text edit the exported
// DSN file and use this class as a template, copying it and giving the
// copy a different class_id and splitting out some of the nets.
clazz->class_id = "kicad_default";
// Insert all the net_ids into the set. They are unique, but even if
// they were not the duplicated name is not our error, but the BOARD's.
// A duplicate would be removed here.
NETS& nets = pcb->network->nets;
for( NETS::iterator i=nets.begin(); i!=nets.end(); ++i )
netIds.insert( i->net_id );
// netIds is now sorted, put them into clazz->net_ids
for( STRINGSET::iterator i=netIds.begin(); i!=netIds.end(); ++i )
clazz->net_ids.push_back( *i );
// output the via and track dimensions, the whole reason for this scope.
int curTrackWidth = aBoard->m_BoardSettings->m_CurrentTrackWidth;
clazz->rules = new RULE( clazz, T_rule );
sprintf( text, "(width %.6g)", scale( curTrackWidth ) );
clazz->rules->rules.push_back( text );
2008-02-14 15:34:40 +00:00
2008-02-29 06:49:34 +00:00
int viaNdx = pcb->library->via_start_index;
sprintf( text, "(use_via %s)", pcb->library->padstacks[viaNdx].padstack_id.c_str() );
clazz->circuit.push_back( text );
}
2008-02-14 15:34:40 +00:00
}
2008-02-29 06:49:34 +00:00
void SPECCTRA_DB::FlipMODULEs( BOARD* aBoard )
2008-02-14 15:34:40 +00:00
{
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;
}
}
modulesAreFlipped = true;
}
void SPECCTRA_DB::RevertMODULEs( BOARD* aBoard )
{
if( !modulesAreFlipped )
return;
2008-02-07 20:23:58 +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-02-14 15:34:40 +00:00
modulesAreFlipped = false;
2008-01-22 20:48:02 +00:00
}
2008-02-07 20:23:58 +00:00
2008-01-21 21:24:39 +00:00
} // namespace DSN