kicad/pcbnew/specctra_export.cpp

2243 lines
74 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.
2008-01-21 21:24:39 +00:00
*
* Copyright (C) 2007-2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2015 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:
2008-09-17 13:32:43 +00:00
http://tech.groups.yahoo.com/group/kicad-users/files/ then file "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 <wxPcbStruct.h>
#include <pcbstruct.h> // HISTORY_NUMBER
#include <confirm.h> // DisplayError()
#include <gestfich.h> // EDA_FileSelector()
#include <trigo.h> // RotatePoint()
#include <macros.h>
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
#include <class_board.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <class_track.h>
#include <class_zone.h>
#include <class_drawsegment.h>
#include <base_units.h>
#include <collectors.h>
#include <geometry/shape_poly_set.h>
#include <specctra.h>
2008-01-21 22:16:45 +00:00
using namespace DSN;
2008-01-21 21:24:39 +00:00
2009-09-10 15:22:26 +00:00
// Add .1 mil to the requested clearances as a safety margin.
// There has been disagreement about interpretation of clearance in the past
// between KiCad and Freerouter, so keep this safetyMargin until the
2009-09-10 15:22:26 +00:00
// disagreement is resolved and stable. Freerouter seems to be moving
// (protected) traces upon loading the DSN file, and even though it seems to sometimes
// add its own 0.1 to the clearances, I believe this is happening after
// the load process (and moving traces) so I am of the opinion this is
// still needed.
static const double safetyMargin = 0.1;
/**
* Function close_ness
* is a non-exact distance calculator used to approximate the distance between
* two points. The distance is very in-exact, but can be helpful when used
* to pick between alternative neighboring points.
* @param aLeft is the first point
* @param aRight is the second point
* @return unsigned - a measure of proximity that the caller knows about, in BIU,
* but remember it is only an approximation.
*/
static unsigned close_ness( const wxPoint& aLeft, const wxPoint& aRight )
{
// Don't need an accurate distance calculation, just something
// approximating it, for relative ordering.
return unsigned( abs( aLeft.x - aRight.x ) + abs( aLeft.y - aRight.y ) );
}
/**
* Function close_enough
* is a local and tunable method of qualifying the proximity of two points.
*
* @param aLeft is the first point
* @param aRight is the second point
* @param aLimit is a measure of proximity that the caller knows about.
* @return bool - true if the two points are close enough, else false.
*/
inline bool close_enough( const wxPoint& aLeft, const wxPoint& aRight, unsigned aLimit )
{
// We don't use an accurate distance calculation, just something
// approximating it, since aLimit is non-exact anyway except when zero.
return close_ness( aLeft, aRight ) <= aLimit;
}
2009-09-10 15:22:26 +00:00
/**
* Function close_st
* is a local method of qualifying if either the start of end point of a segment is closest to a point.
*
* @param aReference is the reference point
* @param aFirst is the first point
* @param aSecond is the second point
* @return bool - true if the the first point is closest to the reference, otherwise false.
*/
inline bool close_st( const wxPoint& aReference, const wxPoint& aFirst, const wxPoint& aSecond )
{
// We don't use an accurate distance calculation, just something
// approximating to find the closest to the reference.
return close_ness( aReference, aFirst ) <= close_ness( aReference, aSecond );
}
2008-01-21 21:24:39 +00:00
// see wxPcbStruct.h
void PCB_EDIT_FRAME::ExportToSpecctra( wxCommandEvent& event )
2008-01-21 21:24:39 +00:00
{
wxString fullFileName = GetBoard()->GetFileName();
wxString path;
wxString name;
wxString ext;
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
fullFileName = EDA_FILE_SELECTOR( _( "Specctra DSN file:" ),
path,
name, // name.ext without path!
dsn_ext,
mask,
this,
wxFD_SAVE,
false );
2008-01-22 20:48:02 +00:00
if( fullFileName == wxEmptyString )
return;
2008-01-21 22:16:45 +00:00
ExportSpecctraFile( fullFileName );
}
bool PCB_EDIT_FRAME::ExportSpecctraFile( const wxString& aFullFilename )
{
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();
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
LOCALE_IO toggle; // Switch the locale to standard C
2008-02-07 20:23:58 +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( GetBoard() );
2008-02-29 06:49:34 +00:00
2008-02-07 20:23:58 +00:00
try
{
2009-09-10 15:22:26 +00:00
GetBoard()->SynchronizeNetsAndNetClasses();
db.FromBOARD( GetBoard() );
db.ExportPCB( aFullFilename, 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( const IO_ERROR& 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-29 06:49:34 +00:00
// done assuredly, even if an exception was thrown and caught.
db.RevertMODULEs( GetBoard() );
2008-02-14 15:34:40 +00:00
2009-08-06 07:11:04 +00:00
// The two calls below to MODULE::Flip(), both set the
2008-02-07 20:23:58 +00:00
// 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 )
{
SetStatusText( 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
}
return ok;
2008-01-21 21:24:39 +00:00
}
namespace DSN {
const KICAD_T SPECCTRA_DB::scanPADs[] = { PCB_PAD_T, EOT };
2008-02-03 15:23:00 +00:00
// "specctra reported units" are what we tell the external router that our
// exported lengths are in.
2008-02-03 15:23:00 +00:00
/**
* Function scale
* converts a distance from PCBNEW internal units to the reported specctra dsn units
* in floating point format.
*/
2008-01-24 21:47:54 +00:00
static inline double scale( int kicadDist )
{
2012-04-11 14:49:11 +00:00
// nanometers to um
return kicadDist / ( IU_PER_MM / 1000.0 );
}
// / Convert integer internal units to float um
static inline double IU2um( int kicadDist )
{
return kicadDist * (1000.0 / IU_PER_MM);
2008-01-24 21:47:54 +00:00
}
2008-01-24 21:47:54 +00:00
static inline double mapX( int x )
{
return scale( x );
2008-01-24 21:47:54 +00:00
}
2008-01-24 21:47:54 +00:00
static inline double mapY( int y )
{
return -scale( y ); // make y negative, since it is increasing going down.
2008-01-24 21:47:54 +00:00
}
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 nanometers (called Internal Units or IU)and we are exporting in units
* of mils, so we have to scale them.
2008-01-23 01:52:49 +00:00
*/
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;
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 for a DRAWSEGMENT with an end point or start point of aPoint, and
* if found, removes it from the TYPE_COLLECTOR and returns it, else returns NULL.
* @param aPoint The starting or ending point to search for.
* @param items The list to remove from.
* @param aLimit is the distance from \a aPoint that still constitutes a valid find.
* @return DRAWSEGMENT* - The first DRAWSEGMENT that has a start or end point matching
* aPoint, otherwise NULL if none.
2008-02-01 20:32:18 +00:00
*/
static DRAWSEGMENT* findPoint( const wxPoint& aPoint, ::PCB_TYPE_COLLECTOR* items, unsigned aLimit )
2008-02-01 20:32:18 +00:00
{
unsigned min_d = INT_MAX;
2013-05-02 14:12:55 +00:00
int ndx_min = 0;
// find the point closest to aPoint and perhaps exactly matching aPoint.
for( int i = 0; i < items->GetCount(); ++i )
2008-02-01 20:32:18 +00:00
{
DRAWSEGMENT* graphic = (DRAWSEGMENT*) (*items)[i];
unsigned d;
2008-02-07 20:23:58 +00:00
wxASSERT( graphic->Type() == PCB_LINE_T || graphic->Type() == PCB_MODULE_EDGE_T );
2011-12-14 04:29:25 +00:00
switch( graphic->GetShape() )
2008-02-01 20:32:18 +00:00
{
2011-12-14 04:29:25 +00:00
case S_ARC:
if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
{
items->Remove( i );
2011-12-14 04:29:25 +00:00
return graphic;
}
d = close_ness( aPoint, graphic->GetArcStart() );
if( d < min_d )
{
min_d = d;
ndx_min = i;
}
d = close_ness( aPoint, graphic->GetArcEnd() );
if( d < min_d )
{
min_d = d;
ndx_min = i;
}
2011-12-14 04:29:25 +00:00
break;
default:
if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
{
items->Remove( i );
2011-12-14 04:29:25 +00:00
return graphic;
}
d = close_ness( aPoint, graphic->GetStart() );
if( d < min_d )
{
min_d = d;
ndx_min = i;
}
d = close_ness( aPoint, graphic->GetEnd() );
if( d < min_d )
{
min_d = d;
ndx_min = i;
}
2008-02-01 20:32:18 +00:00
}
}
2008-02-07 20:23:58 +00:00
if( min_d <= aLimit )
{
DRAWSEGMENT* graphic = (DRAWSEGMENT*) (*items)[ndx_min];
items->Remove( ndx_min );
return graphic;
}
#if defined(DEBUG)
if( items->GetCount() )
2008-02-01 20:32:18 +00:00
{
printf( "Unable to find segment matching point (%.6g;%.6g) (seg count %d)\n",
IU2um( aPoint.x )/1000, IU2um( aPoint.y )/1000,
items->GetCount());
for( int i = 0; i< items->GetCount(); ++i )
{
DRAWSEGMENT* graphic = (DRAWSEGMENT*) (*items)[i];
if( graphic->GetShape() == S_ARC )
printf( "item %d, type=%s, start=%.6g;%.6g end=%.6g;%.6g\n",
i + 1,
TO_UTF8( BOARD_ITEM::ShowShape( graphic->GetShape() ) ),
IU2um( graphic->GetArcStart().x )/1000,
IU2um( graphic->GetArcStart().y )/1000,
IU2um( graphic->GetArcEnd().x )/1000,
IU2um( graphic->GetArcEnd().y )/1000 );
else
printf( "item %d, type=%s, start=%.6g;%.6g end=%.6g;%.6g\n",
i + 1,
TO_UTF8( BOARD_ITEM::ShowShape( graphic->GetShape() ) ),
IU2um( graphic->GetStart().x )/1000,
IU2um( graphic->GetStart().y )/1000,
IU2um( graphic->GetEnd().x )/1000,
IU2um( graphic->GetEnd().y )/1000 );
}
2008-02-01 20:32:18 +00:00
}
#endif
2008-02-07 20:23:58 +00:00
return NULL;
2008-01-22 20:48:02 +00:00
}
2008-02-09 08:34:45 +00:00
/**
* Function isRoundKeepout
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 isRoundKeepout( D_PAD* aPad )
2008-02-09 08:34:45 +00:00
{
if( aPad->GetShape()==PAD_SHAPE_CIRCLE )
{
2012-02-19 04:02:19 +00:00
if( aPad->GetDrillSize().x >= aPad->GetSize().x )
return true;
if( !( aPad->GetLayerSet() & LSET::AllCuMask() ).any() )
return true;
}
return false;
2008-02-09 08:34:45 +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 );
2008-01-25 22:03:36 +00:00
path->AppendPoint( aStart );
path->AppendPoint( aEnd );
path->SetLayerId( aLayerName.c_str() );
return path;
}
2008-02-14 01:07:52 +00:00
PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, D_PAD* aPad )
{
char name[256]; // padstack name builder
2008-02-14 01:07:52 +00:00
std::string uniqifier;
2008-01-29 16:45:14 +00:00
// caller must do these checks before calling here.
wxASSERT( !isRoundKeepout( aPad ) );
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[MAX_CU_LAYERS];
2008-02-07 20:23:58 +00:00
2008-11-10 20:54:26 +00:00
uniqifier = '[';
2008-02-07 20:23:58 +00:00
static const LSET all_cu = LSET::AllCuMask();
bool onAllCopperLayers = ( (aPad->GetLayerSet() & all_cu) == all_cu );
2008-02-07 20:23:58 +00:00
2008-11-10 20:54:26 +00:00
if( onAllCopperLayers )
uniqifier += 'A'; // A for all layers
2008-02-12 02:09:39 +00:00
2008-11-10 20:54:26 +00:00
const int copperCount = aBoard->GetCopperLayerCount();
for( int layer=0; layer<copperCount; ++layer )
2008-02-14 01:07:52 +00:00
{
LAYER_ID kilayer = pcbLayer2kicad[layer];
2008-11-10 20:54:26 +00:00
if( onAllCopperLayers || aPad->IsOnLayer( kilayer ) )
2008-02-14 01:07:52 +00:00
{
2008-11-10 20:54:26 +00:00
layerName[reportedLayers++] = layerIds[layer].c_str();
2008-11-10 20:54:26 +00:00
if( !onAllCopperLayers )
{
2008-11-10 20:54:26 +00:00
if( layer == 0 )
uniqifier += 'T';
else if( layer == copperCount - 1 )
2008-11-10 20:54:26 +00:00
uniqifier += 'B';
else
uniqifier += char('0' + layer); // layer index char
}
}
2008-01-29 16:45:14 +00:00
}
2008-02-07 20:23:58 +00:00
2008-11-10 20:54:26 +00:00
uniqifier += ']';
POINT dsnOffset;
2012-02-19 04:02:19 +00:00
if( aPad->GetOffset().x || aPad->GetOffset().y )
{
char offsetTxt[64];
2012-02-19 04:02:19 +00:00
wxPoint offset( aPad->GetOffset().x, aPad->GetOffset().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;
}
2012-02-19 04:02:19 +00:00
switch( aPad->GetShape() )
2008-02-05 02:13:16 +00:00
{
2008-02-14 01:07:52 +00:00
default:
case PAD_SHAPE_CIRCLE:
2008-02-14 01:07:52 +00:00
{
double diameter = scale( aPad->GetSize().x );
2008-02-01 01:09:39 +00:00
for( int ndx=0; ndx<reportedLayers; ++ndx )
2008-02-14 01:07:52 +00:00
{
SHAPE* shape = new SHAPE( padstack );
2008-02-14 01:07:52 +00:00
padstack->Append( shape );
2008-02-07 20:23:58 +00:00
CIRCLE* circle = new CIRCLE( shape );
2008-02-14 01:07:52 +00:00
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
snprintf( name, sizeof(name), "Round%sPad_%.6g_um",
uniqifier.c_str(), IU2um( aPad->GetSize().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
case PAD_SHAPE_RECT:
2008-02-14 01:07:52 +00:00
{
double dx = scale( aPad->GetSize().x ) / 2.0;
double dy = scale( aPad->GetSize().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;
for( int ndx=0; ndx<reportedLayers; ++ndx )
2008-02-14 01:07:52 +00:00
{
SHAPE* shape = new SHAPE( padstack );
2008-02-14 01:07:52 +00:00
padstack->Append( shape );
2008-02-07 20:23:58 +00:00
RECTANGLE* rect = new RECTANGLE( shape );
2008-02-14 01:07:52 +00:00
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_um",
uniqifier.c_str(),
IU2um( aPad->GetSize().x ),
IU2um( aPad->GetSize().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
case PAD_SHAPE_OVAL:
2008-02-14 01:07:52 +00:00
{
double dx = scale( aPad->GetSize().x ) / 2.0;
double dy = scale( aPad->GetSize().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;
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
snprintf( name, sizeof(name), "Oval%sPad_%.6gx%.6g_um",
uniqifier.c_str(),
IU2um( aPad->GetSize().x ),
IU2um( aPad->GetSize().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
case PAD_SHAPE_TRAPEZOID:
{
double dx = scale( aPad->GetSize().x ) / 2.0;
double dy = scale( aPad->GetSize().y ) / 2.0;
double ddx = scale( aPad->GetDelta().x ) / 2.0;
double ddy = scale( aPad->GetDelta().y ) / 2.0;
// see class_pad_draw_functions.cpp which draws the trapezoid pad
2010-09-07 17:04:39 +00:00
POINT lowerLeft( -dx - ddy, -dy - ddx );
POINT upperLeft( -dx + ddy, +dy + ddx );
POINT upperRight( +dx - ddy, +dy - ddx );
POINT lowerRight( +dx + ddy, -dy + ddx );
lowerLeft += dsnOffset;
upperLeft += dsnOffset;
upperRight += dsnOffset;
lowerRight += dsnOffset;
for( int ndx=0; ndx<reportedLayers; ++ndx )
{
SHAPE* shape = new SHAPE( padstack );
padstack->Append( shape );
// a T_polygon exists as a PATH
PATH* polygon = new PATH( shape, T_polygon );
shape->SetShape( polygon );
polygon->SetLayerId( layerName[ndx] );
polygon->AppendPoint( lowerLeft );
polygon->AppendPoint( upperLeft );
polygon->AppendPoint( upperRight );
polygon->AppendPoint( lowerRight );
}
// this string _must_ be unique for a given physical shape
snprintf( name, sizeof(name), "Trapz%sPad_%.6gx%.6g_%c%.6gx%c%.6g_um",
uniqifier.c_str(), IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ),
2012-02-19 04:02:19 +00:00
aPad->GetDelta().x < 0 ? 'n' : 'p',
2012-08-11 05:50:17 +00:00
std::abs( IU2um( aPad->GetDelta().x )),
2012-02-19 04:02:19 +00:00
aPad->GetDelta().y < 0 ? 'n' : 'p',
2012-08-11 05:50:17 +00:00
std::abs( IU2um( aPad->GetDelta().y ) )
);
name[ sizeof(name)-1 ] = 0;
padstack->SetPadstackId( name );
}
2008-02-14 01:07:52 +00:00
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)
2009-09-10 15:22:26 +00:00
typedef std::map<wxString, int> 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
{
PINMAP pinmap;
wxString padName;
PCB_TYPE_COLLECTOR moduleItems;
2008-01-24 21:47:54 +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
image->image_id = aModule->GetFPID().Format().c_str();
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( isRoundKeepout( pad ) )
2008-01-24 21:47:54 +00:00
{
2012-02-19 04:02:19 +00:00
double diameter = scale( pad->GetDrillSize().x );
POINT vertex = mapPt( pad->GetPos0() );
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 )
2008-02-14 01:07:52 +00:00
{
KEEPOUT* keepout = new KEEPOUT( image, T_keepout );
2008-02-14 01:07:52 +00:00
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 if() could there be a square keepout here?
2008-02-14 01:07:52 +00:00
else
2008-02-08 15:00:50 +00:00
{
PADSTACK* padstack = makePADSTACK( aBoard, pad );
PADSTACKSET::iterator iter = padstackset.find( *padstack );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
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
PIN* pin = new PIN( image );
2008-02-07 20:23:58 +00:00
padName = pad->GetPadName();
pin->pin_id = TO_UTF8( padName );
2008-02-14 01:07:52 +00:00
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
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
pin->kiNetCode = pad->GetNetCode();
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
double angle = pad->GetOrientationDegrees() - aModule->GetOrientationDegrees();
NORMALIZE_ANGLE_DEGREES_POS( angle );
pin->SetRotation( angle );
2008-02-07 20:23:58 +00:00
2012-02-19 04:02:19 +00:00
wxPoint pos( pad->GetPos0() );
2008-01-24 21:47:54 +00:00
pin->SetVertex( mapPt( pos ) );
2008-02-14 01:07:52 +00:00
}
}
2008-02-07 20:23:58 +00:00
#if 1 // enable image (outline) scopes.
static const KICAD_T scanEDGEs[] = { PCB_MODULE_EDGE_T, 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;
2011-12-14 04:29:25 +00:00
switch( graphic->GetShape() )
{
case S_SEGMENT:
outline = new SHAPE( image, T_outline );
image->Append( outline );
path = new PATH( outline );
outline->SetShape( path );
2011-12-14 04:29:25 +00:00
path->SetAperture( scale( graphic->GetWidth() ) );
path->SetLayerId( "signal" );
2011-12-14 04:29:25 +00:00
path->AppendPoint( mapPt( graphic->GetStart0() ) );
path->AppendPoint( mapPt( graphic->GetEnd0() ) );
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 );
2011-12-14 04:29:25 +00:00
path->SetAperture( scale( graphic->GetWidth() ) );
path->SetLayerId( "signal" );
// Do the math using KiCad units, that way we stay out of the
2008-02-29 06:49:34 +00:00
// 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.
double radius = GetLineLength( graphic->GetStart(), graphic->GetEnd() );
// 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 )
{
wxPoint point( KiROUND( radius * cos( radians ) ),
KiROUND( radius * sin( radians ) ) );
2008-02-29 06:49:34 +00:00
point += graphic->m_Start0; // an offset
path->AppendPoint( mapPt( point ) );
}
}
break;
case S_RECT:
case S_ARC:
default:
DBG( printf( "makeIMAGE(): unsupported shape %s\n",
TO_UTF8( BOARD_ITEM::ShowShape( graphic->GetShape() ) ) ); )
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 )
2008-02-14 01:07:52 +00:00
{
char name[48];
PADSTACK* padstack = new PADSTACK();
double dsnDiameter = scale( aCopperDiameter );
2008-02-07 20:23:58 +00:00
for( int layer=aTopLayer; layer<=aBotLayer; ++layer )
2008-02-14 01:07:52 +00:00
{
SHAPE* shape = new SHAPE( padstack );
2008-02-14 01:07:52 +00:00
padstack->Append( shape );
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
CIRCLE* circle = new CIRCLE( shape );
2008-02-14 01:07:52 +00:00
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
snprintf( name, sizeof(name), "Via[%d-%d]_%.6g:%.6g_um",
aTopLayer, aBotLayer, dsnDiameter,
// encode the drill value into the name for later import
IU2um( aDrillDiameter )
);
name[ sizeof(name) - 1 ] = 0;
2008-02-14 01:07:52 +00:00
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
PADSTACK* SPECCTRA_DB::makeVia( const ::VIA* aVia )
2008-02-14 01:07:52 +00:00
{
LAYER_ID topLayerNum;
LAYER_ID botLayerNum;
2008-01-26 02:02:27 +00:00
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
aVia->LayerPair( &topLayerNum, &botLayerNum );
2008-02-07 20:23:58 +00:00
int topLayer = kicadLayer2pcb[topLayerNum];
int botLayer = kicadLayer2pcb[botLayerNum];
2008-02-07 20:23:58 +00:00
2008-02-14 01:07:52 +00:00
if( topLayer > botLayer )
std::swap( topLayer, botLayer );
2008-02-14 01:07:52 +00:00
return makeVia( aVia->GetWidth(), aVia->GetDrillValue(), topLayer, botLayer );
2008-01-21 22:16:45 +00:00
}
2008-01-22 20:48:02 +00:00
/**
* Function makeCircle
* does a line segmented circle into aPath.
*/
static void makeCircle( PATH* aPath, DRAWSEGMENT* aGraphic )
{
// do a circle segmentation
const int STEPS = 2 * 36;
int radius = aGraphic->GetRadius();
if( radius <= 0 ) // Should not occur, but ...
return;
wxPoint center = aGraphic->GetCenter();
double angle = 3600.0;
wxPoint start = center;
start.x += radius;
wxPoint nextPt;
for( int step = 0; step<STEPS; ++step )
{
double rotation = ( angle * step ) / STEPS;
nextPt = start;
RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
aPath->AppendPoint( mapPt( nextPt ) );
}
}
void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
throw( IO_ERROR, boost::bad_pointer )
{
PCB_TYPE_COLLECTOR items;
unsigned prox; // a proximity BIU metric, not an accurate distance
const int STEPS = 36; // for a segmentation of an arc of 360 degrees
// Get all the DRAWSEGMENTS and module graphics into 'items',
// then keep only those on layer == Edge_Cuts.
static const KICAD_T scan_graphics[] = { PCB_LINE_T, PCB_MODULE_EDGE_T, EOT };
items.Collect( aBoard, scan_graphics );
for( int i = 0; i<items.GetCount(); )
{
if( items[i]->GetLayer() != Edge_Cuts )
items.Remove( i );
else // remove graphics not on Edge_Cuts layer
++i;
}
if( items.GetCount() )
{
PATH* path = new PATH( boundary );
boundary->paths.push_back( path );
path->layer_id = "pcb";
wxPoint prevPt;
DRAWSEGMENT* graphic;
// Find edge point with minimum x, this should be in the outer polygon
// which will define the perimeter Edge.Cuts polygon.
wxPoint xmin = wxPoint( INT_MAX, 0 );
int xmini = 0;
for( int i = 0; i < items.GetCount(); i++ )
{
graphic = (DRAWSEGMENT*) items[i];
switch( graphic->GetShape() )
{
case S_SEGMENT:
{
if( graphic->GetStart().x < xmin.x )
{
xmin = graphic->GetStart();
xmini = i;
}
if( graphic->GetEnd().x < xmin.x )
{
xmin = graphic->GetEnd();
xmini = i;
}
}
break;
case S_ARC:
// Freerouter does not yet understand arcs, so approximate
// an arc with a series of short lines and put those
// line segments into the !same! PATH.
{
wxPoint start = graphic->GetArcStart();
wxPoint center = graphic->GetCenter();
double angle = -graphic->GetAngle();
int steps = STEPS * fabs(angle) /3600.0;
if( steps == 0 )
steps = 1;
wxPoint pt;
for( int step = 1; step<=steps; ++step )
{
double rotation = ( angle * step ) / steps;
pt = start;
RotatePoint( &pt.x, &pt.y, center.x, center.y, rotation );
if( pt.x < xmin.x )
{
xmin = pt;
xmini = i;
}
}
}
break;
case S_CIRCLE:
{
wxPoint pt = graphic->GetCenter();
// pt has minimum x point
pt.x -= graphic->GetRadius();
// when the radius <= 0, this is a mal-formed circle. Skip it
if( graphic->GetRadius() > 0 && pt.x < xmin.x )
{
xmin = pt;
xmini = i;
}
}
break;
default:
{
wxString error = wxString::Format( _( "Unsupported DRAWSEGMENT type %s" ),
GetChars( BOARD_ITEM::ShowShape( graphic->GetShape() ) ) );
THROW_IO_ERROR( error );
}
break;
}
}
// Grab the left most point, assume its on the board's perimeter, and see if we
// can put enough graphics together by matching endpoints to formulate a cohesive
// polygon.
graphic = (DRAWSEGMENT*) items[xmini];
// The first DRAWSEGMENT is in 'graphic', ok to remove it from 'items'
items.Remove( xmini );
// Set maximum proximity threshold for point to point nearness metric for
// board perimeter only, not interior keepouts yet.
prox = Millimeter2iu( 0.01 ); // should be enough to fix rounding issues
// is arc start and end point calculations
// Output the Edge.Cuts perimeter as circle or polygon.
if( graphic->GetShape() == S_CIRCLE )
{
makeCircle( path, graphic );
}
else
{
// Polygon start point. Arbitrarily chosen end of the
// segment and build the poly from here.
wxPoint startPt = wxPoint( graphic->GetEnd() );
prevPt = graphic->GetEnd();
path->AppendPoint( mapPt( prevPt ) );
// Do not append the other end point yet of this 'graphic', this first
// 'graphic' might be an arc.
for(;;)
{
switch( graphic->GetShape() )
{
case S_SEGMENT:
{
wxPoint nextPt;
// Use the line segment end point furthest away from
// prevPt as we assume the other end to be ON prevPt or
// very close to it.
if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
{
nextPt = graphic->GetEnd();
}
else
{
nextPt = graphic->GetStart();
}
path->AppendPoint( mapPt( nextPt ) );
prevPt = nextPt;
}
break;
case S_ARC:
// Freerouter does not yet understand arcs, so approximate
// an arc with a series of short lines and put those
// line segments into the !same! PATH.
{
wxPoint start = graphic->GetArcStart();
wxPoint end = graphic->GetArcEnd();
wxPoint center = graphic->GetCenter();
double angle = -graphic->GetAngle();
int steps = STEPS * fabs(angle) /3600.0;
if( steps == 0 )
steps = 1;
if( !close_enough( prevPt, start, prox ) )
{
wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), prox ) );
angle = -angle;
std::swap( start, end );
}
wxPoint nextPt;
for( int step = 1; step<=steps; ++step )
{
double rotation = ( angle * step ) / steps;
nextPt = start;
RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
path->AppendPoint( mapPt( nextPt ) );
}
prevPt = nextPt;
}
break;
default:
{
wxString error = wxString::Format( _( "Unsupported DRAWSEGMENT type %s" ),
GetChars( BOARD_ITEM::ShowShape( graphic->GetShape() ) ) );
THROW_IO_ERROR( error );
}
break;
}
// Get next closest segment.
graphic = findPoint( prevPt, &items, prox );
// If there are no more close segments, check if the board
// outline polygon can be closed.
if( !graphic )
{
if( close_enough( startPt, prevPt, prox ) )
{
// Close the polygon back to start point
path->AppendPoint( mapPt( startPt ) );
}
else
{
wxString error = wxString::Format(
_( "Unable to find the next boundary segment with an endpoint of (%s mm, %s mm).\n"
"Edit Edge.Cuts perimeter graphics, making them contiguous polygons each." ),
GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.x ).c_str() ) ),
GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.y ).c_str() ) )
);
THROW_IO_ERROR( error );
}
break;
}
}
}
// Output the interior Edge.Cuts graphics as keepouts, using same nearness
// metric as the board edge as otherwise we have trouble completing complex
// polygons.
prox = Millimeter2iu( 0.05 );
while( items.GetCount() )
{
// emit a signal layers keepout for every interior polygon left...
KEEPOUT* keepout = new KEEPOUT( NULL, T_keepout );
PATH* poly_ko = new PATH( NULL, T_polygon );
keepout->SetShape( poly_ko );
poly_ko->SetLayerId( "signal" );
pcb->structure->keepouts.push_back( keepout );
graphic = (DRAWSEGMENT*) items[0];
items.Remove( 0 );
if( graphic->GetShape() == S_CIRCLE )
{
makeCircle( poly_ko, graphic );
}
else
{
// Polygon start point. Arbitrarily chosen end of the
// segment and build the poly from here.
wxPoint startPt( graphic->GetEnd() );
prevPt = graphic->GetEnd();
poly_ko->AppendPoint( mapPt( prevPt ) );
// do not append the other end point yet, this first 'graphic' might be an arc
for(;;)
{
switch( graphic->GetShape() )
{
case S_SEGMENT:
{
wxPoint nextPt;
// Use the line segment end point furthest away from
// prevPt as we assume the other end to be ON prevPt or
// very close to it.
if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
{
nextPt = graphic->GetEnd();
}
else
{
nextPt = graphic->GetStart();
}
prevPt = nextPt;
poly_ko->AppendPoint( mapPt( prevPt ) );
}
break;
case S_ARC:
// Freerouter does not yet understand arcs, so approximate
// an arc with a series of short lines and put those
// line segments into the !same! PATH.
{
wxPoint start = graphic->GetArcStart();
wxPoint end = graphic->GetArcEnd();
wxPoint center = graphic->GetCenter();
double angle = -graphic->GetAngle();
int steps = STEPS * fabs(angle) /3600.0;
if( steps == 0 )
steps = 1;
if( !close_enough( prevPt, start, prox ) )
{
wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), prox ) );
angle = -angle;
std::swap( start, end );
}
wxPoint nextPt;
for( int step = 1; step<=steps; ++step )
{
double rotation = ( angle * step ) / steps;
nextPt = start;
RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
poly_ko->AppendPoint( mapPt( nextPt ) );
}
prevPt = nextPt;
}
break;
default:
{
wxString error = wxString::Format(
_( "Unsupported DRAWSEGMENT type %s" ),
GetChars( BOARD_ITEM::ShowShape( graphic->GetShape() ) ) );
THROW_IO_ERROR( error );
}
break;
}
// Get next closest segment.
graphic = findPoint( prevPt, &items, prox );
// If there are no more close segments, check if polygon
// can be closed.
if( !graphic )
{
if( close_enough( startPt, prevPt, prox ) )
{
// Close the polygon back to start point
poly_ko->AppendPoint( mapPt( startPt ) );
}
else
{
wxString error = wxString::Format(
_( "Unable to find the next keepout segment with an endpoint of (%s mm, %s mm).\n"
"Edit Edge.Cuts interior graphics, making them contiguous polygons each." ),
GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.x ).c_str() ) ),
GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.y ).c_str() ) )
);
THROW_IO_ERROR( error );
}
break;
}
}
}
}
}
else
{
// User has not defined a board perimeter yet...
EDA_RECT bbbox = aBoard->ComputeBoundingBox();
RECTANGLE* rect = new RECTANGLE( boundary );
2008-05-05 20:13:54 +00:00
boundary->rectangle = rect;
rect->layer_id = "pcb";
// opposite corners
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
2011-12-05 06:15:33 +00:00
wxPoint bottomRight( bbbox.GetRight(), bbbox.GetBottom() );
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
2011-12-05 06:15:33 +00:00
rect->SetCorners( mapPt( bbbox.GetOrigin() ),
mapPt( bottomRight ) );
}
}
/* This function is not used in SPECCTRA export,
* but uses a lot of functions from it
* and is used to extract a board outlines (3D view, automatic zones build ...)
* makes the board perimeter for the DSN file by filling the BOUNDARY element.
* Any closed outline inside the main outline is a hole
* All contours should be closed, i.e. valid closed polygon vertices
*/
bool SPECCTRA_DB::GetBoardPolygonOutlines( BOARD* aBoard,
SHAPE_POLY_SET& aOutlines,
SHAPE_POLY_SET& aHoles,
wxString* aErrorText )
{
bool success = true;
double specctra2UIfactor = IU_PER_MM / 1000.0; // Specctra unite = micron
if( ! pcb )
{
pcb = new PCB();
pcb->structure = new STRUCTURE( pcb );
}
CPolyPt corner;
BOUNDARY* boundary = new BOUNDARY( 0 );
pcb->structure->SetBOUNDARY( boundary );
aOutlines.NewOutline();
try
{
fillBOUNDARY( aBoard, boundary );
std::vector<double> buffer;
boundary->GetCorners( buffer );
for( unsigned ii = 0; ii < buffer.size(); ii+=2 )
{
corner.x = buffer[ii] * specctra2UIfactor;
corner.y = - buffer[ii+1] * specctra2UIfactor;
aOutlines.Append( corner.x, corner.y );
}
// Export holes, stored as keepouts polygonal shapes.
// by fillBOUNDARY()
KEEPOUTS& holes = pcb->structure->keepouts;
for( KEEPOUTS::iterator i=holes.begin(); i!=holes.end(); ++i )
{
KEEPOUT& keepout = *i;
PATH* poly_hole = (PATH*)keepout.shape;
POINTS& plist = poly_hole->GetPoints();
aHoles.NewOutline();
for( unsigned ii = 0; ii < plist.size(); ii++ )
{
corner.x = plist[ii].x * specctra2UIfactor;
corner.y = - plist[ii].y * specctra2UIfactor;
aHoles.Append( corner.x, corner.y );
}
}
}
catch( const IO_ERROR& ioe )
{
// Creates a valid polygon outline is not possible.
// So uses the board edge cuts bounding box to create a
// rectangular outline
// (when no edge cuts items, fillBOUNDARY build a contour
// from global bounding box
success = false;
if( aErrorText )
*aErrorText = ioe.errorText;
EDA_RECT bbbox = aBoard->ComputeBoundingBox( true );
// Ensure non null area. If happen, gives a minimal size.
if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
bbbox.Inflate( Millimeter2iu( 1.0 ) );
aOutlines.RemoveAllContours();
aOutlines.NewOutline();
corner.x = bbbox.GetOrigin().x;
corner.y = bbbox.GetOrigin().y;
aOutlines.Append( corner.x, corner.y );
corner.x = bbbox.GetOrigin().x;
corner.y = bbbox.GetEnd().y;
aOutlines.Append( corner.x, corner.y );
corner.x = bbbox.GetEnd().x;
corner.y = bbbox.GetEnd().y;
aOutlines.Append( corner.x, corner.y );
corner.x = bbbox.GetEnd().x;
corner.y = bbbox.GetOrigin().y;
aOutlines.Append( corner.x, corner.y );
}
return success;
}
typedef std::set<std::string> STRINGSET;
typedef std::pair<STRINGSET::iterator, bool> STRINGSET_PAIR;
2008-02-14 01:07:52 +00:00
void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
throw( IO_ERROR, boost::bad_ptr_container_operation )
2008-01-22 20:48:02 +00:00
{
PCB_TYPE_COLLECTOR items;
2008-01-22 20:48:02 +00:00
static const KICAD_T scanMODULEs[] = { PCB_MODULE_T, 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-05-06 12:32:41 +00:00
// back to us later from the router.
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 )
{
THROW_IO_ERROR( wxString::Format( _( "Component with value of '%s' has empty reference id." ),
GetChars( module->GetValue() ) ) );
2008-02-09 08:34:45 +00:00
}
// if we cannot insert OK, that means the reference has been seen before.
STRINGSET_PAIR refpair = refs.insert( 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
{
THROW_IO_ERROR( wxString::Format( _( "Multiple components have identical reference IDs of '%s'." ),
GetChars( module->GetReference() ) ) );
2008-02-09 08:34:45 +00:00
}
}
}
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 )
{
LAYER* layer = new LAYER( pcb->structure );
2008-01-31 01:30:52 +00:00
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;
2008-03-04 04:22:27 +00:00
switch( aBoard->GetLayerType( pcbLayer2kicad[pcbNdx] ) )
{
default:
case LT_SIGNAL: layerType = T_signal; break;
case LT_POWER: layerType = T_power; break;
#if 1 // Freerouter does not support type "mixed", only signal and power.
// Remap "mixed" to "signal".
case LT_MIXED: layerType = T_signal; break;
#else
case LT_MIXED: layerType = T_mixed; break;
#endif
case LT_JUMPER: layerType = T_jumper; break;
2008-03-04 04:22:27 +00:00
}
layer->layer_type = layerType;
2008-02-05 02:13:16 +00:00
layer->properties.push_back( PROPERTY() );
PROPERTY* property = &layer->properties.back();
2008-02-05 02:13:16 +00:00
property->name = "index";
char temp[32];
2008-02-05 02:13:16 +00:00
sprintf( temp, "%d", pcbNdx );
2008-02-07 20:23:58 +00:00
property->value = temp;
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-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
{
2012-04-11 14:49:11 +00:00
// tell freerouter to use "tenths of micrometers",
// which is 100 nm resolution. Possibly more resolution is possible
// in freerouter, but it would need testing.
pcb->unit->units = T_um;
pcb->resolution->units = T_um;
pcb->resolution->value = 10; // tenths of a um
2012-04-11 14:49:11 +00:00
// pcb->resolution->value = 1000; // "thousandths of a um" (i.e. "nm")
2008-01-23 01:52:49 +00:00
}
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
{
// Because fillBOUNDARY() can throw an exception, we link in an
// empty boundary so the BOUNDARY does not get lost in the event of
// of an exception.
BOUNDARY* boundary = new BOUNDARY( 0 );
pcb->structure->SetBOUNDARY( boundary );
fillBOUNDARY( aBoard, 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>--------------------------------------------------------
{
char rule[80];
NETCLASSPTR defaultClass = aBoard->GetDesignSettings().GetDefault();
2008-02-07 20:23:58 +00:00
int defaultTrackWidth = defaultClass->GetTrackWidth();
int defaultClearance = defaultClass->GetClearance();
2008-10-23 21:06:43 +00:00
double clearance = scale( defaultClearance );
2008-02-09 16:33:03 +00:00
STRINGS& rules = pcb->structure->rules->rules;
2008-02-07 20:23:58 +00:00
2009-09-10 15:22:26 +00:00
sprintf( rule, "(width %.6g)", scale( defaultTrackWidth ) );
2008-01-31 06:46:31 +00:00
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
sprintf( rule, "(clearance %.6g)", clearance + safetyMargin );
2008-10-23 21:06:43 +00:00
rules.push_back( rule );
// On a high density board (a board with 4 mil tracks, 4 mil spacing)
// a typical solder mask clearance will be 2-3 mils.
// This exposes 2 to 3 mils of bare board around each pad, and would
// leave only 1 to 2 mils of solder mask between the solder mask's boundary
// to the edge of any trace within "clearance" of the pad. So we need at least
// 2 mils *extra* clearance for traces which would come near a pad on
2008-10-23 21:06:43 +00:00
// a different net. So if the baseline trace to trace clearance was say 4 mils, then
// the SMD to trace clearance should be at least 6 mils.
double default_smd = clearance + safetyMargin;
if( default_smd <= 6.0 )
default_smd = 6.0;
sprintf( rule, "(clearance %.6g (type default_smd))", default_smd );
2008-01-31 06:46:31 +00:00
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-03-31 13:45:36 +00:00
/* see: http://www.freerouting.net/usren/viewtopic.php?f=5&t=339#p474
2008-10-23 21:06:43 +00:00
sprintf( rule, "(clearance %.6g (type pad_to_turn_gap))", clearance + safetyMargin );
2008-01-31 06:46:31 +00:00
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-10-23 21:06:43 +00:00
sprintf( rule, "(clearance %.6g (type smd_to_turn_gap))", clearance + safetyMargin );
2008-01-31 06:46:31 +00:00
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-10-23 21:06:43 +00:00
sprintf( rule, "(clearance %.6g (type via_via))", clearance + safetyMargin );
2008-01-31 06:46:31 +00:00
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-10-23 21:06:43 +00:00
sprintf( rule, "(clearance %.6g (type via_smd))", clearance + safetyMargin );
2008-01-31 06:46:31 +00:00
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-10-23 21:06:43 +00:00
sprintf( rule, "(clearance %.6g (type via_pin))", clearance + safetyMargin );
2008-01-31 06:46:31 +00:00
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-10-23 21:06:43 +00:00
sprintf( rule, "(clearance %.6g (type pin_pin))", clearance + safetyMargin );
2008-01-31 06:46:31 +00:00
rules.push_back( rule );
2008-02-07 20:23:58 +00:00
2008-10-23 21:06:43 +00:00
sprintf( rule, "(clearance %.6g (type smd_pin))", clearance + safetyMargin );
2008-01-31 06:46:31 +00:00
rules.push_back( rule );
2008-03-31 13:45:36 +00:00
*/
2008-02-07 20:23:58 +00:00
2008-10-23 21:06:43 +00:00
// Pad to pad spacing on a single SMT part can be closer than our
// clearance, we don't want freerouter complaining about that, so
// output a significantly smaller pad to pad clearance to freerouter.
clearance = scale( defaultClearance ) / 4;
2008-02-09 16:33:03 +00:00
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
//-----<zone containers (not keepout areas) become planes>--------------------------------
// Note: only zones are output here, keepout areas be be created later
2008-01-23 22:36:37 +00:00
{
int netlessZones = 0;
static const KICAD_T scanZONEs[] = { PCB_ZONE_AREA_T, EOT };
2008-01-23 22:36:37 +00:00
items.Collect( aBoard, scanZONEs );
for( int i = 0; i<items.GetCount(); ++i )
2008-01-23 22:36:37 +00:00
{
ZONE_CONTAINER* item = (ZONE_CONTAINER*) items[i];
if( item->GetIsKeepout() )
continue;
// Currently, we export only copper layers
if( ! IsCopperLayer( item->GetLayer() ) )
continue;
2008-01-23 22:36:37 +00:00
COPPER_PLANE* plane = new COPPER_PLANE( pcb->structure );
2008-05-22 15:55:24 +00:00
pcb->structure->planes.push_back( plane );
PATH* mainPolygon = new PATH( plane, T_polygon );
2009-02-06 14:23:56 +00:00
plane->SetShape( mainPolygon );
2008-02-07 20:23:58 +00:00
plane->name = TO_UTF8( item->GetNetname() );
2008-01-31 01:30:52 +00:00
if( plane->name.size() == 0 )
{
char name[32];
// This is one of those no connection zones, netcode=0, and it has no name.
// Create a unique, bogus netname.
NET* no_net = new NET( pcb->network );
sprintf( name, "@:no_net_%d", netlessZones++ );
no_net->net_id = name;
// add the bogus net name to network->nets.
pcb->network->nets.push_back( no_net );
// use the bogus net name in the netless zone.
plane->name = no_net->net_id;
}
2009-02-06 14:23:56 +00:00
mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
2008-02-07 20:23:58 +00:00
int count = item->Outline()->m_CornersList.GetCornersCount();
2009-02-06 14:23:56 +00:00
int ndx = 0; // used in 2 for() loops below
for( ; ndx<count; ++ndx )
2008-01-23 22:36:37 +00:00
{
wxPoint point( item->Outline()->m_CornersList[ndx].x,
item->Outline()->m_CornersList[ndx].y );
2009-02-06 14:23:56 +00:00
mainPolygon->AppendPoint( mapPt(point) );
// this was the end of the main polygon
if( item->Outline()->m_CornersList[ndx].end_contour )
2009-02-06 14:23:56 +00:00
break;
}
WINDOW* window = 0;
PATH* cutout = 0;
2009-02-06 14:23:56 +00:00
// handle the cutouts
for( ++ndx; ndx<count; ++ndx )
{
if( item->Outline()->m_CornersList[ndx-1].end_contour )
2009-02-06 14:23:56 +00:00
{
window = new WINDOW( plane );
2009-02-06 14:23:56 +00:00
plane->AddWindow( window );
cutout = new PATH( window, T_polygon );
2009-02-06 14:23:56 +00:00
window->SetShape( cutout );
cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
}
wxASSERT( window );
wxASSERT( cutout );
wxPoint point(item->Outline()->m_CornersList[ndx].x,
item->Outline()->m_CornersList[ndx].y );
2009-02-06 14:23:56 +00:00
cutout->AppendPoint( mapPt(point) );
2008-01-23 22:36:37 +00:00
}
}
}
2008-01-24 21:47:54 +00:00
//-----<zone containers flagged keepout areas become keepout>--------------------------------
{
static const KICAD_T scanZONEs[] = { PCB_ZONE_AREA_T, EOT };
items.Collect( aBoard, scanZONEs );
for( int i=0; i<items.GetCount(); ++i )
{
ZONE_CONTAINER* item = (ZONE_CONTAINER*) items[i];
if( ! item->GetIsKeepout() )
continue;
// keepout areas have a type. types are
// T_place_keepout, T_via_keepout, T_wire_keepout,
// T_bend_keepout, T_elongate_keepout, T_keepout.
// Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
DSN_T keepout_type;
if( item->GetDoNotAllowVias() && item->GetDoNotAllowTracks() )
keepout_type = T_keepout;
else if( item->GetDoNotAllowVias() )
keepout_type = T_via_keepout;
else if( item->GetDoNotAllowTracks() )
keepout_type = T_wire_keepout;
else
keepout_type = T_keepout;
KEEPOUT* keepout = new KEEPOUT( pcb->structure, keepout_type );
pcb->structure->keepouts.push_back( keepout );
PATH* mainPolygon = new PATH( keepout, T_polygon );
keepout->SetShape( mainPolygon );
mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
int count = item->Outline()->m_CornersList.GetCornersCount();
int ndx = 0; // used in 2 for() loops below
for( ; ndx<count; ++ndx )
{
wxPoint point( item->Outline()->m_CornersList[ndx].x,
item->Outline()->m_CornersList[ndx].y );
mainPolygon->AppendPoint( mapPt(point) );
// this was the end of the main polygon
if( item->Outline()->m_CornersList[ndx].end_contour )
break;
}
WINDOW* window = 0;
PATH* cutout = 0;
// handle the cutouts
for( ++ndx; ndx<count; ++ndx )
{
if( item->Outline()->m_CornersList[ndx-1].end_contour )
{
window = new WINDOW( keepout );
keepout->AddWindow( window );
cutout = new PATH( window, T_polygon );
window->SetShape( cutout );
cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
}
wxASSERT( window );
wxASSERT( cutout );
wxPoint point(item->Outline()->m_CornersList[ndx].x,
item->Outline()->m_CornersList[ndx].y );
cutout->AppendPoint( mapPt(point) );
}
}
}
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
{
PIN_REF empty( pcb->network );
2008-02-29 06:49:34 +00:00
std::string componentId;
2008-02-14 01:07:52 +00:00
// find the highest numbered netCode within the board.
int highestNetCode = aBoard->GetNetCount() - 1;
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 )
2008-02-14 01:07:52 +00:00
nets[i] = new NET( pcb->network );
2008-01-29 16:45:14 +00:00
for( unsigned ii = 0; ii < aBoard->GetNetCount(); ii++ )
2008-02-14 01:07:52 +00:00
{
NETINFO_ITEM* net = aBoard->FindNet( ii );
int netcode = net->GetNet();
2008-02-14 01:07:52 +00:00
if( netcode > 0 )
nets[ netcode ]->net_id = TO_UTF8( net->GetNetname() );
2008-02-14 01:07:52 +00:00
}
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();
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
IMAGE* image = makeIMAGE( aBoard, module );
2008-02-14 01:07:52 +00:00
componentId = TO_UTF8( module->GetReference() );
2008-02-29 06:49:34 +00:00
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 )
2008-02-14 01:07:52 +00:00
{
PIN* pin = &image->pins[p];
int netcode = pin->kiNetCode;
2008-02-14 01:07:52 +00:00
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
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
}
COMPONENT* comp = pcb->placement->LookupCOMPONENT( image->GetImageId() );
PLACE* place = new PLACE( comp );
2008-02-07 20:23:58 +00:00
comp->places.push_back( place );
2008-02-07 20:23:58 +00:00
place->SetRotation( module->GetOrientationDegrees() );
place->SetVertex( mapPt( module->GetPosition() ) );
2008-02-29 06:49:34 +00:00
place->component_id = componentId;
place->part_number = TO_UTF8( module->GetValue() );
2008-02-07 20:23:58 +00:00
// module is flipped from bottom side, set side to T_back
if( module->GetFlag() )
2008-02-05 02:13:16 +00:00
{
double angle = 180.0 - module->GetOrientationDegrees();
NORMALIZE_ANGLE_DEGREES_POS( angle );
place->SetRotation( angle );
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() )
2008-02-14 01:07:52 +00:00
{
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 )
2008-02-14 01:07:52 +00:00
{
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
//-----< output vias used in netclasses >-----------------------------------
2008-02-14 01:07:52 +00:00
{
NETCLASSES& nclasses = aBoard->GetDesignSettings().m_NetClasses;
2008-02-07 20:23:58 +00:00
// Assume the netclass vias are all the same kind of thru, blind, or buried vias.
// This is in lieu of either having each netclass via have its own layer pair in
// the netclass dialog, or such control in the specctra export dialog.
// if( aBoard->GetDesignSettings().m_CurrentViaType == VIA_THROUGH )
{
m_top_via_layer = 0; // first specctra cu layer is number zero.
m_bot_via_layer = aBoard->GetCopperLayerCount()-1;
}
/*
else
{
// again, should be in the BOARD:
topLayer = kicadLayer2pcb[ GetScreen()->m_Route_Layer_TOP ];
botLayer = kicadLayer2pcb[ GetScreen()->m_Route_Layer_BOTTOM ];
}
*/
// Add the via from the Default netclass first. The via container
// in pcb->library preserves the sequence of addition.
2008-02-14 01:07:52 +00:00
NETCLASSPTR netclass = nclasses.GetDefault();
PADSTACK* via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
m_top_via_layer, m_bot_via_layer );
2008-02-07 20:23:58 +00:00
// we AppendVia() this first one, there is no way it can be a duplicate,
// the pcb->library via container is empty at this point. After this,
// we'll have to use LookupVia().
wxASSERT( pcb->library->vias.size() == 0 );
pcb->library->AppendVia( via );
#if 0
// I've seen no way to make stock vias useable by freerouter. Also the
// zero based diameter was leading to duplicates in the LookupVia() function.
// User should use netclass based vias when going to freerouter.
// Output the stock vias, but preserve uniqueness in the via container by
// using LookupVia().
for( unsigned i = 0; i < aBoard->m_ViasDimensionsList.size(); ++i )
{
int viaSize = aBoard->m_ViasDimensionsList[i].m_Diameter;
int viaDrill = aBoard->m_ViasDimensionsList[i].m_Drill;
2008-02-07 20:23:58 +00:00
via = makeVia( viaSize, viaDrill,
m_top_via_layer, m_bot_via_layer );
// maybe add 'via' to the library, but only if unique.
PADSTACK* registered = pcb->library->LookupVia( via );
if( registered != via )
delete via;
}
#endif
// set the "spare via" index at the start of the
// pcb->library->spareViaIndex = pcb->library->vias.size();
// output the non-Default netclass vias
for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
{
netclass = nc->second;
via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
m_top_via_layer, m_bot_via_layer );
// maybe add 'via' to the library, but only if unique.
PADSTACK* registered = pcb->library->LookupVia( via );
if( registered != via )
delete via;
}
}
2008-01-30 19:16:46 +00:00
2008-02-14 01:07:52 +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.
static const KICAD_T scanTRACKs[] = { PCB_TRACE_T, 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;
LAYER_NUM old_layer = UNDEFINED_LAYER;
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
int netcode = track->GetNetCode();
2008-02-29 06:49:34 +00:00
if( netcode == 0 )
2008-01-31 01:30:52 +00:00
continue;
2015-11-11 18:47:51 +00:00
if( old_netcode != netcode ||
old_width != track->GetWidth() ||
old_layer != track->GetLayer() ||
(path && path->points.back() != mapPt(track->GetStart()) )
2008-02-07 20:23:58 +00:00
)
2008-01-31 01:30:52 +00:00
{
old_width = track->GetWidth();
2008-01-31 06:46:31 +00:00
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;
NETINFO_ITEM* net = aBoard->FindNet( netcode );
wxASSERT( net );
netname = TO_UTF8( net->GetNetname() );
2008-01-31 01:30:52 +00:00
}
2008-01-31 06:46:31 +00:00
WIRE* wire = new WIRE( wiring );
2008-01-31 06:46:31 +00:00
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
wire->wire_type = T_protect; // @todo, this should be configurable
2008-02-07 20:23:58 +00:00
LAYER_NUM 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 );
2008-01-31 06:46:31 +00:00
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->GetStart() ) );
2008-01-31 01:30:52 +00:00
}
2008-02-07 20:23:58 +00:00
2015-11-11 18:47:51 +00:00
if( path ) // Should not occur
path->AppendPoint( mapPt( track->GetEnd() ) );
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 vias, once per unique size and drill diameter combo.
static const KICAD_T scanVIAs[] = { PCB_VIA_T, 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
for( int i = 0; i<items.GetCount(); ++i )
2008-02-01 01:09:39 +00:00
{
::VIA* via = (::VIA*) items[i];
wxASSERT( via->Type() == PCB_VIA_T );
2008-02-07 20:23:58 +00:00
int netcode = via->GetNetCode();
2008-02-21 06:04:32 +00:00
if( netcode == 0 )
continue;
PADSTACK* padstack = makeVia( via );
PADSTACK* registered = pcb->library->LookupVia( padstack );
// if the one looked up is not our padstack, then delete our padstack
// since it was a duplicate of one already registered.
2008-02-01 01:09:39 +00:00
if( padstack != registered )
{
delete padstack;
}
WIRE_VIA* dsnVia = new WIRE_VIA( pcb->wiring );
2008-02-01 01:09:39 +00:00
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
NETINFO_ITEM* net = aBoard->FindNet( netcode );
wxASSERT( net );
2008-02-07 20:23:58 +00:00
dsnVia->net_id = TO_UTF8( net->GetNetname() );
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-07 20:23:58 +00:00
2008-02-01 01:09:39 +00:00
//-----<via_descriptor>-------------------------------------------------
{
// The pcb->library will output <padstack_descriptors> which is a combined
// list of part padstacks and via padstacks. specctra dsn uses the
// <via_descriptors> to say which of those padstacks are vias.
// Output the vias in the padstack list here, by name only. This must
2008-02-01 01:09:39 +00:00
// be done after exporting existing vias as WIRE_VIAs.
VIA* vias = pcb->structure->via;
2008-02-01 01:09:39 +00:00
for( unsigned viaNdx = 0; viaNdx < pcb->library->vias.size(); ++viaNdx )
2008-02-01 01:09:39 +00:00
{
vias->AppendVia( pcb->library->vias[viaNdx].padstack_id.c_str() );
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
2009-09-10 15:22:26 +00:00
//-----<output NETCLASSs>----------------------------------------------------
NETCLASSES& nclasses = aBoard->GetDesignSettings().m_NetClasses;
2009-09-10 15:22:26 +00:00
exportNETCLASS( nclasses.GetDefault(), aBoard );
2009-09-10 15:22:26 +00:00
for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
2008-02-29 06:49:34 +00:00
{
NETCLASSPTR netclass = nc->second;
exportNETCLASS( netclass, aBoard );
2009-09-10 15:22:26 +00:00
}
}
2008-02-29 06:49:34 +00:00
void SPECCTRA_DB::exportNETCLASS( NETCLASSPTR aNetClass, BOARD* aBoard )
2009-09-10 15:22:26 +00:00
{
/* From page 11 of specctra spec:
*
* Routing and Placement Rule Hierarchies
*
* Routing and placement rules can be defined at multiple levels of design
* specification. When a routing or placement rule is defined for an object at
* multiple levels, a predefined routing or placement precedence order
* automatically determines which rule to apply to the object. The routing rule
* precedence order is
*
* pcb < layer < class < class layer < group_set < group_set layer < net <
* net layer < group < group layer < fromto < fromto layer < class_class <
* class_class layer < padstack < region < class region < net region <
* class_class region
*
* A pcb rule (global rule for the PCB design) has the lowest precedence in the
* hierarchy. A class-to-class region rule has the highest precedence. Rules
* set at one level of the hierarchy override conflicting rules set at lower
* levels. The placement rule precedence order is
*
* pcb < image_set < image < component < super cluster < room <
* room_image_set < family_family < image_image
*
* A pcb rule (global rule for the PCB design) has the lowest precedence in the
* hierarchy. An image-to-image rule has the highest precedence. Rules set at
* one level of the hierarchy override conflicting rules set at lower levels.
*/
char text[256];
2008-02-29 06:49:34 +00:00
2009-09-10 15:22:26 +00:00
CLASS* clazz = new CLASS( pcb->network );
2009-09-10 15:22:26 +00:00
pcb->network->classes.push_back( clazz );
2008-02-29 06:49:34 +00:00
2009-09-10 15:22:26 +00:00
// 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.
clazz->class_id = TO_UTF8( aNetClass->GetName() );
2008-02-29 06:49:34 +00:00
for( NETCLASS::iterator net = aNetClass->begin(); net != aNetClass->end(); ++net )
clazz->net_ids.push_back( TO_UTF8( *net ) );
2008-02-14 15:34:40 +00:00
2009-09-10 15:22:26 +00:00
clazz->rules = new RULE( clazz, T_rule );
// output the track width.
int trackWidth = aNetClass->GetTrackWidth();
2009-09-10 15:22:26 +00:00
sprintf( text, "(width %.6g)", scale( trackWidth ) );
clazz->rules->rules.push_back( text );
// output the clearance.
int clearance = aNetClass->GetClearance();
sprintf( text, "(clearance %.6g)", scale( clearance ) + safetyMargin );
clazz->rules->rules.push_back( text );
if( aNetClass->GetName() == NETCLASS::Default )
{
clazz->class_id = "kicad_default";
}
// the easiest way to get the via name is to create a via (which generates
// the name internal to the PADSTACK), and then grab the name and then
// delete the via. There are not that many netclasses so
// this should never become a performance issue.
PADSTACK* via = makeVia( aNetClass->GetViaDiameter(), aNetClass->GetViaDrill(),
m_top_via_layer, m_bot_via_layer );
snprintf( text, sizeof(text), "(use_via %s)", via->GetPadstackId().c_str() );
clazz->circuit.push_back( text );
delete via;
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->SetFlag( 0 );
if( module->GetLayer() == B_Cu )
2008-02-14 15:34:40 +00:00
{
module->Flip( module->GetPosition() );
module->SetFlag( 1 );
2008-02-14 15:34:40 +00:00
}
}
modulesAreFlipped = true;
}
void SPECCTRA_DB::RevertMODULEs( BOARD* aBoard )
{
if( !modulesAreFlipped )
return;
2008-02-07 20:23:58 +00:00
// DSN Images (=KiCad MODULES and pads) must be presented from the
// top view. Restore those that were flipped.
2008-01-25 22:03:36 +00:00
for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
{
if( module->GetFlag() )
2008-01-25 22:03:36 +00:00
{
module->Flip( module->GetPosition() );
module->SetFlag( 0 );
2008-01-25 22:03:36 +00:00
}
}
2008-02-14 15:34:40 +00:00
modulesAreFlipped = false;
2008-01-22 20:48:02 +00:00
}
2008-01-21 21:24:39 +00:00
} // namespace DSN