Commit 2d0d8050 authored by Wayne Stambaugh's avatar Wayne Stambaugh

Add Pcbnew s-expression file parser.

* Add s-expression file parser object and keyword files.
* Fix minor issues with s-expression file formatting.
* Fix a minor bug the zone container fill state parsing in the legacy plugin.
* Move EDA_TEXT visibility definition to eda_text.h.
* Add minor BOARD_ITEM object improvements to support s-expression file
  parser.
parent de471744
......@@ -30,3 +30,5 @@ new/sweet_keywords.cpp
new/sweet_lexer.h
bitmaps_png/png*
bitmaps_png/tmp
common/pcb_keywords.cpp
include/pcb_lexer.h
......@@ -177,7 +177,7 @@ GLuint EDA_3D_CANVAS::CreateDrawGL_List()
// because all boards thickness no not match with this setup:
// double epoxy_width = 1.6; // epoxy width in mm
g_Parm_3D_Visu.m_Epoxy_Width = pcb->GetDesignSettings().m_BoardThickness
g_Parm_3D_Visu.m_Epoxy_Width = pcb->GetDesignSettings().GetBoardThickness()
* g_Parm_3D_Visu.m_BoardScale;
// calculate z position for each layer
......
......@@ -121,6 +121,8 @@ set(PCB_COMMON_SRCS
../pcbnew/legacy_plugin.cpp
../pcbnew/kicad_plugin.cpp
pcb_plot_params_keywords.cpp
pcb_keywords.cpp
../pcbnew/pcb_parser.cpp
)
......@@ -148,6 +150,14 @@ make_lexer(
PCBPLOTPARAMS_T
)
# auto-generate pcbnew_sexpr.h and pcbnew_sexpr.cpp
make_lexer( ${CMAKE_CURRENT_SOURCE_DIR}/pcb.keywords
${PROJECT_SOURCE_DIR}/include/pcb_lexer.h
${CMAKE_CURRENT_SOURCE_DIR}/pcb_keywords.cpp
PCB
)
# The dsntest may not build properly using MS Visual Studio.
if(NOT MSVC)
# This one gets made only when testing.
......
......@@ -354,7 +354,11 @@ void EDA_TEXT::Format( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aControl
// Add font support here at some point in the future.
if( ( m_Size.x != DEFAULT_SIZE_TEXT ) || ( m_Size.y != DEFAULT_SIZE_TEXT ) )
aFormatter->Print( 0, " (size %s)", FMT_IU( m_Size ).c_str() );
aFormatter->Print( 0, " (size %s %s)", FMT_IU( m_Size.GetHeight() ).c_str(),
FMT_IU( m_Size.GetWidth() ).c_str() );
if( m_Thickness != 0 )
aFormatter->Print( 0, " (thickness %s)", FMT_IU( m_Thickness ).c_str() );
if( m_Bold )
aFormatter->Print( 0, " bold" );
......
#
# This program source code file is part of KiCad, a free EDA CAD application.
#
# Copyright (C) 2012 CERN.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, you may find one here:
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# or you may search the http://www.gnu.org website for the version 2 license,
# or you may write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
# These are the keywords for the Pcbnew s-expression file format.
add_net
angle
arc
arc_segments
area
arrow1a
arrow1b
arrow2a
arrow2b
at
attr
autoplace_cost90
autoplace_cost180
aux_axis_origin
blind
bold
bottom
center
chamfer
circle
clearance
comment
company
connect
connect_pads
crossbar
date
descr
die_length
dimension
drawings
drill
edge
edge_width
effects
end
feature1
feature2
fill
fill_segments
filled_polygon
fillet
font
fp_arc
fp_circle
fp_curve
fp_line
fp_poly
fp_text
full
general
gr_arc
gr_circle
gr_curve
gr_line
gr_poly
gr_text
hatch
hide
italic
justify
kicad_pcb
last_trace_width
layer
layers
left
links
locked
micro
min_thickness
mirror
mod_edge_width
mod_text_size
mod_text_width
mode
model
module
net
net_class
net_name
nets
no
no_connects
none
np_thru_hole
offset
oval
pad
pad_drill
pad_size
pad_to_mask_clearance
pad_to_paste_clearance
pad_to_paste_clearance_ratio
page
path
pcb_text_size
pcb_text_width
placed
plus
polygon
portrait
priority
pts
radius
rev
rectangle
reference
right
rotate
scale
segment
segment_width
setup
size
smd
smoothing
solder_mask_margin
solder_paste_margin
solder_paste_margin_ratio
solder_paste_ratio
start
status
tags
target
title
title_block
tedit
thermal_width
thermal_gap
thermal_bridge_width
thickness
top
trace_width
tracks
trace_min
trace_clearance
trapezoid
thru
thru_hole
tstamp
use_thermal
user
user_trace_width
user_via
uvia_dia
uvia_drill
uvia_min_drill
uvia_min_size
uvia_size
uvias_allowed
value
version
via
via_dia
via_drill
via_min_drill
via_min_size
via_size
virtual
visible_elements
width
x
xy
xyz
yes
zone
zone_45_only
zone_clearance
zone_connect
zones
......@@ -1673,28 +1673,36 @@ void TITLE_BLOCK::Format( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aCont
aFormatter->Print( aNestLevel, "(title_block\n" );
if( !m_title.IsEmpty() )
aFormatter->Print( aNestLevel+1, "(title %s)\n", EscapedUTF8( m_title ).c_str() );
aFormatter->Print( aNestLevel+1, "(title %s)\n",
aFormatter->Quotew( m_title ).c_str() );
if( !m_date.IsEmpty() )
aFormatter->Print( aNestLevel+1, "(date %s)\n", EscapedUTF8( m_date ).c_str() );
aFormatter->Print( aNestLevel+1, "(date %s)\n",
aFormatter->Quotew( m_date ).c_str() );
if( !m_revision.IsEmpty() )
aFormatter->Print( aNestLevel+1, "(rev %s)\n", EscapedUTF8( m_revision ).c_str() );
aFormatter->Print( aNestLevel+1, "(rev %s)\n",
aFormatter->Quotew( m_revision ).c_str() );
if( !m_company.IsEmpty() )
aFormatter->Print( aNestLevel+1, "(company %s)\n", EscapedUTF8( m_company ).c_str() );
aFormatter->Print( aNestLevel+1, "(company %s)\n",
aFormatter->Quotew( m_company ).c_str() );
if( !m_comment1.IsEmpty() )
aFormatter->Print( aNestLevel+1, "(comment1 %s)\n", EscapedUTF8( m_comment1 ).c_str() );
aFormatter->Print( aNestLevel+1, "(comment 1 %s)\n",
aFormatter->Quotew( m_comment1 ).c_str() );
if( !m_comment2.IsEmpty() )
aFormatter->Print( aNestLevel+1, "(comment2 %s)\n", EscapedUTF8( m_comment2 ).c_str() );
aFormatter->Print( aNestLevel+1, "(comment 2 %s)\n",
aFormatter->Quotew( m_comment2 ).c_str() );
if( !m_comment3.IsEmpty() )
aFormatter->Print( aNestLevel+1, "(comment3 %s)\n", EscapedUTF8( m_comment3 ).c_str() );
aFormatter->Print( aNestLevel+1, "(comment 3 %s)\n",
aFormatter->Quotew( m_comment3 ).c_str() );
if( !m_comment4.IsEmpty() )
aFormatter->Print( aNestLevel+1, "(comment4 %s)\n", EscapedUTF8( m_comment4 ).c_str() );
aFormatter->Print( aNestLevel+1, "(comment 4 %s)\n",
aFormatter->Quotew( m_comment4 ).c_str() );
aFormatter->Print( aNestLevel, ")\n\n" );
}
......
......@@ -33,8 +33,6 @@ class TRANSFORM;
#define HIGHLIGHT_COLOR WHITE
#define TEXT_NO_VISIBLE 1
//#define GR_DEFAULT_DRAWMODE GR_COPY
#define GR_DEFAULT_DRAWMODE GR_COPY
......
......@@ -37,7 +37,6 @@ public:
int m_SolderPasteMargin; ///< Solder paste margin absolute value
double m_SolderPasteMarginRatio; ///< Solder pask margin ratio value of pad size
///< The final margin is the sum of these 2 values
int m_BoardThickness; ///< Board Thickness for 3D viewer
// Variables used in footprint handling
wxSize m_ModuleTextSize; ///< Default footprint texts size
......@@ -188,11 +187,15 @@ public:
*/
void AppendConfigs( PARAM_CFG_ARRAY* aResult );
int GetBoardThickness() const { return m_boardThickness; }
void SetBoardThickness( int aThickness ) { m_boardThickness = aThickness; }
private:
int m_CopperLayerCount; ///< Number of copper layers for this design
int m_EnabledLayers; ///< Bit-mask for layer enabling
int m_VisibleLayers; ///< Bit-mask for layer visibility
int m_VisibleElements; ///< Bit-mask for element category visibility
int m_boardThickness; ///< Board thickness for 3D viewer
};
#endif // BOARD_DESIGN_SETTINGS_H_
......@@ -57,7 +57,10 @@ enum EDA_DRAW_MODE_T {
SKETCH // sketch mode: segments have thickness, but are not filled
};
#define DEFAULT_SIZE_TEXT 60 /* default text height (in mils or 1/1000") */
#define TEXT_NO_VISIBLE 1 //< EDA_TEXT::m_Attribut(e?) visibility flag.
/**
* Class EDA_TEXT
......@@ -110,6 +113,15 @@ public:
void SetItalic( bool isItalic ) { m_Italic = isItalic; }
bool IsItalic() const { return m_Italic; }
void SetBold( bool aBold ) { m_Bold = aBold; }
bool IsBold() const { return m_Bold; }
void SetVisible( bool aVisible )
{
( aVisible ) ? m_Attributs &= ~TEXT_NO_VISIBLE : m_Attributs |= TEXT_NO_VISIBLE;
}
bool IsVisible() const { return !( m_Attributs & TEXT_NO_VISIBLE ); }
void SetMirrored( bool isMirrored ) { m_Mirror = isMirrored; }
bool IsMirrored() const { return m_Mirror; }
......
......@@ -7,6 +7,7 @@
#define _LAYERS_ID_AND_VISIBILITY_H_
/* Layer identification (layer number) */
#define UNDEFINED_LAYER -1
#define FIRST_COPPER_LAYER 0
#define LAYER_N_BACK 0
#define LAYER_N_2 1
......
......@@ -539,7 +539,10 @@ bool TREE_PROJECT_FRAME::AddFileToTree( const wxString& aName,
bool addFile = false;
for( unsigned i = 0; i < m_Filters.size(); i++ )
{
reg.Compile( m_Filters[i], wxRE_ICASE );
wxCHECK2_MSG( reg.Compile( m_Filters[i], wxRE_ICASE ), continue,
wxT( "Regular expression " ) + m_Filters[i] +
wxT( " failed to compile." ) );
if( reg.Matches( aName ) )
{
addFile = true;
......
......@@ -339,6 +339,18 @@ int BOARD::GetCurrentMicroViaDrill()
}
bool BOARD::SetLayer( int aIndex, const LAYER& aLayer )
{
if( aIndex < NB_COPPER_LAYERS )
{
m_Layer[ aIndex ] = aLayer;
return true;
}
return false;
}
wxString BOARD::GetLayerName( int aLayerIndex ) const
{
if( !IsValidLayerIndex( aLayerIndex ) )
......@@ -407,7 +419,7 @@ bool BOARD::SetLayerName( int aLayerIndex, const wxString& aLayerName )
if( !IsValidCopperLayerIndex( aLayerIndex ) )
return false;
if( aLayerName == wxEmptyString || aLayerName.Len() > 20 )
if( aLayerName == wxEmptyString || aLayerName.Len() > 20 )
return false;
// no quote chars in the name allowed
......@@ -506,14 +518,65 @@ LAYER_T LAYER::ParseType( const char* aType )
else if( strcmp( aType, "jumper" ) == 0 )
return LT_JUMPER;
else
return LAYER_T( -1 );
return LT_UNDEFINED;
}
int LAYER::GetDefaultIndex( const wxString& aName )
{
static LAYER_INDEX_HASH_MAP layerIndices;
if( layerIndices.empty() )
{
// These are only default layer names. The copper names may be over-ridden in
// the BOARD (*.brd) file.
layerIndices[ _( "Front" ) ] = LAYER_N_FRONT;
layerIndices[ _( "Inner2" ) ] = LAYER_N_2;
layerIndices[ _( "Inner3" ) ] = LAYER_N_3;
layerIndices[ _( "Inner4" ) ] = LAYER_N_4;
layerIndices[ _( "Inner5" ) ] = LAYER_N_5;
layerIndices[ _( "Inner6" ) ] = LAYER_N_6;
layerIndices[ _( "Inner7" ) ] = LAYER_N_7;
layerIndices[ _( "Inner8" ) ] = LAYER_N_8;
layerIndices[ _( "Inner9" ) ] = LAYER_N_9;
layerIndices[ _( "Inner10" ) ] = LAYER_N_10;
layerIndices[ _( "Inner11" ) ] = LAYER_N_11;
layerIndices[ _( "Inner12" ) ] = LAYER_N_12;
layerIndices[ _( "Inner13" ) ] = LAYER_N_13;
layerIndices[ _( "Inner14" ) ] = LAYER_N_14;
layerIndices[ _( "Inner15" ) ] = LAYER_N_15;
layerIndices[ _( "Back" ) ] = LAYER_N_BACK;
layerIndices[ _( "Adhes_Back" ) ] = ADHESIVE_N_BACK;
layerIndices[ _( "Adhes_Front" ) ] = ADHESIVE_N_FRONT;
layerIndices[ _( "SoldP_Back" ) ] = SOLDERPASTE_N_BACK;
layerIndices[ _( "SoldP_Front" ) ] = SOLDERPASTE_N_FRONT;
layerIndices[ _( "SilkS_Back" ) ] = SILKSCREEN_N_BACK;
layerIndices[ _( "SilkS_Front" ) ] = SILKSCREEN_N_FRONT;
layerIndices[ _( "Mask_Back" ) ] = SOLDERMASK_N_BACK;
layerIndices[ _( "Mask_Front" ) ] = SOLDERMASK_N_FRONT;
layerIndices[ _( "Drawings" ) ] = DRAW_N;
layerIndices[ _( "Comments" ) ] = COMMENT_N;
layerIndices[ _( "Eco1" ) ] = ECO1_N;
layerIndices[ _( "Eco2" ) ] = ECO2_N;
layerIndices[ _( "PCB_Edges" ) ] = EDGE_N;
}
const LAYER_INDEX_HASH_MAP::iterator it = layerIndices.find( aName );
if( it == layerIndices.end() )
return UNDEFINED_LAYER;
return layerIndices[ aName ];
}
int BOARD::GetCopperLayerCount() const
{
return m_designSettings.GetCopperLayerCount();
}
void BOARD::SetCopperLayerCount( int aCount )
{
m_designSettings.SetCopperLayerCount( aCount );
......
......@@ -19,6 +19,9 @@
#include <class_zone_settings.h>
#include <pcb_plot_params.h>
#include <wx/hashmap.h>
class PCB_BASE_FRAME;
class PCB_EDIT_FRAME;
class PICKED_ITEMS_LIST;
......@@ -43,6 +46,7 @@ typedef std::vector< TRACK* > TRACK_PTRS;
*/
enum LAYER_T
{
LT_UNDEFINED = -1,
LT_SIGNAL,
LT_POWER,
LT_MIXED,
......@@ -51,11 +55,27 @@ enum LAYER_T
/**
* Struct LAYER
* Class LAYER
* holds information pertinent to a layer of a BOARD.
*/
struct LAYER
class LAYER
{
public:
LAYER( const wxString& aName = wxEmptyString, LAYER_T aType = LT_SIGNAL,
bool aVisible = true ) :
m_Name( aName ),
m_Type( aType ),
m_visible( aVisible ),
m_fixedListIndex( UNDEFINED_LAYER )
{
}
void SetVisible( bool aEnable ) { m_visible = aEnable; }
bool IsVisible() const { return m_visible; }
void SetFixedListIndex( int aIndex ) { m_fixedListIndex = aIndex; }
int GetFixedListIndex() const { return m_fixedListIndex; }
/** The name of the layer, there should be no spaces in this name. */
wxString m_Name;
......@@ -63,7 +83,6 @@ struct LAYER
LAYER_T m_Type;
// int m_Color;
// bool m_Visible; // ? use flags in m_Color instead ?
/**
* Function ShowType
......@@ -81,9 +100,25 @@ struct LAYER
* LAYER_T(-1) if the string is invalid
*/
static LAYER_T ParseType( const char* aType );
/**
* Function GetDefaultIndex
* returns the layer index of the layer \a aName.
*
* @param aName A reference to a wxString object containing the default layer name.
* @return The index of the layer \a aName if found otherwise #UNDEFINED_LAYER_INDEX.
*/
static int GetDefaultIndex( const wxString& aName );
private:
bool m_visible;
int m_fixedListIndex;
};
WX_DECLARE_STRING_HASH_MAP( int, LAYER_INDEX_HASH_MAP );
/**
* Struct VIA_DIMENSION
* is a small helper container to handle a stock of specific vias each with
......@@ -600,6 +635,8 @@ public:
*/
bool SetLayerName( int aLayerIndex, const wxString& aLayerName );
bool SetLayer( int aIndex, const LAYER& aLayer );
/**
* Function GetLayerType
* returns the type of the copper layer given by aLayerIndex.
......
......@@ -62,7 +62,7 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS() :
m_ModuleSegmentWidth = DMils2iu( 100 );
// Layer thickness for 3D viewer
m_BoardThickness = DMils2iu( DEFAULT_BOARD_THICKNESS_DMILS );
m_boardThickness = DMils2iu( DEFAULT_BOARD_THICKNESS_DMILS );
}
......@@ -70,7 +70,7 @@ void BOARD_DESIGN_SETTINGS::AppendConfigs( PARAM_CFG_ARRAY* aResult )
{
m_Pad_Master.AppendConfigs( aResult );
aResult->push_back( new PARAM_CFG_INT( wxT( "BoardThickness" ), &m_BoardThickness,
aResult->push_back( new PARAM_CFG_INT( wxT( "BoardThickness" ), &m_boardThickness,
DMils2iu( DEFAULT_BOARD_THICKNESS_DMILS ), 0, 0xFFFF ) );
aResult->push_back( new PARAM_CFG_INT( wxT( "TxtPcbV" ), &m_PcbTextSize.y,
......
......@@ -91,31 +91,15 @@ wxString BOARD_ITEM::GetLayerName() const
std::string BOARD_ITEM::FormatInternalUnits( int aValue )
{
char buf[50];
char buf[50];
#if defined( USE_PCBNEW_NANOMETRES )
double engUnits = aValue / 1000000.0;
int nm = aValue;
#else
double engUnits = ( aValue * 10000.0 ) / 25.4 / 1000000.0;
int nm = KIROUND( ( aValue / 10000.0 ) * 25.4 * 1e6 );
#endif
int len;
if( engUnits != 0.0 && fabs( engUnits ) <= 0.0001 )
{
// printf( "f: " );
len = snprintf( buf, 49, "%.10f", engUnits );
while( --len > 0 && buf[len] == '0' )
buf[len] = '\0';
++len;
}
else
{
// printf( "g: " );
len = snprintf( buf, 49, "%.10g", engUnits );
}
int len = snprintf( buf, 49, "%g", nm / 1e6 );
return std::string( buf, len );
}
......
......@@ -32,6 +32,7 @@
#include <class_board_item.h>
#include <class_pcb_text.h>
class LINE_READER;
......
......@@ -572,6 +572,20 @@ D_PAD* MODULE::GetPad( const wxPoint& aPosition, int aLayerMask )
}
void MODULE::Add3DModel( S3D_MASTER* a3DModel )
{
a3DModel->SetParent( this );
m_3D_Drawings.PushBack( a3DModel );
}
void MODULE::AddPad( D_PAD* aPad )
{
aPad->SetParent( this );
m_Pads.PushBack( aPad );
}
// see class_module.h
SEARCH_RESULT MODULE::Visit( INSPECTOR* inspector, const void* testData,
const KICAD_T scanTypes[] )
......
......@@ -325,6 +325,22 @@ public:
*/
unsigned GetPadCount() const { return m_Pads.GetCount() ; }
/**
* Function Add3DModel
* adds \a a3DModel definition to the end of the 3D model list.
*
* @param a3DModel A pointer to a #S3D_MASTER to add to the list.
*/
void Add3DModel( S3D_MASTER* a3DModel );
/**
* Function AddPad
* adds \a aPad to the end of the pad list.
*
* @param aPad A pointer to a #D_PAD to add to the list.
*/
void AddPad( D_PAD* aPad );
SEARCH_RESULT Visit( INSPECTOR* inspector, const void* testData,
const KICAD_T scanTypes[] );
......
......@@ -916,6 +916,24 @@ ZoneConnection ZONE_CONTAINER::GetPadConnection( D_PAD* aPad ) const
}
void ZONE_CONTAINER::AddPolygon( std::vector< wxPoint >& aPolygon )
{
if( aPolygon.empty() )
return;
for( unsigned i = 0; i < aPolygon.size(); i++ )
{
if( i == 0 )
m_Poly->Start( GetLayer(), aPolygon[i].x, aPolygon[i].y, GetHatchStyle() );
else
AppendCorner( aPolygon[i] );
}
m_Poly->Close();
}
wxString ZONE_CONTAINER::GetSelectMenuText() const
{
wxString text;
......
......@@ -429,6 +429,11 @@ public:
return m_Poly->GetHatchStyle();
}
void SetHatchStyle( CPolyLine::hatch_style aStyle )
{
m_Poly->SetHatchStyle( aStyle );
}
/**
* Function TransformShapeWithClearanceToPolygon
* Convert the track shape to a closed polygon
......@@ -515,6 +520,18 @@ public:
unsigned int GetCornerRadius() const { return cornerRadius; };
void AddPolygon( std::vector< wxPoint >& aPolygon );
void AddFilledPolygon( std::vector< CPolyPt >& aPolygon )
{
m_FilledPolysList.insert( m_FilledPolysList.end(), aPolygon.begin(), aPolygon.end() );
}
void AddFillSegments( std::vector< SEGMENT >& aSegments )
{
m_FillSegmList.insert( m_FillSegmList.end(), aSegments.begin(), aSegments.end() );
}
virtual wxString GetSelectMenuText() const;
virtual BITMAP_DEF GetMenuImage() const { return add_zone_xpm; }
......
......@@ -369,7 +369,7 @@ static void compute_layer_Zs( BOARD* pcb ) //{{{
int copper_layers = pcb->GetCopperLayerCount( );
// We call it 'layer' thickness, but it's the whole board thickness!
double board_thickness = pcb->GetDesignSettings().m_BoardThickness;
double board_thickness = pcb->GetDesignSettings().GetBoardThickness();
double half_thickness = board_thickness / 2;
// Compute each layer's Z value, more or less like the 3d view
......
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 CERN
* Copyright (C) 1992-2011 KiCad Developers, see change_log.txt for contributors.
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <fctsys.h>
#include <kicad_string.h>
#include <common.h>
#include <build_version.h> // LEGACY_BOARD_FILE_VERSION
#include <3d_struct.h>
#include <class_board.h>
#include <class_module.h>
#include <class_pcb_text.h>
#include <class_dimension.h>
#include <class_track.h>
#include <class_zone.h>
#include <class_drawsegment.h>
#include <class_mire.h>
#include <class_edge_mod.h>
#include <zones.h>
#include <kicad_plugin.h>
#include <wx/wfstream.h>
#define FMTIU BOARD_ITEM::FormatInternalUnits
void PCB_IO::Save( const wxString& aFileName, BOARD* aBoard, PROPERTIES* aProperties )
{
LOCALE_IO toggle; // toggles on, then off, the C locale.
m_board = aBoard;
wxFileOutputStream fs( aFileName );
if( !fs.IsOk() )
{
m_error.Printf( _( "cannot open file '%s'" ), aFileName.GetData() );
THROW_IO_ERROR( m_error );
}
STREAM_OUTPUTFORMATTER formatter( fs );
formatter.Print( 0, "(kicad-board (version %d) (host pcbnew %s)\n", SEXPR_BOARD_FILE_VERSION,
formatter.Quotew( GetBuildVersion() ).c_str() );
Format( aBoard, (OUTPUTFORMATTER*) &formatter, 1, 0 );
formatter.Print( 0, ")\n" );
}
void PCB_IO::Format( BOARD_ITEM* aItem, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
switch( aItem->Type() )
{
case PCB_T:
format( (BOARD*) aItem, aFormatter, aNestLevel, aControlBits );
break;
case PCB_DIMENSION_T:
format( ( DIMENSION*) aItem, aFormatter, aNestLevel, aControlBits );
break;
case PCB_LINE_T:
format( (DRAWSEGMENT*) aItem, aFormatter, aNestLevel, aControlBits );
break;
case PCB_MODULE_EDGE_T:
format( (EDGE_MODULE*) aItem, aFormatter, aNestLevel, aControlBits );
break;
case PCB_TARGET_T:
format( (PCB_TARGET*) aItem, aFormatter, aNestLevel, aControlBits );
break;
case PCB_MODULE_T:
format( (MODULE*) aItem, aFormatter, aNestLevel, aControlBits );
break;
case PCB_PAD_T:
format( (D_PAD*) aItem, aFormatter, aNestLevel, aControlBits );
break;
case PCB_TEXT_T:
format( (TEXTE_PCB*) aItem, aFormatter, aNestLevel, aControlBits );
break;
case PCB_MODULE_TEXT_T:
format( (TEXTE_MODULE*) aItem, aFormatter, aNestLevel, aControlBits );
break;
case PCB_TRACE_T:
case PCB_VIA_T:
format( (TRACK*) aItem, aFormatter, aNestLevel, aControlBits );
break;
case PCB_ZONE_AREA_T:
format( (ZONE_CONTAINER*) aItem, aFormatter, aNestLevel, aControlBits );
break;
default:
wxFAIL_MSG( wxT( "Cannot format item " ) + aItem->GetClass() );
}
}
void PCB_IO::format( BOARD* aBoard, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
aFormatter->Print( aNestLevel, ")\n" );
aFormatter->Print( 0, "\n" );
aFormatter->Print( aNestLevel, "(general\n" );
aFormatter->Print( aNestLevel+1, "(links %d)\n", aBoard->GetRatsnestsCount() );
aFormatter->Print( aNestLevel+1, "(no_connects %d)\n", aBoard->m_NbNoconnect );
// Write Bounding box info
aFormatter->Print( aNestLevel+1, "(area %s %s %s %s)\n",
FMTIU( aBoard->GetBoundingBox().GetX() ).c_str(),
FMTIU( aBoard->GetBoundingBox().GetY() ).c_str(),
FMTIU( aBoard->GetBoundingBox().GetRight() ).c_str(),
FMTIU( aBoard->GetBoundingBox().GetBottom() ).c_str() );
aFormatter->Print( aNestLevel+1, "(thickness %s)\n",
FMTIU( aBoard->GetDesignSettings().m_BoardThickness ).c_str() );
aFormatter->Print( aNestLevel+1, "(drawings %d)\n", aBoard->m_Drawings.GetCount() );
aFormatter->Print( aNestLevel+1, "(tracks %d)\n", aBoard->GetNumSegmTrack() );
aFormatter->Print( aNestLevel+1, "(zones %d)\n", aBoard->GetNumSegmZone() );
aFormatter->Print( aNestLevel+1, "(modules %d)\n", aBoard->m_Modules.GetCount() );
aFormatter->Print( aNestLevel+1, "(nets %d)\n", aBoard->GetNetCount() );
aFormatter->Print( aNestLevel, ")\n\n" );
aBoard->GetPageSettings().Format( aFormatter, aNestLevel, aControlBits );
aBoard->GetTitleBlock().Format( aFormatter, aNestLevel, aControlBits );
// Layers.
aFormatter->Print( aNestLevel, "(layers %d %08X", aBoard->GetCopperLayerCount(),
aBoard->GetEnabledLayers() );
if( aBoard->GetEnabledLayers() != aBoard->GetVisibleLayers() )
aFormatter->Print( 0, " %08X", aBoard->GetVisibleLayers() );
aFormatter->Print( 0, "\n" );
unsigned layerMask = ALL_CU_LAYERS & aBoard->GetEnabledLayers();
for( int layer = 0; layerMask; ++layer, layerMask >>= 1 )
{
if( layerMask & 1 )
{
aFormatter->Print( aNestLevel+1, "(layer%d %s %s)\n", layer,
aFormatter->Quotew( aBoard->GetLayerName( layer ) ).c_str(),
LAYER::ShowType( aBoard->GetLayerType( layer ) ) );
}
}
aFormatter->Print( aNestLevel, ")\n\n" );
// Setup
aFormatter->Print( aNestLevel, "(setup\n" );
// Save current default track width, for compatibility with older Pcbnew version;
aFormatter->Print( aNestLevel+1, "(last_trace_width %s)\n",
FMTIU( aBoard->m_TrackWidthList[aBoard->m_TrackWidthSelector] ).c_str() );
// Save custom tracks width list (the first is not saved here: this is the netclass value
for( unsigned ii = 1; ii < aBoard->m_TrackWidthList.size(); ii++ )
aFormatter->Print( aNestLevel+1, "(user_trace_width%d %s)\n",
ii+1, FMTIU( aBoard->m_TrackWidthList[ii] ).c_str() );
aFormatter->Print( aNestLevel+1, "(trace_clearance %s)\n",
FMTIU( aBoard->m_NetClasses.GetDefault()->GetClearance() ).c_str() );
// ZONE_SETTINGS
aFormatter->Print( aNestLevel+1, "(zone_clearance %s)\n",
FMTIU( aBoard->GetZoneSettings().m_ZoneClearance ).c_str() );
aFormatter->Print( aNestLevel+1, "(zone_45_only %d)\n",
aBoard->GetZoneSettings().m_Zone_45_Only );
aFormatter->Print( aNestLevel+1, "(trace_min %s)\n",
FMTIU( aBoard->GetDesignSettings().m_TrackMinWidth ).c_str() );
aFormatter->Print( aNestLevel+1, "(segment_width %s)\n",
FMTIU( aBoard->GetDesignSettings().m_DrawSegmentWidth ).c_str() );
aFormatter->Print( aNestLevel+1, "(edge_width %s)\n",
FMTIU( aBoard->GetDesignSettings().m_EdgeSegmentWidth ).c_str() );
// Save current default via size, for compatibility with older Pcbnew version;
aFormatter->Print( aNestLevel+1, "(via_size %s)\n",
FMTIU( aBoard->m_NetClasses.GetDefault()->GetViaDiameter() ).c_str() );
aFormatter->Print( aNestLevel+1, "(via_drill %s)\n",
FMTIU( aBoard->m_NetClasses.GetDefault()->GetViaDrill() ).c_str() );
aFormatter->Print( aNestLevel+1, "(via_min_size %s)\n",
FMTIU( aBoard->GetDesignSettings().m_ViasMinSize ).c_str() );
aFormatter->Print( aNestLevel+1, "(via_min_drill %s)\n",
FMTIU( aBoard->GetDesignSettings().m_ViasMinDrill ).c_str() );
// Save custom vias diameters list (the first is not saved here: this is
// the netclass value
for( unsigned ii = 1; ii < aBoard->m_ViasDimensionsList.size(); ii++ )
aFormatter->Print( aNestLevel+1, "(user_via%d %s %s)\n", ii,
FMTIU( aBoard->m_ViasDimensionsList[ii].m_Diameter ).c_str(),
FMTIU( aBoard->m_ViasDimensionsList[ii].m_Drill ).c_str() );
// for old versions compatibility:
aFormatter->Print( aNestLevel+1, "(uvia_size %s)\n",
FMTIU( aBoard->m_NetClasses.GetDefault()->GetuViaDiameter() ).c_str() );
aFormatter->Print( aNestLevel+1, "(uvia_drill %s)\n",
FMTIU( aBoard->m_NetClasses.GetDefault()->GetuViaDrill() ).c_str() );
aFormatter->Print( aNestLevel+1, "(uvias_allow %s)\n",
FMTIU( aBoard->GetDesignSettings().m_MicroViasAllowed ).c_str() );
aFormatter->Print( aNestLevel+1, "(uvia_min_size %s)\n",
FMTIU( aBoard->GetDesignSettings().m_MicroViasMinSize ).c_str() );
aFormatter->Print( aNestLevel+1, "(uvia_min_drill %s)\n",
FMTIU( aBoard->GetDesignSettings().m_MicroViasMinDrill ).c_str() );
aFormatter->Print( aNestLevel+1, "(pcb_text_width %s)\n",
FMTIU( aBoard->GetDesignSettings().m_PcbTextWidth ).c_str() );
aFormatter->Print( aNestLevel+1, "(pcb_text_size %s %s)\n",
FMTIU( aBoard->GetDesignSettings().m_PcbTextSize.x ).c_str(),
FMTIU( aBoard->GetDesignSettings().m_PcbTextSize.y ).c_str() );
aFormatter->Print( aNestLevel+1, "(mod_edge_width %s)\n",
FMTIU( aBoard->GetDesignSettings().m_ModuleSegmentWidth ).c_str() );
aFormatter->Print( aNestLevel+1, "(mod_text_size %s %s)\n",
FMTIU( aBoard->GetDesignSettings().m_ModuleTextSize.x ).c_str(),
FMTIU( aBoard->GetDesignSettings().m_ModuleTextSize.y ).c_str() );
aFormatter->Print( aNestLevel+1, "(mod_text_width %s)\n",
FMTIU( aBoard->GetDesignSettings().m_ModuleTextWidth ).c_str() );
aFormatter->Print( aNestLevel+1, "(pad_size %s %s)\n",
FMTIU( aBoard->GetDesignSettings().m_Pad_Master.GetSize().x ).c_str(),
FMTIU( aBoard->GetDesignSettings().m_Pad_Master.GetSize().x ).c_str() );
aFormatter->Print( aNestLevel+1, "(pad_drill %s)\n",
FMTIU( aBoard->GetDesignSettings().m_Pad_Master.GetDrillSize().x ).c_str() );
aFormatter->Print( aNestLevel+1, "(pad_to_mask_clearance %s)\n",
FMTIU( aBoard->GetDesignSettings().m_SolderMaskMargin ).c_str() );
if( aBoard->GetDesignSettings().m_SolderPasteMargin != 0 )
aFormatter->Print( aNestLevel+1, "(pad_to_paste_clearance %s)\n",
FMTIU( aBoard->GetDesignSettings().m_SolderPasteMargin ).c_str() );
if( aBoard->GetDesignSettings().m_SolderPasteMarginRatio != 0 )
aFormatter->Print( aNestLevel+1, "(pad_to_paste_clearance_ratio %g)\n",
aBoard->GetDesignSettings().m_SolderPasteMarginRatio );
aFormatter->Print( aNestLevel+1, "(aux_axis_origin %s %s)\n",
FMTIU( aBoard->GetOriginAxisPosition().x ).c_str(),
FMTIU( aBoard->GetOriginAxisPosition().y ).c_str() );
aFormatter->Print( aNestLevel+1, "(visible_elements %X)\n",
aBoard->GetDesignSettings().GetVisibleElements() );
aFormatter->Print( aNestLevel, ")\n\n" );
int netcount = aBoard->GetNetCount();
for( int i = 0; i < netcount; ++i )
aFormatter->Print( aNestLevel, "(net %d %s)\n",
aBoard->FindNet( i )->GetNet(),
aFormatter->Quotew( aBoard->FindNet( i )->GetNetname() ).c_str() );
aFormatter->Print( 0, "\n" );
// Save the default net class first.
aBoard->m_NetClasses.GetDefault()->Format( aFormatter, aNestLevel, aControlBits );
// Save the rest of the net classes alphabetically.
for( NETCLASSES::const_iterator it = aBoard->m_NetClasses.begin();
it != aBoard->m_NetClasses.end();
++it )
{
NETCLASS* netclass = it->second;
netclass->Format( aFormatter, aNestLevel, aControlBits );
}
// Save the graphical items on the board (not owned by a module)
for( BOARD_ITEM* item = aBoard->m_Drawings; item; item = item->Next() )
Format( item, aFormatter, aNestLevel, aControlBits );
aFormatter->Print( 0, "\n" );
// Save the modules.
for( MODULE* module = aBoard->m_Modules; module; module = (MODULE*) module->Next() )
{
Format( module, aFormatter, aNestLevel, aControlBits );
aFormatter->Print( 0, "\n" );
}
// Do not save MARKER_PCBs, they can be regenerated easily.
// Save the tracks and vias.
for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
Format( track, aFormatter, aNestLevel, aControlBits );
/// @todo Add warning here that the old segment filed zones are no longer supported and
/// will not be saved.
aFormatter->Print( 0, "\n" );
// Save the polygon (which are the newer technology) zones.
for( int i=0; i < aBoard->GetAreaCount(); ++i )
Format( aBoard->GetArea( i ), aFormatter, aNestLevel, aControlBits );
}
void PCB_IO::format( DIMENSION* aDimension, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
aFormatter->Print( aNestLevel, "(dimension %s (width %s) (layer %s)",
FMT_IU( aDimension->m_Value ).c_str(),
FMT_IU( aDimension->m_Width ).c_str(),
aFormatter->Quotew( aDimension->GetLayerName() ).c_str() );
if( aDimension->GetTimeStamp() )
aFormatter->Print( 0, " (tstamp %lX)", aDimension->GetTimeStamp() );
aFormatter->Print( 0, "\n" );
Format( (TEXTE_PCB*) &aDimension->m_Text, aFormatter, aNestLevel+1, aControlBits );
aFormatter->Print( aNestLevel+1, "(feature1 pts((xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_featureLineDOx ).c_str(),
FMT_IU( aDimension->m_featureLineDOy ).c_str(),
FMT_IU( aDimension->m_featureLineDFx ).c_str(),
FMT_IU( aDimension->m_featureLineDFy ).c_str() );
aFormatter->Print( aNestLevel+1, "(feature2 pts((xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_featureLineGOx ).c_str(),
FMT_IU( aDimension->m_featureLineGOy ).c_str(),
FMT_IU( aDimension->m_featureLineGFx ).c_str(),
FMT_IU( aDimension->m_featureLineGFy ).c_str() );
aFormatter->Print( aNestLevel+1, "(crossbar pts((xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_crossBarOx ).c_str(),
FMT_IU( aDimension->m_crossBarOy ).c_str(),
FMT_IU( aDimension->m_crossBarFx ).c_str(),
FMT_IU( aDimension->m_crossBarFy ).c_str() );
aFormatter->Print( aNestLevel+1, "(arrow1a pts((xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_arrowD1Ox ).c_str(),
FMT_IU( aDimension->m_arrowD1Oy ).c_str(),
FMT_IU( aDimension->m_arrowD1Fx ).c_str(),
FMT_IU( aDimension->m_arrowD1Fy ).c_str() );
aFormatter->Print( aNestLevel+1, "(arrow1b pts((xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_arrowD2Ox ).c_str(),
FMT_IU( aDimension->m_arrowD2Oy ).c_str(),
FMT_IU( aDimension->m_arrowD2Fx ).c_str(),
FMT_IU( aDimension->m_arrowD2Fy ).c_str() );
aFormatter->Print( aNestLevel+1, "(arrow2a pts((xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_arrowG1Ox ).c_str(),
FMT_IU( aDimension->m_arrowG1Oy ).c_str(),
FMT_IU( aDimension->m_arrowG1Fx ).c_str(),
FMT_IU( aDimension->m_arrowG1Fy ).c_str() );
aFormatter->Print( aNestLevel+1, "(arrow2b pts((xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_arrowG2Ox ).c_str(),
FMT_IU( aDimension->m_arrowG2Oy ).c_str(),
FMT_IU( aDimension->m_arrowG2Fx ).c_str(),
FMT_IU( aDimension->m_arrowG2Fy ).c_str() );
aFormatter->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( DRAWSEGMENT* aSegment, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
unsigned i;
switch( aSegment->GetShape() )
{
case S_SEGMENT: // Line
aFormatter->Print( aNestLevel, "(gr_line (pts (xy %s) (xy %s))",
FMT_IU( aSegment->GetStart() ).c_str(),
FMT_IU( aSegment->GetEnd() ).c_str() );
break;
case S_CIRCLE: // Circle
aFormatter->Print( aNestLevel, "(gr_circle (center (xy %s)) (end (xy %s))",
FMT_IU( aSegment->GetStart() ).c_str(),
FMT_IU( aSegment->GetEnd() ).c_str() );
break;
case S_ARC: // Arc
aFormatter->Print( aNestLevel, "(gr_arc (start (xy %s)) (end (xy %s)) (angle %s)",
FMT_IU( aSegment->GetStart() ).c_str(),
FMT_IU( aSegment->GetEnd() ).c_str(),
FMT_ANGLE( aSegment->GetAngle() ).c_str() );
break;
case S_POLYGON: // Polygon
aFormatter->Print( aNestLevel, "(gr_line (pts" );
for( i = 0; i < aSegment->GetPolyPoints().size(); ++i )
aFormatter->Print( 0, " (xy %s)", FMT_IU( aSegment->GetPolyPoints()[i] ).c_str() );
aFormatter->Print( 0, ")" );
break;
case S_CURVE: // Bezier curve
aFormatter->Print( aNestLevel, "(gr_curve pts(xy(%s) xy(%s) xy(%s) xy(%s))",
FMT_IU( aSegment->GetStart() ).c_str(),
FMT_IU( aSegment->GetBezControl1() ).c_str(),
FMT_IU( aSegment->GetBezControl2() ).c_str(),
FMT_IU( aSegment->GetEnd() ).c_str() );
break;
default:
wxFAIL_MSG( wxT( "Cannot format invalid DRAWSEGMENT type." ) );
};
aFormatter->Print( 0, " (layer %s)", aFormatter->Quotew( aSegment->GetLayerName() ).c_str() );
if( aSegment->GetWidth() != 0 )
aFormatter->Print( 0, " (width %s)", FMT_IU( aSegment->GetWidth() ).c_str() );
if( aSegment->GetTimeStamp() )
aFormatter->Print( 0, " (tstamp %lX)", aSegment->GetTimeStamp() );
if( aSegment->GetStatus() )
aFormatter->Print( 0, " (status %X)", aSegment->GetStatus() );
aFormatter->Print( 0, ")\n" );
}
void PCB_IO::format( EDGE_MODULE* aModuleDrawing, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
switch( aModuleDrawing->GetShape() )
{
case S_SEGMENT: // Line
aFormatter->Print( aNestLevel, "(fp_line (pts (xy %s) (xy %s)",
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
FMT_IU( aModuleDrawing->GetEnd0() ).c_str() );
break;
case S_CIRCLE: // Circle
aFormatter->Print( aNestLevel, "(fp_circle (center (xy %s)) (end (xy %s)",
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
FMT_IU( aModuleDrawing->GetEnd0() ).c_str() );
break;
case S_ARC: // Arc
aFormatter->Print( aNestLevel, "(fp_arc (start (xy %s)) (end (xy %s)) (angle %s)",
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
FMT_IU( aModuleDrawing->GetEnd0() ).c_str(),
FMT_ANGLE( aModuleDrawing->GetAngle() ).c_str() );
break;
case S_POLYGON: // Polygon
aFormatter->Print( aNestLevel, "(fp_poly (pts" );
for( unsigned i = 0; i < aModuleDrawing->GetPolyPoints().size(); ++i )
aFormatter->Print( 0, " (xy %s)",
FMT_IU( aModuleDrawing->GetPolyPoints()[i] ).c_str() );
break;
case S_CURVE: // Bezier curve
aFormatter->Print( aNestLevel, "(fp_curve pts(xy(%s) xy(%s) xy(%s) xy(%s))",
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
FMT_IU( aModuleDrawing->GetBezControl1() ).c_str(),
FMT_IU( aModuleDrawing->GetBezControl2() ).c_str(),
FMT_IU( aModuleDrawing->GetEnd0() ).c_str() );
break;
default:
wxFAIL_MSG( wxT( "Cannot format invalid DRAWSEGMENT type." ) );
};
aFormatter->Print( 0, " (layer %s)",
aFormatter->Quotew( aModuleDrawing->GetLayerName() ).c_str() );
if( aModuleDrawing->GetWidth() != 0 )
aFormatter->Print( 0, " (width %s)", FMT_IU( aModuleDrawing->GetWidth() ).c_str() );
if( aModuleDrawing->GetTimeStamp() )
aFormatter->Print( 0, " (tstamp %lX)", aModuleDrawing->GetTimeStamp() );
if( aModuleDrawing->GetStatus() )
aFormatter->Print( 0, " (status %X)", aModuleDrawing->GetStatus() );
aFormatter->Print( 0, ")\n" );
}
void PCB_IO::format( PCB_TARGET* aTarget, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
aFormatter->Print( aNestLevel, "(target %c (pos (xy %s)) (size %s)",
( aTarget->GetShape() ) ? 'x' : '+',
FMT_IU( aTarget->GetPosition() ).c_str(),
FMT_IU( aTarget->GetSize() ).c_str() );
if( aTarget->GetWidth() != 0 )
aFormatter->Print( aNestLevel, " (width %s)", FMT_IU( aTarget->GetWidth() ).c_str() );
aFormatter->Print( aNestLevel, " (layer %d)", aTarget->GetLayer() );
if( aTarget->GetTimeStamp() )
aFormatter->Print( aNestLevel, " (tstamp %lX)", aTarget->GetTimeStamp() );
aFormatter->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( MODULE* aModule, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
aFormatter->Print( aNestLevel, "(module %s", aFormatter->Quotew( aModule->m_LibRef ).c_str() );
if( aModule->IsLocked() )
aFormatter->Print( aNestLevel, " locked" );
if( aModule->IsPlaced() )
aFormatter->Print( aNestLevel, " placed" );
aFormatter->Print( 0, " (tedit %lX) (tstamp %lX)\n",
aModule->GetLastEditTime(), aModule->GetTimeStamp() );
aFormatter->Print( aNestLevel+1, "(at %s", FMT_IU( aModule->m_Pos ).c_str() );
if( aModule->m_Orient != 0.0 )
aFormatter->Print( 0, " %s", FMT_ANGLE( aModule->m_Orient ).c_str() );
aFormatter->Print( 0, ")\n" );
if( !aModule->m_Doc.IsEmpty() )
aFormatter->Print( aNestLevel+1, "(descr %s)\n",
aFormatter->Quotew( aModule->m_Doc ).c_str() );
if( !aModule->m_KeyWord.IsEmpty() )
aFormatter->Print( aNestLevel+1, "(tags %s)\n",
aFormatter->Quotew( aModule->m_KeyWord ).c_str() );
if( !aModule->m_Path.IsEmpty() )
aFormatter->Print( aNestLevel+1, "(path %s)\n",
aFormatter->Quotew( aModule->m_Path ).c_str() );
if( aModule->m_CntRot90 != 0 )
aFormatter->Print( aNestLevel+1, "(autoplace_cost90 %d)\n", aModule->m_CntRot90 );
if( aModule->m_CntRot180 != 0 )
aFormatter->Print( aNestLevel+1, "(autoplace_cost180 %d)\n", aModule->m_CntRot180 );
if( aModule->GetLocalSolderMaskMargin() != 0 )
aFormatter->Print( aNestLevel+1, "(solder_mask_margin %s)\n",
FMT_IU( aModule->GetLocalSolderMaskMargin() ).c_str() );
if( aModule->GetLocalSolderPasteMargin() != 0 )
aFormatter->Print( aNestLevel+1, "(solder_paste_margin %s)\n",
FMT_IU( aModule->GetLocalSolderPasteMargin() ).c_str() );
if( aModule->GetLocalSolderPasteMarginRatio() != 0 )
aFormatter->Print( aNestLevel+1, "(solder_paste_ratio %g)\n",
aModule->GetLocalSolderPasteMarginRatio() );
if( aModule->GetLocalClearance() != 0 )
aFormatter->Print( aNestLevel+1, "(clearance %s)\n",
FMT_IU( aModule->GetLocalClearance() ).c_str() );
if( aModule->m_ZoneConnection != UNDEFINED_CONNECTION )
aFormatter->Print( aNestLevel+1, "(zone_connect %d)\n", aModule->m_ZoneConnection );
if( aModule->m_ThermalWidth != 0 )
aFormatter->Print( aNestLevel+1, "(thermal_width %s)\n",
FMT_IU( aModule->m_ThermalWidth ).c_str() );
if( aModule->m_ThermalGap != 0 )
aFormatter->Print( aNestLevel+1, "(thermal_gap %s)\n",
FMT_IU( aModule->m_ThermalGap ).c_str() );
// Attributes
if( aModule->m_Attributs != MOD_DEFAULT )
{
aFormatter->Print( aNestLevel+1, "(attr " );
if( aModule->m_Attributs & MOD_CMS )
aFormatter->Print( 0, " smd" );
if( aModule->m_Attributs & MOD_VIRTUAL )
aFormatter->Print( 0, " virtual" );
aFormatter->Print( 0, ")\n" );
}
Format( (BOARD_ITEM*) aModule->m_Reference, aFormatter, aNestLevel+1, aControlBits );
Format( (BOARD_ITEM*) aModule->m_Value, aFormatter, aNestLevel+1, aControlBits );
// Save drawing elements.
for( BOARD_ITEM* gr = aModule->m_Drawings; gr; gr = gr->Next() )
Format( gr, aFormatter, aNestLevel+1, aControlBits );
// Save pads.
for( D_PAD* pad = aModule->m_Pads; pad; pad = pad->Next() )
Format( pad, aFormatter, aNestLevel+1, aControlBits );
// Save 3D info.
for( S3D_MASTER* t3D = aModule->m_3D_Drawings; t3D; t3D = t3D->Next() )
{
if( !t3D->m_Shape3DName.IsEmpty() )
{
aFormatter->Print( aNestLevel+1, "(3d_shape %s\n",
aFormatter->Quotew( t3D->m_Shape3DName ).c_str() );
aFormatter->Print( aNestLevel+2, "(at (xyz %.16g %.16g %.16g))\n",
t3D->m_MatPosition.x,
t3D->m_MatPosition.y,
t3D->m_MatPosition.z );
aFormatter->Print( aNestLevel+2, "(scale (xyz %.16g %.16g %.16g))\n",
t3D->m_MatScale.x,
t3D->m_MatScale.y,
t3D->m_MatScale.z );
aFormatter->Print( aNestLevel+2, "(rotate (xyz %.16g %.16g %.16g))\n",
t3D->m_MatRotation.x,
t3D->m_MatRotation.y,
t3D->m_MatRotation.z );
aFormatter->Print( aNestLevel+1, ")\n" );
}
}
aFormatter->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( D_PAD* aPad, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
std::string shape;
switch( aPad->GetShape() )
{
case PAD_CIRCLE: shape = "circle"; break;
case PAD_RECT: shape = "rectangle"; break;
case PAD_OVAL: shape = "oval"; break;
case PAD_TRAPEZOID: shape = "trapezoid"; break;
default:
THROW_IO_ERROR( wxString::Format( _( "unknown pad type: %d"), aPad->GetShape() ) );
}
std::string type;
switch( aPad->GetAttribute() )
{
case PAD_STANDARD: type = "thru_hole"; break;
case PAD_SMD: type = "smd"; break;
case PAD_CONN: type = "connect"; break;
case PAD_HOLE_NOT_PLATED: type = "np_thru_hole"; break;
default:
THROW_IO_ERROR( wxString::Format( _( "unknown pad attribute: %d" ),
aPad->GetAttribute() ) );
}
aFormatter->Print( aNestLevel, "(pad %s %s %s (size %s)\n",
aFormatter->Quotew( aPad->GetPadName() ).c_str(),
type.c_str(), shape.c_str(),
FMT_IU( aPad->GetSize() ).c_str() );
aFormatter->Print( aNestLevel+1, "(at %s", FMT_IU( aPad->GetPos0() ).c_str() );
if( aPad->GetOrientation() != 0.0 )
aFormatter->Print( 0, " %s", FMT_ANGLE( aPad->GetOrientation() ).c_str() );
aFormatter->Print( 0, ")\n" );
if( (aPad->GetDrillSize().GetWidth() > 0) || (aPad->GetDrillSize().GetHeight() > 0) )
{
std::string drill = (aPad->GetDrillSize().GetHeight() > 0) ?
FMT_IU( aPad->GetDrillSize() ).c_str() :
FMT_IU( aPad->GetDrillSize().GetWidth() ).c_str();
aFormatter->Print( aNestLevel+1, "(drill %s", drill.c_str() );
if( (aPad->GetOffset().x > 0) || (aPad->GetOffset().y > 0) )
{
std::string drillOffset = ( aPad->GetOffset().x > 0 ) ?
FMT_IU( aPad->GetOffset() ).c_str() :
FMT_IU( aPad->GetOffset().x ).c_str();
aFormatter->Print( 0, " (offset %s)", drillOffset.c_str() );
}
aFormatter->Print( 0, ")\n" );
}
aFormatter->Print( aNestLevel+1, "(layers %08X)\n", aPad->GetLayerMask() );
aFormatter->Print( aNestLevel+1, "(net %d %s)\n",
aPad->GetNet(), aFormatter->Quotew( aPad->GetNetname() ).c_str() );
if( aPad->GetDieLength() != 0 )
aFormatter->Print( aNestLevel+1, "(die_length %s)\n",
FMT_IU( aPad->GetDieLength() ).c_str() );
if( aPad->GetLocalSolderMaskMargin() != 0 )
aFormatter->Print( aNestLevel+1, "(solder_mask_margin %s)\n",
FMT_IU( aPad->GetLocalSolderMaskMargin() ).c_str() );
if( aPad->GetLocalSolderPasteMargin() != 0 )
aFormatter->Print( aNestLevel+1, "(solder_paste_margin %s)\n",
FMT_IU( aPad->GetLocalSolderPasteMargin() ).c_str() );
if( aPad->GetLocalSolderPasteMarginRatio() != 0 )
aFormatter->Print( aNestLevel+1, "(solder_paste_margin_ratio %g)\n",
aPad->GetLocalSolderPasteMarginRatio() );
if( aPad->GetLocalClearance() != 0 )
aFormatter->Print( aNestLevel+1, "(clearance %s)\n",
FMT_IU( aPad->GetLocalClearance() ).c_str() );
if( aPad->GetZoneConnection() != UNDEFINED_CONNECTION )
aFormatter->Print( aNestLevel+1, "(zone_connect %d)\n", aPad->GetZoneConnection() );
if( aPad->GetThermalWidth() != 0 )
aFormatter->Print( aNestLevel+1, "(thermal_width %s)\n",
FMT_IU( aPad->GetThermalWidth() ).c_str() );
if( aPad->GetThermalGap() != 0 )
aFormatter->Print( aNestLevel+1, "(thermal_gap %s)\n",
FMT_IU( aPad->GetThermalGap() ).c_str() );
aFormatter->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( TEXTE_PCB* aText, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
aFormatter->Print( aNestLevel, "(gr_text %s (at %s %s) (layer %s)",
aFormatter->Quotew( aText->GetText() ).c_str(),
FMT_IU( aText->GetPosition() ).c_str(),
FMT_ANGLE( aText->GetOrientation() ).c_str(),
aFormatter->Quotew( aText->GetLayerName() ).c_str() );
if( aText->GetTimeStamp() )
aFormatter->Print( 0, " (tstamp %lX)", aText->GetTimeStamp() );
aFormatter->Print( 0, "\n" );
aText->EDA_TEXT::Format( aFormatter, aNestLevel, aControlBits );
aFormatter->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( TEXTE_MODULE* aText, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
MODULE* parent = (MODULE*) aText->GetParent();
double orient = aText->GetOrientation();
wxString type;
switch( aText->GetType() )
{
case 0: type = wxT( "reference" ); break;
case 1: type = wxT( "value" ); break;
default: type = wxT( "user" );
}
// Due to the Pcbnew history, m_Orient is saved in screen value
// but it is handled as relative to its parent footprint
if( parent )
orient += parent->GetOrientation();
aFormatter->Print( aNestLevel, "(fp_text %s %s (at %s %s)%s\n",
aFormatter->Quotew( type ).c_str(),
aFormatter->Quotew( aText->GetText() ).c_str(),
FMT_IU( aText->GetPos0() ).c_str(), FMT_ANGLE( orient ).c_str(),
(!aText->IsVisible()) ? " hide" : "" );
aText->EDA_TEXT::Format( aFormatter, aNestLevel, aControlBits );
aFormatter->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( TRACK* aTrack, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
if( aTrack->Type() == PCB_VIA_T )
{
std::string type;
int layer1, layer2;
SEGVIA* via = (SEGVIA*) aTrack;
BOARD* board = (BOARD*) via->GetParent();
wxCHECK_RET( board != 0, wxT( "Via " ) + via->GetSelectMenuText() +
wxT( " has no parent." ) );
via->ReturnLayerPair( &layer1, &layer2 );
switch( aTrack->GetShape() )
{
case VIA_THROUGH: type = "thru"; break;
case VIA_BLIND_BURIED: type = "blind"; break;
case VIA_MICROVIA: type = "micro"; break;
default:
THROW_IO_ERROR( wxString::Format( _( "unknown via type %d" ), aTrack->GetShape() ) );
}
aFormatter->Print( aNestLevel, "(via %s (at %s) (size %s)", type.c_str(),
FMT_IU( aTrack->GetStart() ).c_str(),
FMT_IU( aTrack->GetWidth() ).c_str() );
if( aTrack->GetDrill() != UNDEFINED_DRILL_DIAMETER )
aFormatter->Print( 0, " (drill %s)", FMT_IU( aTrack->GetDrill() ).c_str() );
aFormatter->Print( 0, " (layers %s %s) (net %d)",
aFormatter->Quotew( board->GetLayerName( layer1 ) ).c_str(),
aFormatter->Quotew( board->GetLayerName( layer2 ) ).c_str(),
aTrack->GetNet() );
}
else
{
aFormatter->Print( aNestLevel, "(segment (start %s) (end %s) (width %s)",
FMT_IU( aTrack->GetStart() ).c_str(), FMT_IU( aTrack->GetEnd() ).c_str(),
FMT_IU( aTrack->GetWidth() ).c_str() );
aFormatter->Print( 0, " (layer %s) (net %d)",
aFormatter->Quotew( aTrack->GetLayerName() ).c_str(),
aTrack->GetNet() );
}
if( aTrack->GetTimeStamp() != 0 )
aFormatter->Print( 0, " (tstamp %lX)", aTrack->GetTimeStamp() );
if( aTrack->GetStatus() != 0 )
aFormatter->Print( 0, " (status %X)", aTrack->GetStatus() );
aFormatter->Print( 0, ")\n" );
}
void PCB_IO::format( ZONE_CONTAINER* aZone, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR )
{
aFormatter->Print( aNestLevel, "(zone (net %d %s) (layer %s) (tstamp %lX)\n",
aZone->GetNet(), aFormatter->Quotew( aZone->GetNetName() ).c_str(),
aFormatter->Quotew( aZone->GetLayerName() ).c_str(),
aZone->GetTimeStamp() );
// Save the outline aux info
std::string hatch;
switch( aZone->GetHatchStyle() )
{
default:
case CPolyLine::NO_HATCH: hatch = "none"; break;
case CPolyLine::DIAGONAL_EDGE: hatch = "edge"; break;
case CPolyLine::DIAGONAL_FULL: hatch = "full"; break;
}
aFormatter->Print( aNestLevel+1, "(hatch %s)\n", hatch.c_str() );
if( aZone->GetPriority() > 0 )
aFormatter->Print( aNestLevel+1, "(priority %d)\n", aZone->GetPriority() );
// Save pad option and clearance
std::string padoption;
switch( aZone->GetPadConnection() )
{
default:
case PAD_IN_ZONE: padoption = "yes"; break;
case THERMAL_PAD: padoption = "use_thermal"; break;
case PAD_NOT_IN_ZONE: padoption = "no"; break;
}
aFormatter->Print( aNestLevel+1, "(connect_pads %s (clearance %s))\n",
padoption.c_str(), FMT_IU( aZone->GetZoneClearance() ).c_str() );
aFormatter->Print( aNestLevel+1, "(min_thickness %s)\n",
FMT_IU( aZone->GetMinThickness() ).c_str() );
aFormatter->Print( aNestLevel+1,
"(fill %s (mode %s) (arc_segments %d) (thermal_gap %s) (thermal_bridge_width %s)\n",
(aZone->IsFilled()) ? "yes" : "no",
(aZone->GetFillMode()) ? "segment" : "polygon",
aZone->m_ArcToSegmentsCount,
FMT_IU( aZone->GetThermalReliefGap() ).c_str(),
FMT_IU( aZone->GetThermalReliefCopperBridge() ).c_str() );
std::string smoothing;
switch( aZone->GetCornerSmoothingType() )
{
case ZONE_SETTINGS::SMOOTHING_NONE: smoothing = "none"; break;
case ZONE_SETTINGS::SMOOTHING_CHAMFER: smoothing = "chamfer"; break;
case ZONE_SETTINGS::SMOOTHING_FILLET: smoothing = "fillet"; break;
default:
THROW_IO_ERROR( wxString::Format( _( "unknown zone corner smoothing type %d" ),
aZone->GetCornerSmoothingType() ) );
}
aFormatter->Print( aNestLevel+1, "(smoothing %s (radius %s))\n",
smoothing.c_str(), FMT_IU( aZone->GetCornerRadius() ).c_str() );
const std::vector< CPolyPt >& cv = aZone->m_Poly->corner;
if( cv.size() )
{
aFormatter->Print( aNestLevel+1, "(polygon\n");
aFormatter->Print( aNestLevel+2, "(pts\n" );
for( std::vector< CPolyPt >::const_iterator it = cv.begin(); it != cv.end(); ++it )
{
aFormatter->Print( aNestLevel+3, "(xy %s %s)\n",
FMT_IU( it->x ).c_str(), FMT_IU( it->y ).c_str() );
if( it->end_contour )
{
aFormatter->Print( aNestLevel+2, ")\n" );
if( it+1 != cv.end() )
{
aFormatter->Print( aNestLevel+1, ")\n" );
aFormatter->Print( aNestLevel+1, "(polygon\n" );
aFormatter->Print( aNestLevel+2, "(pts\n" );
}
}
}
aFormatter->Print( aNestLevel+1, ")\n" );
}
// Save the PolysList
const std::vector< CPolyPt >& fv = aZone->GetFilledPolysList();
if( fv.size() )
{
aFormatter->Print( aNestLevel+1, "(filled_polygon\n" );
aFormatter->Print( aNestLevel+2, "(pts\n" );
for( std::vector< CPolyPt >::const_iterator it = fv.begin(); it != fv.end(); ++it )
{
aFormatter->Print( aNestLevel+3, "(xy %s %s)\n",
FMT_IU( it->x ).c_str(), FMT_IU( it->y ).c_str() );
if( it->end_contour )
{
aFormatter->Print( aNestLevel+2, ")\n" );
if( it+1 != fv.end() )
{
aFormatter->Print( aNestLevel+1, ")\n" );
aFormatter->Print( aNestLevel+1, "(filled_polygon\n" );
aFormatter->Print( aNestLevel+2, "(pts\n" );
}
}
}
aFormatter->Print( aNestLevel+1, ")\n" );
}
// Save the filling segments list
const std::vector< SEGMENT >& segs = aZone->m_FillSegmList;
if( segs.size() )
{
aFormatter->Print( aNestLevel+1, "(fill_segments\n" );
for( std::vector< SEGMENT >::const_iterator it = segs.begin(); it != segs.end(); ++it )
{
aFormatter->Print( aNestLevel+2, "(pts (xy %s) (xy %s))\n",
FMT_IU( it->m_Start ).c_str(),
FMT_IU( it->m_End ).c_str() );
}
aFormatter->Print( aNestLevel+1, ")\n" );
}
aFormatter->Print( aNestLevel, ")\n" );
}
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 CERN
* Copyright (C) 1992-2011 KiCad Developers, see change_log.txt for contributors.
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <fctsys.h>
#include <kicad_string.h>
#include <common.h>
#include <build_version.h> // LEGACY_BOARD_FILE_VERSION
#include <macros.h>
#include <3d_struct.h>
#include <class_board.h>
#include <class_module.h>
#include <class_pcb_text.h>
#include <class_dimension.h>
#include <class_track.h>
#include <class_zone.h>
#include <class_drawsegment.h>
#include <class_mire.h>
#include <class_edge_mod.h>
#include <zones.h>
#include <kicad_plugin.h>
#include <pcb_parser.h>
#include <wx/wfstream.h>
#define FMTIU BOARD_ITEM::FormatInternalUnits
void PCB_IO::Save( const wxString& aFileName, BOARD* aBoard, PROPERTIES* aProperties )
{
LOCALE_IO toggle; // toggles on, then off, the C locale.
m_board = aBoard;
wxFileOutputStream fs( aFileName );
if( !fs.IsOk() )
{
m_error.Printf( _( "cannot open file '%s'" ), aFileName.GetData() );
THROW_IO_ERROR( m_error );
}
STREAM_OUTPUTFORMATTER formatter( fs );
m_out = &formatter; // no ownership
m_out->Print( 0, "(kicad_pcb (version %d) (host pcbnew %s)\n", SEXPR_BOARD_FILE_VERSION,
formatter.Quotew( GetBuildVersion() ).c_str() );
Format( aBoard, 1 );
m_out->Print( 0, ")\n" );
}
void PCB_IO::Format( BOARD_ITEM* aItem, int aNestLevel ) const
throw( IO_ERROR )
{
switch( aItem->Type() )
{
case PCB_T:
format( (BOARD*) aItem, aNestLevel );
break;
case PCB_DIMENSION_T:
format( ( DIMENSION*) aItem, aNestLevel );
break;
case PCB_LINE_T:
format( (DRAWSEGMENT*) aItem, aNestLevel );
break;
case PCB_MODULE_EDGE_T:
format( (EDGE_MODULE*) aItem, aNestLevel );
break;
case PCB_TARGET_T:
format( (PCB_TARGET*) aItem, aNestLevel );
break;
case PCB_MODULE_T:
format( (MODULE*) aItem, aNestLevel );
break;
case PCB_PAD_T:
format( (D_PAD*) aItem, aNestLevel );
break;
case PCB_TEXT_T:
format( (TEXTE_PCB*) aItem, aNestLevel );
break;
case PCB_MODULE_TEXT_T:
format( (TEXTE_MODULE*) aItem, aNestLevel );
break;
case PCB_TRACE_T:
case PCB_VIA_T:
format( (TRACK*) aItem, aNestLevel );
break;
case PCB_ZONE_AREA_T:
format( (ZONE_CONTAINER*) aItem, aNestLevel );
break;
default:
wxFAIL_MSG( wxT( "Cannot format item " ) + aItem->GetClass() );
}
}
void PCB_IO::formatLayer( const BOARD_ITEM* aItem ) const
{
#if USE_LAYER_NAMES
m_out->Print( 0, " (layer %s)", m_out->Quotew( aItem->GetLayerName() ).c_str() );
#else
m_out->Print( 0, " (layer %d)", aItem->GetLayer() );
#endif
}
void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const
throw( IO_ERROR )
{
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel, "(general\n" );
m_out->Print( aNestLevel+1, "(links %d)\n", aBoard->GetRatsnestsCount() );
m_out->Print( aNestLevel+1, "(no_connects %d)\n", aBoard->m_NbNoconnect );
// Write Bounding box info
m_out->Print( aNestLevel+1, "(area %s %s %s %s)\n",
FMTIU( aBoard->GetBoundingBox().GetX() ).c_str(),
FMTIU( aBoard->GetBoundingBox().GetY() ).c_str(),
FMTIU( aBoard->GetBoundingBox().GetRight() ).c_str(),
FMTIU( aBoard->GetBoundingBox().GetBottom() ).c_str() );
m_out->Print( aNestLevel+1, "(thickness %s)\n",
FMTIU( aBoard->GetDesignSettings().GetBoardThickness() ).c_str() );
m_out->Print( aNestLevel+1, "(drawings %d)\n", aBoard->m_Drawings.GetCount() );
m_out->Print( aNestLevel+1, "(tracks %d)\n", aBoard->GetNumSegmTrack() );
m_out->Print( aNestLevel+1, "(zones %d)\n", aBoard->GetNumSegmZone() );
m_out->Print( aNestLevel+1, "(modules %d)\n", aBoard->m_Modules.GetCount() );
m_out->Print( aNestLevel+1, "(nets %d)\n", aBoard->GetNetCount() );
m_out->Print( aNestLevel, ")\n\n" );
aBoard->GetPageSettings().Format( m_out, aNestLevel, m_ctl );
aBoard->GetTitleBlock().Format( m_out, aNestLevel, m_ctl );
// Layers.
m_out->Print( aNestLevel, "(layers\n" );
unsigned mask = LAYER_FRONT;
unsigned layer = LAYER_N_FRONT;
// Save only the used copper layers from front to back.
while( mask != 0 )
{
if( mask & aBoard->GetEnabledLayers() )
{
#if USE_LAYER_NAMES
m_out->Print( aNestLevel+1, "(%s %s",
m_out->Quotew( aBoard->GetLayerName( layer ) ).c_str(),
LAYER::ShowType( aBoard->GetLayerType( layer ) ) );
#else
m_out->Print( aNestLevel+1, "(%d %s %s", layer,
m_out->Quotew( aBoard->GetLayerName( layer ) ).c_str(),
LAYER::ShowType( aBoard->GetLayerType( layer ) ) );
#endif
if( !( aBoard->GetVisibleLayers() & mask ) )
m_out->Print( 0, "hide" );
m_out->Print( 0, ")\n" );
}
mask >>= 1;
layer--;
}
mask = ADHESIVE_LAYER_BACK;
layer = ADHESIVE_N_BACK;
// Save used non-copper layers in the order they are defined.
while( layer < LAYER_COUNT )
{
if( mask & aBoard->GetEnabledLayers() )
{
#if USE_LAYER_NAMES
m_out->Print( aNestLevel+1, "(%s user",
m_out->Quotew( aBoard->GetLayerName( layer ) ).c_str() );
#else
m_out->Print( aNestLevel+1, "(%d %s user", layer,
m_out->Quotew( aBoard->GetLayerName( layer ) ).c_str() );
#endif
if( !( aBoard->GetVisibleLayers() & mask ) )
m_out->Print( 0, "hide" );
m_out->Print( 0, ")\n" );
}
mask <<= 1;
layer++;
}
m_out->Print( aNestLevel, ")\n\n" );
// Setup
m_out->Print( aNestLevel, "(setup\n" );
// Save current default track width, for compatibility with older Pcbnew version;
m_out->Print( aNestLevel+1, "(last_trace_width %s)\n",
FMTIU( aBoard->GetCurrentTrackWidth() ).c_str() );
// Save custom tracks width list (the first is not saved here: this is the netclass value
for( unsigned ii = 1; ii < aBoard->m_TrackWidthList.size(); ii++ )
m_out->Print( aNestLevel+1, "(user_trace_width %s)\n",
FMTIU( aBoard->m_TrackWidthList[ii] ).c_str() );
m_out->Print( aNestLevel+1, "(trace_clearance %s)\n",
FMTIU( aBoard->m_NetClasses.GetDefault()->GetClearance() ).c_str() );
// ZONE_SETTINGS
m_out->Print( aNestLevel+1, "(zone_clearance %s)\n",
FMTIU( aBoard->GetZoneSettings().m_ZoneClearance ).c_str() );
m_out->Print( aNestLevel+1, "(zone_45_only %s)\n",
aBoard->GetZoneSettings().m_Zone_45_Only ? "yes" : "no" );
m_out->Print( aNestLevel+1, "(trace_min %s)\n",
FMTIU( aBoard->GetDesignSettings().m_TrackMinWidth ).c_str() );
m_out->Print( aNestLevel+1, "(segment_width %s)\n",
FMTIU( aBoard->GetDesignSettings().m_DrawSegmentWidth ).c_str() );
m_out->Print( aNestLevel+1, "(edge_width %s)\n",
FMTIU( aBoard->GetDesignSettings().m_EdgeSegmentWidth ).c_str() );
// Save current default via size, for compatibility with older Pcbnew version;
m_out->Print( aNestLevel+1, "(via_size %s)\n",
FMTIU( aBoard->m_NetClasses.GetDefault()->GetViaDiameter() ).c_str() );
m_out->Print( aNestLevel+1, "(via_drill %s)\n",
FMTIU( aBoard->m_NetClasses.GetDefault()->GetViaDrill() ).c_str() );
m_out->Print( aNestLevel+1, "(via_min_size %s)\n",
FMTIU( aBoard->GetDesignSettings().m_ViasMinSize ).c_str() );
m_out->Print( aNestLevel+1, "(via_min_drill %s)\n",
FMTIU( aBoard->GetDesignSettings().m_ViasMinDrill ).c_str() );
// Save custom vias diameters list (the first is not saved here: this is
// the netclass value
for( unsigned ii = 1; ii < aBoard->m_ViasDimensionsList.size(); ii++ )
m_out->Print( aNestLevel+1, "(user_via %s %s)\n",
FMTIU( aBoard->m_ViasDimensionsList[ii].m_Diameter ).c_str(),
FMTIU( aBoard->m_ViasDimensionsList[ii].m_Drill ).c_str() );
// for old versions compatibility:
m_out->Print( aNestLevel+1, "(uvia_size %s)\n",
FMTIU( aBoard->m_NetClasses.GetDefault()->GetuViaDiameter() ).c_str() );
m_out->Print( aNestLevel+1, "(uvia_drill %s)\n",
FMTIU( aBoard->m_NetClasses.GetDefault()->GetuViaDrill() ).c_str() );
m_out->Print( aNestLevel+1, "(uvias_allowed %s)\n",
( aBoard->GetDesignSettings().m_MicroViasAllowed ) ? "yes" : "no" );
m_out->Print( aNestLevel+1, "(uvia_min_size %s)\n",
FMTIU( aBoard->GetDesignSettings().m_MicroViasMinSize ).c_str() );
m_out->Print( aNestLevel+1, "(uvia_min_drill %s)\n",
FMTIU( aBoard->GetDesignSettings().m_MicroViasMinDrill ).c_str() );
m_out->Print( aNestLevel+1, "(pcb_text_width %s)\n",
FMTIU( aBoard->GetDesignSettings().m_PcbTextWidth ).c_str() );
m_out->Print( aNestLevel+1, "(pcb_text_size %s %s)\n",
FMTIU( aBoard->GetDesignSettings().m_PcbTextSize.x ).c_str(),
FMTIU( aBoard->GetDesignSettings().m_PcbTextSize.y ).c_str() );
m_out->Print( aNestLevel+1, "(mod_edge_width %s)\n",
FMTIU( aBoard->GetDesignSettings().m_ModuleSegmentWidth ).c_str() );
m_out->Print( aNestLevel+1, "(mod_text_size %s %s)\n",
FMTIU( aBoard->GetDesignSettings().m_ModuleTextSize.x ).c_str(),
FMTIU( aBoard->GetDesignSettings().m_ModuleTextSize.y ).c_str() );
m_out->Print( aNestLevel+1, "(mod_text_width %s)\n",
FMTIU( aBoard->GetDesignSettings().m_ModuleTextWidth ).c_str() );
m_out->Print( aNestLevel+1, "(pad_size %s %s)\n",
FMTIU( aBoard->GetDesignSettings().m_Pad_Master.GetSize().x ).c_str(),
FMTIU( aBoard->GetDesignSettings().m_Pad_Master.GetSize().y ).c_str() );
m_out->Print( aNestLevel+1, "(pad_drill %s)\n",
FMTIU( aBoard->GetDesignSettings().m_Pad_Master.GetDrillSize().x ).c_str() );
m_out->Print( aNestLevel+1, "(pad_to_mask_clearance %s)\n",
FMTIU( aBoard->GetDesignSettings().m_SolderMaskMargin ).c_str() );
if( aBoard->GetDesignSettings().m_SolderPasteMargin != 0 )
m_out->Print( aNestLevel+1, "(pad_to_paste_clearance %s)\n",
FMTIU( aBoard->GetDesignSettings().m_SolderPasteMargin ).c_str() );
if( aBoard->GetDesignSettings().m_SolderPasteMarginRatio != 0 )
m_out->Print( aNestLevel+1, "(pad_to_paste_clearance_ratio %g)\n",
aBoard->GetDesignSettings().m_SolderPasteMarginRatio );
m_out->Print( aNestLevel+1, "(aux_axis_origin %s %s)\n",
FMTIU( aBoard->GetOriginAxisPosition().x ).c_str(),
FMTIU( aBoard->GetOriginAxisPosition().y ).c_str() );
m_out->Print( aNestLevel+1, "(visible_elements %X)\n",
aBoard->GetDesignSettings().GetVisibleElements() );
m_out->Print( aNestLevel, ")\n\n" );
int netcount = aBoard->GetNetCount();
for( int i = 0; i < netcount; ++i )
m_out->Print( aNestLevel, "(net %d %s)\n",
aBoard->FindNet( i )->GetNet(),
m_out->Quotew( aBoard->FindNet( i )->GetNetname() ).c_str() );
m_out->Print( 0, "\n" );
// Save the default net class first.
aBoard->m_NetClasses.GetDefault()->Format( m_out, aNestLevel, m_ctl );
// Save the rest of the net classes alphabetically.
for( NETCLASSES::const_iterator it = aBoard->m_NetClasses.begin();
it != aBoard->m_NetClasses.end();
++it )
{
NETCLASS* netclass = it->second;
netclass->Format( m_out, aNestLevel, m_ctl );
}
// Save the graphical items on the board (not owned by a module)
for( BOARD_ITEM* item = aBoard->m_Drawings; item; item = item->Next() )
Format( item, aNestLevel );
m_out->Print( 0, "\n" );
// Save the modules.
for( MODULE* module = aBoard->m_Modules; module; module = (MODULE*) module->Next() )
{
Format( module, aNestLevel );
m_out->Print( 0, "\n" );
}
// Do not save MARKER_PCBs, they can be regenerated easily.
// Save the tracks and vias.
for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
Format( track, aNestLevel );
/// @todo Add warning here that the old segment filed zones are no longer supported and
/// will not be saved.
m_out->Print( 0, "\n" );
// Save the polygon (which are the newer technology) zones.
for( int i=0; i < aBoard->GetAreaCount(); ++i )
Format( aBoard->GetArea( i ), aNestLevel );
}
void PCB_IO::format( DIMENSION* aDimension, int aNestLevel ) const
throw( IO_ERROR )
{
m_out->Print( aNestLevel, "(dimension %s (width %s)",
FMT_IU( aDimension->m_Value ).c_str(),
FMT_IU( aDimension->m_Width ).c_str() );
formatLayer( aDimension );
if( aDimension->GetTimeStamp() )
m_out->Print( 0, " (tstamp %lX)", aDimension->GetTimeStamp() );
m_out->Print( 0, "\n" );
Format( (TEXTE_PCB*) &aDimension->m_Text, aNestLevel+1 );
m_out->Print( aNestLevel+1, "(feature1 (pts (xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_featureLineDOx ).c_str(),
FMT_IU( aDimension->m_featureLineDOy ).c_str(),
FMT_IU( aDimension->m_featureLineDFx ).c_str(),
FMT_IU( aDimension->m_featureLineDFy ).c_str() );
m_out->Print( aNestLevel+1, "(feature2 (pts (xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_featureLineGOx ).c_str(),
FMT_IU( aDimension->m_featureLineGOy ).c_str(),
FMT_IU( aDimension->m_featureLineGFx ).c_str(),
FMT_IU( aDimension->m_featureLineGFy ).c_str() );
m_out->Print( aNestLevel+1, "(crossbar (pts (xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_crossBarOx ).c_str(),
FMT_IU( aDimension->m_crossBarOy ).c_str(),
FMT_IU( aDimension->m_crossBarFx ).c_str(),
FMT_IU( aDimension->m_crossBarFy ).c_str() );
m_out->Print( aNestLevel+1, "(arrow1a (pts (xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_arrowD1Ox ).c_str(),
FMT_IU( aDimension->m_arrowD1Oy ).c_str(),
FMT_IU( aDimension->m_arrowD1Fx ).c_str(),
FMT_IU( aDimension->m_arrowD1Fy ).c_str() );
m_out->Print( aNestLevel+1, "(arrow1b (pts (xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_arrowD2Ox ).c_str(),
FMT_IU( aDimension->m_arrowD2Oy ).c_str(),
FMT_IU( aDimension->m_arrowD2Fx ).c_str(),
FMT_IU( aDimension->m_arrowD2Fy ).c_str() );
m_out->Print( aNestLevel+1, "(arrow2a (pts (xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_arrowG1Ox ).c_str(),
FMT_IU( aDimension->m_arrowG1Oy ).c_str(),
FMT_IU( aDimension->m_arrowG1Fx ).c_str(),
FMT_IU( aDimension->m_arrowG1Fy ).c_str() );
m_out->Print( aNestLevel+1, "(arrow2b (pts (xy %s %s) (xy %s %s)))\n",
FMT_IU( aDimension->m_arrowG2Ox ).c_str(),
FMT_IU( aDimension->m_arrowG2Oy ).c_str(),
FMT_IU( aDimension->m_arrowG2Fx ).c_str(),
FMT_IU( aDimension->m_arrowG2Fy ).c_str() );
m_out->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( DRAWSEGMENT* aSegment, int aNestLevel ) const
throw( IO_ERROR )
{
unsigned i;
switch( aSegment->GetShape() )
{
case S_SEGMENT: // Line
m_out->Print( aNestLevel, "(gr_line (pts (xy %s) (xy %s))",
FMT_IU( aSegment->GetStart() ).c_str(),
FMT_IU( aSegment->GetEnd() ).c_str() );
break;
case S_CIRCLE: // Circle
m_out->Print( aNestLevel, "(gr_circle (center (xy %s)) (end (xy %s))",
FMT_IU( aSegment->GetStart() ).c_str(),
FMT_IU( aSegment->GetEnd() ).c_str() );
break;
case S_ARC: // Arc
m_out->Print( aNestLevel, "(gr_arc (start (xy %s)) (end (xy %s)) (angle %s)",
FMT_IU( aSegment->GetStart() ).c_str(),
FMT_IU( aSegment->GetEnd() ).c_str(),
FMT_ANGLE( aSegment->GetAngle() ).c_str() );
break;
case S_POLYGON: // Polygon
m_out->Print( aNestLevel, "(gr_poly (pts" );
for( i = 0; i < aSegment->GetPolyPoints().size(); ++i )
m_out->Print( 0, " (xy %s)", FMT_IU( aSegment->GetPolyPoints()[i] ).c_str() );
m_out->Print( 0, ")" );
break;
case S_CURVE: // Bezier curve
m_out->Print( aNestLevel, "(gr_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
FMT_IU( aSegment->GetStart() ).c_str(),
FMT_IU( aSegment->GetBezControl1() ).c_str(),
FMT_IU( aSegment->GetBezControl2() ).c_str(),
FMT_IU( aSegment->GetEnd() ).c_str() );
break;
default:
wxFAIL_MSG( wxT( "Cannot format invalid DRAWSEGMENT type." ) );
};
formatLayer( aSegment );
if( aSegment->GetWidth() != 0 )
m_out->Print( 0, " (width %s)", FMT_IU( aSegment->GetWidth() ).c_str() );
if( aSegment->GetTimeStamp() )
m_out->Print( 0, " (tstamp %lX)", aSegment->GetTimeStamp() );
if( aSegment->GetStatus() )
m_out->Print( 0, " (status %X)", aSegment->GetStatus() );
m_out->Print( 0, ")\n" );
}
void PCB_IO::format( EDGE_MODULE* aModuleDrawing, int aNestLevel ) const
throw( IO_ERROR )
{
switch( aModuleDrawing->GetShape() )
{
case S_SEGMENT: // Line
m_out->Print( aNestLevel, "(fp_line (pts (xy %s) (xy %s))",
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
FMT_IU( aModuleDrawing->GetEnd0() ).c_str() );
break;
case S_CIRCLE: // Circle
m_out->Print( aNestLevel, "(fp_circle (center (xy %s)) (end (xy %s))",
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
FMT_IU( aModuleDrawing->GetEnd0() ).c_str() );
break;
case S_ARC: // Arc
m_out->Print( aNestLevel, "(fp_arc (start (xy %s)) (end (xy %s)) (angle %s)",
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
FMT_IU( aModuleDrawing->GetEnd0() ).c_str(),
FMT_ANGLE( aModuleDrawing->GetAngle() ).c_str() );
break;
case S_POLYGON: // Polygon
m_out->Print( aNestLevel, "(fp_poly (pts" );
for( unsigned i = 0; i < aModuleDrawing->GetPolyPoints().size(); ++i )
m_out->Print( 0, " (xy %s)",
FMT_IU( aModuleDrawing->GetPolyPoints()[i] ).c_str() );
m_out->Print( 0, ")\n" );
break;
case S_CURVE: // Bezier curve
m_out->Print( aNestLevel, "(fp_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
FMT_IU( aModuleDrawing->GetStart0() ).c_str(),
FMT_IU( aModuleDrawing->GetBezControl1() ).c_str(),
FMT_IU( aModuleDrawing->GetBezControl2() ).c_str(),
FMT_IU( aModuleDrawing->GetEnd0() ).c_str() );
break;
default:
wxFAIL_MSG( wxT( "Cannot format invalid DRAWSEGMENT type." ) );
};
formatLayer( aModuleDrawing );
if( aModuleDrawing->GetWidth() != 0 )
m_out->Print( 0, " (width %s)", FMT_IU( aModuleDrawing->GetWidth() ).c_str() );
if( aModuleDrawing->GetTimeStamp() )
m_out->Print( 0, " (tstamp %lX)", aModuleDrawing->GetTimeStamp() );
if( aModuleDrawing->GetStatus() )
m_out->Print( 0, " (status %X)", aModuleDrawing->GetStatus() );
m_out->Print( 0, ")\n" );
}
void PCB_IO::format( PCB_TARGET* aTarget, int aNestLevel ) const
throw( IO_ERROR )
{
m_out->Print( aNestLevel, "(target %s (at %s) (size %s)",
( aTarget->GetShape() ) ? "x" : "plus",
FMT_IU( aTarget->GetPosition() ).c_str(),
FMT_IU( aTarget->GetSize() ).c_str() );
if( aTarget->GetWidth() != 0 )
m_out->Print( aNestLevel, " (width %s)", FMT_IU( aTarget->GetWidth() ).c_str() );
formatLayer( aTarget );
if( aTarget->GetTimeStamp() )
m_out->Print( aNestLevel, " (tstamp %lX)", aTarget->GetTimeStamp() );
m_out->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( MODULE* aModule, int aNestLevel ) const
throw( IO_ERROR )
{
m_out->Print( aNestLevel, "(module %s", m_out->Quotew( aModule->m_LibRef ).c_str() );
if( aModule->IsLocked() )
m_out->Print( aNestLevel, " locked" );
if( aModule->IsPlaced() )
m_out->Print( aNestLevel, " placed" );
formatLayer( aModule );
m_out->Print( 0, " (tedit %lX) (tstamp %lX)\n",
aModule->GetLastEditTime(), aModule->GetTimeStamp() );
m_out->Print( aNestLevel+1, "(at %s", FMT_IU( aModule->m_Pos ).c_str() );
if( aModule->m_Orient != 0.0 )
m_out->Print( 0, " %s", FMT_ANGLE( aModule->m_Orient ).c_str() );
m_out->Print( 0, ")\n" );
if( !aModule->m_Doc.IsEmpty() )
m_out->Print( aNestLevel+1, "(descr %s)\n",
m_out->Quotew( aModule->m_Doc ).c_str() );
if( !aModule->m_KeyWord.IsEmpty() )
m_out->Print( aNestLevel+1, "(tags %s)\n",
m_out->Quotew( aModule->m_KeyWord ).c_str() );
if( !aModule->m_Path.IsEmpty() )
m_out->Print( aNestLevel+1, "(path %s)\n",
m_out->Quotew( aModule->m_Path ).c_str() );
if( aModule->m_CntRot90 != 0 )
m_out->Print( aNestLevel+1, "(autoplace_cost90 %d)\n", aModule->m_CntRot90 );
if( aModule->m_CntRot180 != 0 )
m_out->Print( aNestLevel+1, "(autoplace_cost180 %d)\n", aModule->m_CntRot180 );
if( aModule->GetLocalSolderMaskMargin() != 0 )
m_out->Print( aNestLevel+1, "(solder_mask_margin %s)\n",
FMT_IU( aModule->GetLocalSolderMaskMargin() ).c_str() );
if( aModule->GetLocalSolderPasteMargin() != 0 )
m_out->Print( aNestLevel+1, "(solder_paste_margin %s)\n",
FMT_IU( aModule->GetLocalSolderPasteMargin() ).c_str() );
if( aModule->GetLocalSolderPasteMarginRatio() != 0 )
m_out->Print( aNestLevel+1, "(solder_paste_ratio %g)\n",
aModule->GetLocalSolderPasteMarginRatio() );
if( aModule->GetLocalClearance() != 0 )
m_out->Print( aNestLevel+1, "(clearance %s)\n",
FMT_IU( aModule->GetLocalClearance() ).c_str() );
if( aModule->m_ZoneConnection != UNDEFINED_CONNECTION )
m_out->Print( aNestLevel+1, "(zone_connect %d)\n", aModule->m_ZoneConnection );
if( aModule->m_ThermalWidth != 0 )
m_out->Print( aNestLevel+1, "(thermal_width %s)\n",
FMT_IU( aModule->m_ThermalWidth ).c_str() );
if( aModule->m_ThermalGap != 0 )
m_out->Print( aNestLevel+1, "(thermal_gap %s)\n",
FMT_IU( aModule->m_ThermalGap ).c_str() );
// Attributes
if( aModule->m_Attributs != MOD_DEFAULT )
{
m_out->Print( aNestLevel+1, "(attr" );
if( aModule->m_Attributs & MOD_CMS )
m_out->Print( 0, " smd" );
if( aModule->m_Attributs & MOD_VIRTUAL )
m_out->Print( 0, " virtual" );
m_out->Print( 0, ")\n" );
}
Format( (BOARD_ITEM*) aModule->m_Reference, aNestLevel+1 );
Format( (BOARD_ITEM*) aModule->m_Value, aNestLevel+1 );
// Save drawing elements.
for( BOARD_ITEM* gr = aModule->m_Drawings; gr; gr = gr->Next() )
Format( gr, aNestLevel+1 );
// Save pads.
for( D_PAD* pad = aModule->m_Pads; pad; pad = pad->Next() )
Format( pad, aNestLevel+1 );
// Save 3D info.
for( S3D_MASTER* t3D = aModule->m_3D_Drawings; t3D; t3D = t3D->Next() )
{
if( !t3D->m_Shape3DName.IsEmpty() )
{
m_out->Print( aNestLevel+1, "(model %s\n",
m_out->Quotew( t3D->m_Shape3DName ).c_str() );
m_out->Print( aNestLevel+2, "(at (xyz %.16g %.16g %.16g))\n",
t3D->m_MatPosition.x,
t3D->m_MatPosition.y,
t3D->m_MatPosition.z );
m_out->Print( aNestLevel+2, "(scale (xyz %.16g %.16g %.16g))\n",
t3D->m_MatScale.x,
t3D->m_MatScale.y,
t3D->m_MatScale.z );
m_out->Print( aNestLevel+2, "(rotate (xyz %.16g %.16g %.16g))\n",
t3D->m_MatRotation.x,
t3D->m_MatRotation.y,
t3D->m_MatRotation.z );
m_out->Print( aNestLevel+1, ")\n" );
}
}
m_out->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( D_PAD* aPad, int aNestLevel ) const
throw( IO_ERROR )
{
std::string shape;
switch( aPad->GetShape() )
{
case PAD_CIRCLE: shape = "circle"; break;
case PAD_RECT: shape = "rectangle"; break;
case PAD_OVAL: shape = "oval"; break;
case PAD_TRAPEZOID: shape = "trapezoid"; break;
default:
THROW_IO_ERROR( wxString::Format( _( "unknown pad type: %d"), aPad->GetShape() ) );
}
std::string type;
switch( aPad->GetAttribute() )
{
case PAD_STANDARD: type = "thru_hole"; break;
case PAD_SMD: type = "smd"; break;
case PAD_CONN: type = "connect"; break;
case PAD_HOLE_NOT_PLATED: type = "np_thru_hole"; break;
default:
THROW_IO_ERROR( wxString::Format( _( "unknown pad attribute: %d" ),
aPad->GetAttribute() ) );
}
m_out->Print( aNestLevel, "(pad %s %s %s (size %s)\n",
m_out->Quotew( aPad->GetPadName() ).c_str(),
type.c_str(), shape.c_str(),
FMT_IU( aPad->GetSize() ).c_str() );
m_out->Print( aNestLevel+1, "(at %s", FMT_IU( aPad->GetPos0() ).c_str() );
if( aPad->GetOrientation() != 0.0 )
m_out->Print( 0, " %s", FMT_ANGLE( aPad->GetOrientation() ).c_str() );
m_out->Print( 0, ")\n" );
if( (aPad->GetDrillSize().GetWidth() > 0) || (aPad->GetDrillSize().GetHeight() > 0) )
{
std::string drill = (aPad->GetDrillSize().GetHeight() > 0) ?
FMT_IU( aPad->GetDrillSize() ).c_str() :
FMT_IU( aPad->GetDrillSize().GetWidth() ).c_str();
m_out->Print( aNestLevel+1, "(drill %s", drill.c_str() );
if( (aPad->GetOffset().x > 0) || (aPad->GetOffset().y > 0) )
{
std::string drillOffset = ( aPad->GetOffset().x > 0 ) ?
FMT_IU( aPad->GetOffset() ).c_str() :
FMT_IU( aPad->GetOffset().x ).c_str();
m_out->Print( 0, " (offset %s)", drillOffset.c_str() );
}
m_out->Print( 0, ")\n" );
}
m_out->Print( aNestLevel+1, "(layers" );
unsigned layerMask = aPad->GetLayerMask() & m_board->GetEnabledLayers();
for( int layer = 0; layerMask; ++layer, layerMask >>= 1 )
{
if( layerMask & 1 )
{
#if USE_LAYER_NAMES
m_out->Print( 0, " %s", m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
#else
m_out->Print( 0, " %d", layer );
#endif
}
}
m_out->Print( 0, ")\n" );
m_out->Print( aNestLevel+1, "(net %d %s)\n",
aPad->GetNet(), m_out->Quotew( aPad->GetNetname() ).c_str() );
if( aPad->GetDieLength() != 0 )
m_out->Print( aNestLevel+1, "(die_length %s)\n",
FMT_IU( aPad->GetDieLength() ).c_str() );
if( aPad->GetLocalSolderMaskMargin() != 0 )
m_out->Print( aNestLevel+1, "(solder_mask_margin %s)\n",
FMT_IU( aPad->GetLocalSolderMaskMargin() ).c_str() );
if( aPad->GetLocalSolderPasteMargin() != 0 )
m_out->Print( aNestLevel+1, "(solder_paste_margin %s)\n",
FMT_IU( aPad->GetLocalSolderPasteMargin() ).c_str() );
if( aPad->GetLocalSolderPasteMarginRatio() != 0 )
m_out->Print( aNestLevel+1, "(solder_paste_margin_ratio %g)\n",
aPad->GetLocalSolderPasteMarginRatio() );
if( aPad->GetLocalClearance() != 0 )
m_out->Print( aNestLevel+1, "(clearance %s)\n",
FMT_IU( aPad->GetLocalClearance() ).c_str() );
if( aPad->GetZoneConnection() != UNDEFINED_CONNECTION )
m_out->Print( aNestLevel+1, "(zone_connect %d)\n", aPad->GetZoneConnection() );
if( aPad->GetThermalWidth() != 0 )
m_out->Print( aNestLevel+1, "(thermal_width %s)\n",
FMT_IU( aPad->GetThermalWidth() ).c_str() );
if( aPad->GetThermalGap() != 0 )
m_out->Print( aNestLevel+1, "(thermal_gap %s)\n",
FMT_IU( aPad->GetThermalGap() ).c_str() );
m_out->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( TEXTE_PCB* aText, int aNestLevel ) const
throw( IO_ERROR )
{
m_out->Print( aNestLevel, "(gr_text %s (at %s %s)",
m_out->Quotew( aText->GetText() ).c_str(),
FMT_IU( aText->GetPosition() ).c_str(),
FMT_ANGLE( aText->GetOrientation() ).c_str() );
formatLayer( aText );
if( aText->GetTimeStamp() )
m_out->Print( 0, " (tstamp %lX)", aText->GetTimeStamp() );
m_out->Print( 0, "\n" );
aText->EDA_TEXT::Format( m_out, aNestLevel, m_ctl );
m_out->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( TEXTE_MODULE* aText, int aNestLevel ) const
throw( IO_ERROR )
{
MODULE* parent = (MODULE*) aText->GetParent();
double orient = aText->GetOrientation();
wxString type;
switch( aText->GetType() )
{
case 0: type = wxT( "reference" ); break;
case 1: type = wxT( "value" ); break;
default: type = wxT( "user" );
}
// Due to the Pcbnew history, m_Orient is saved in screen value
// but it is handled as relative to its parent footprint
if( parent )
orient += parent->GetOrientation();
m_out->Print( aNestLevel, "(fp_text %s %s (at %s %s)%s\n",
m_out->Quotew( type ).c_str(),
m_out->Quotew( aText->GetText() ).c_str(),
FMT_IU( aText->GetPos0() ).c_str(), FMT_ANGLE( orient ).c_str(),
(!aText->IsVisible()) ? " hide" : "" );
aText->EDA_TEXT::Format( m_out, aNestLevel, m_ctl );
m_out->Print( aNestLevel, ")\n" );
}
void PCB_IO::format( TRACK* aTrack, int aNestLevel ) const
throw( IO_ERROR )
{
if( aTrack->Type() == PCB_VIA_T )
{
std::string type;
int layer1, layer2;
SEGVIA* via = (SEGVIA*) aTrack;
BOARD* board = (BOARD*) via->GetParent();
wxCHECK_RET( board != 0, wxT( "Via " ) + via->GetSelectMenuText() +
wxT( " has no parent." ) );
via->ReturnLayerPair( &layer1, &layer2 );
switch( aTrack->GetShape() )
{
case VIA_THROUGH: type = "thru"; break;
case VIA_BLIND_BURIED: type = "blind"; break;
case VIA_MICROVIA: type = "micro"; break;
default:
THROW_IO_ERROR( wxString::Format( _( "unknown via type %d" ), aTrack->GetShape() ) );
}
m_out->Print( aNestLevel, "(via %s (at %s) (size %s)", type.c_str(),
FMT_IU( aTrack->GetStart() ).c_str(),
FMT_IU( aTrack->GetWidth() ).c_str() );
if( aTrack->GetDrill() != UNDEFINED_DRILL_DIAMETER )
m_out->Print( 0, " (drill %s)", FMT_IU( aTrack->GetDrill() ).c_str() );
#if USE_LAYER_NAMES
m_out->Print( 0, " (layers %s %s)",
m_out->Quotew( m_board->GetLayerName( layer1 ) ).c_str(),
m_out->Quotew( m_board->GetLayerName( layer2 ) ).c_str() );
#else
m_out->Print( 0, " (layers %d %d)", layer1, layer2 );
#endif
}
else
{
m_out->Print( aNestLevel, "(segment (start %s) (end %s) (width %s)",
FMT_IU( aTrack->GetStart() ).c_str(), FMT_IU( aTrack->GetEnd() ).c_str(),
FMT_IU( aTrack->GetWidth() ).c_str() );
#if USE_LAYER_NAMES
m_out->Print( 0, " (layer %s)", m_out->Quotew( aTrack->GetLayerName() ).c_str() );
#else
m_out->Print( 0, " (layer %d)", aTrack->GetLayer() );
#endif
}
m_out->Print( 0, " (net %d)", aTrack->GetNet() );
if( aTrack->GetTimeStamp() != 0 )
m_out->Print( 0, " (tstamp %lX)", aTrack->GetTimeStamp() );
if( aTrack->GetStatus() != 0 )
m_out->Print( 0, " (status %X)", aTrack->GetStatus() );
m_out->Print( 0, ")\n" );
}
void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
throw( IO_ERROR )
{
m_out->Print( aNestLevel, "(zone (net %d) (net_name %s)",
aZone->GetNet(), m_out->Quotew( aZone->GetNetName() ).c_str() );
formatLayer( aZone );
m_out->Print( 0, " (tstamp %lX)", aZone->GetTimeStamp() );
// Save the outline aux info
std::string hatch;
switch( aZone->GetHatchStyle() )
{
default:
case CPolyLine::NO_HATCH: hatch = "none"; break;
case CPolyLine::DIAGONAL_EDGE: hatch = "edge"; break;
case CPolyLine::DIAGONAL_FULL: hatch = "full"; break;
}
m_out->Print( 0, " (hatch %s %s)\n", hatch.c_str(),
FMT_IU( aZone->m_Poly->GetHatchPitch() ).c_str() );
if( aZone->GetPriority() > 0 )
m_out->Print( aNestLevel+1, " (priority %d)\n", aZone->GetPriority() );
// Save pad option and clearance
std::string padoption;
switch( aZone->GetPadConnection() )
{
default:
case PAD_IN_ZONE: padoption = "yes"; break;
case THERMAL_PAD: padoption = "use_thermal"; break;
case PAD_NOT_IN_ZONE: padoption = "no"; break;
}
m_out->Print( aNestLevel+1, "(connect_pads %s (clearance %s))\n",
padoption.c_str(), FMT_IU( aZone->GetZoneClearance() ).c_str() );
m_out->Print( aNestLevel+1, "(min_thickness %s)\n",
FMT_IU( aZone->GetMinThickness() ).c_str() );
m_out->Print( aNestLevel+1,
"(fill %s (mode %s) (arc_segments %d) (thermal_gap %s) (thermal_bridge_width %s)\n",
(aZone->IsFilled()) ? "yes" : "no",
(aZone->GetFillMode()) ? "segment" : "polygon",
aZone->GetArcSegCount(),
FMT_IU( aZone->GetThermalReliefGap() ).c_str(),
FMT_IU( aZone->GetThermalReliefCopperBridge() ).c_str() );
std::string smoothing;
switch( aZone->GetCornerSmoothingType() )
{
case ZONE_SETTINGS::SMOOTHING_NONE: smoothing = "none"; break;
case ZONE_SETTINGS::SMOOTHING_CHAMFER: smoothing = "chamfer"; break;
case ZONE_SETTINGS::SMOOTHING_FILLET: smoothing = "fillet"; break;
default:
THROW_IO_ERROR( wxString::Format( _( "unknown zone corner smoothing type %d" ),
aZone->GetCornerSmoothingType() ) );
}
m_out->Print( aNestLevel+1, "(smoothing %s) (radius %s))\n",
smoothing.c_str(), FMT_IU( aZone->GetCornerRadius() ).c_str() );
const std::vector< CPolyPt >& cv = aZone->m_Poly->corner;
if( cv.size() )
{
m_out->Print( aNestLevel+1, "(polygon\n");
m_out->Print( aNestLevel+2, "(pts\n" );
for( std::vector< CPolyPt >::const_iterator it = cv.begin(); it != cv.end(); ++it )
{
m_out->Print( aNestLevel+3, "(xy %s %s)\n",
FMT_IU( it->x ).c_str(), FMT_IU( it->y ).c_str() );
if( it->end_contour )
{
m_out->Print( aNestLevel+2, ")\n" );
if( it+1 != cv.end() )
{
m_out->Print( aNestLevel+1, ")\n" );
m_out->Print( aNestLevel+1, "(polygon\n" );
m_out->Print( aNestLevel+2, "(pts\n" );
}
}
}
m_out->Print( aNestLevel+1, ")\n" );
}
// Save the PolysList
const std::vector< CPolyPt >& fv = aZone->GetFilledPolysList();
if( fv.size() )
{
m_out->Print( aNestLevel+1, "(filled_polygon\n" );
m_out->Print( aNestLevel+2, "(pts\n" );
for( std::vector< CPolyPt >::const_iterator it = fv.begin(); it != fv.end(); ++it )
{
m_out->Print( aNestLevel+3, "(xy %s %s)\n",
FMT_IU( it->x ).c_str(), FMT_IU( it->y ).c_str() );
if( it->end_contour )
{
m_out->Print( aNestLevel+2, ")\n" );
if( it+1 != fv.end() )
{
m_out->Print( aNestLevel+1, ")\n" );
m_out->Print( aNestLevel+1, "(filled_polygon\n" );
m_out->Print( aNestLevel+2, "(pts\n" );
}
}
}
m_out->Print( aNestLevel+1, ")\n" );
}
// Save the filling segments list
const std::vector< SEGMENT >& segs = aZone->m_FillSegmList;
if( segs.size() )
{
m_out->Print( aNestLevel+1, "(fill_segments\n" );
for( std::vector< SEGMENT >::const_iterator it = segs.begin(); it != segs.end(); ++it )
{
m_out->Print( aNestLevel+2, "(pts (xy %s) (xy %s))\n",
FMT_IU( it->m_Start ).c_str(),
FMT_IU( it->m_End ).c_str() );
}
m_out->Print( aNestLevel+1, ")\n" );
}
m_out->Print( aNestLevel, ")\n" );
}
PCB_IO::PCB_IO()
{
m_out = &m_sf;
}
BOARD* PCB_IO::Load( const wxString& aFileName, BOARD* aAppendToMe, PROPERTIES* aProperties )
{
wxFFile file( aFileName, "r" );
if( !file.IsOpened() )
{
THROW_IO_ERROR( _( "Unable to read file \"" ) + GetChars( aFileName ) + wxT( "\"" ) );
}
PCB_PARSER parser( new FILE_LINE_READER( file.fp(), aFileName ), aAppendToMe );
return (BOARD*) parser.Parse();
}
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) CERN.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef KICAD_PLUGIN_H_
#define KICAD_PLUGIN_H_
#include <io_mgr.h>
#include <string>
class BOARD;
class BOARD_ITEM;
/** Current s-expression file format version. 2 was the last legacy format version. */
#define SEXPR_BOARD_FILE_VERSION 3
/** Format output for the clipboard instead of a file. */
#define CTL_CLIPBOARD (1 << 0)
/**
* Class PCB_IO
* is a PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
*
* @note This class is not thread safe, but it is re-entrant multiple times in sequence.
*/
class PCB_IO : public PLUGIN
{
public:
const wxString& PluginName() const
{
static const wxString name = wxT( "KiCad" );
return name;
}
const wxString& GetFileExtension() const
{
static const wxString extension = wxT( "kicad_pcb" );
return extension;
}
void Save( const wxString& aFileName, BOARD* aBoard,
PROPERTIES* aProperties = NULL ); // overload
/**
* Function Format
* outputs \a aItem to \a aFormatter in s-expression format.
*
* @param aItem A pointer the an #BOARD_ITEM object to format.
* @param aFormatter The #OUTPUTFORMATTER object to write to.
* @param aNestLevel The indentation nest level.
* @param aControlBits The control bit definition for object specific formatting.
* @throw IO_ERROR on write error.
*/
void Format( BOARD_ITEM* aItem, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
protected:
wxString m_error; ///< for throwing exceptions
BOARD* m_board; ///< which BOARD, no ownership here
PROPERTIES* m_props; ///< passed via Save() or Load(), no ownership, may be NULL.
LINE_READER* m_reader; ///< no ownership here.
wxString m_filename; ///< for saves only, name is in m_reader for loads
int m_loading_format_version; ///< which BOARD_FORMAT_VERSION am I Load()ing?
private:
void format( BOARD* aBoard, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
void format( DIMENSION* aDimension, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
void format( EDGE_MODULE* aModuleDrawing, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
void format( DRAWSEGMENT* aSegment, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
void format( PCB_TARGET* aTarget, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
void format( MODULE* aModule, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
void format( D_PAD* aPad, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
void format( TEXTE_PCB* aText, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
void format( TEXTE_MODULE* aText, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
void format( TRACK* aTrack, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
void format( ZONE_CONTAINER* aZone, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const
throw( IO_ERROR );
};
#endif // KICAD_PLUGIN_H_
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 CERN.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef KICAD_PLUGIN_H_
#define KICAD_PLUGIN_H_
#include <io_mgr.h>
#include <string>
class BOARD;
class BOARD_ITEM;
/** Current s-expression file format version. 2 was the last legacy format version. */
#define SEXPR_BOARD_FILE_VERSION 3
/** Format output for the clipboard instead of a file. */
#define CTL_CLIPBOARD (1 << 0)
/**
* Class PCB_IO
* is a PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
*
* @note This class is not thread safe, but it is re-entrant multiple times in sequence.
*/
class PCB_IO : public PLUGIN
{
public:
//-----<PLUGIN API>---------------------------------------------------------
const wxString& PluginName() const
{
static const wxString name = wxT( "KiCad" );
return name;
}
const wxString& GetFileExtension() const
{
static const wxString extension = wxT( "kicad_pcb" );
return extension;
}
void Save( const wxString& aFileName, BOARD* aBoard,
PROPERTIES* aProperties = NULL ); // overload
BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, PROPERTIES* aProperties = NULL );
//-----</PLUGIN API>--------------------------------------------------------
PCB_IO();
/**
* Function Format
* outputs \a aItem to \a aFormatter in s-expression format.
*
* @param aItem A pointer the an #BOARD_ITEM object to format.
* @param aNestLevel The indentation nest level.
* @throw IO_ERROR on write error.
*/
void Format( BOARD_ITEM* aItem, int aNestLevel = 0 ) const
throw( IO_ERROR );
std::string GetStringOutput( bool doClear )
{
std::string ret = m_sf.GetString();
if( doClear )
m_sf.Clear();
return ret;
}
protected:
wxString m_error; ///< for throwing exceptions
BOARD* m_board; ///< which BOARD, no ownership here
PROPERTIES* m_props; ///< passed via Save() or Load(), no ownership, may be NULL.
LINE_READER* m_reader; ///< no ownership here.
wxString m_filename; ///< for saves only, name is in m_reader for loads
int m_loading_format_version; ///< which BOARD_FORMAT_VERSION am I Load()ing?
STRING_FORMATTER m_sf;
OUTPUTFORMATTER* m_out; ///< output any Format()s to this, no ownership
int m_ctl;
private:
void format( BOARD* aBoard, int aNestLevel = 0 ) const
throw( IO_ERROR );
void format( DIMENSION* aDimension, int aNestLevel = 0 ) const
throw( IO_ERROR );
void format( EDGE_MODULE* aModuleDrawing, int aNestLevel = 0 ) const
throw( IO_ERROR );
void format( DRAWSEGMENT* aSegment, int aNestLevel = 0 ) const
throw( IO_ERROR );
void format( PCB_TARGET* aTarget, int aNestLevel = 0 ) const
throw( IO_ERROR );
void format( MODULE* aModule, int aNestLevel = 0 ) const
throw( IO_ERROR );
void format( D_PAD* aPad, int aNestLevel = 0 ) const
throw( IO_ERROR );
void format( TEXTE_PCB* aText, int aNestLevel = 0 ) const
throw( IO_ERROR );
void format( TEXTE_MODULE* aText, int aNestLevel = 0 ) const
throw( IO_ERROR );
void format( TRACK* aTrack, int aNestLevel = 0 ) const
throw( IO_ERROR );
void format( ZONE_CONTAINER* aZone, int aNestLevel = 0 ) const
throw( IO_ERROR );
void formatLayer( const BOARD_ITEM* aItem ) const;
};
#endif // KICAD_PLUGIN_H_
......@@ -403,7 +403,7 @@ void LEGACY_PLUGIN::loadGENERAL()
else if( TESTLINE( "BoardThickness" ) )
{
BIU thickn = biuParse( line + SZ( "BoardThickness" ) );
m_board->GetDesignSettings().m_BoardThickness = thickn;
m_board->GetDesignSettings().SetBoardThickness( thickn );
}
/*
......@@ -2215,7 +2215,7 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
arcsegcount = 32;
zc->SetArcSegCount( arcsegcount );
zc->SetIsFilled( fillstate == 'S' ? true : false );
zc->SetIsFilled( fillstate == 'F' ? true : false );
zc->SetThermalReliefGap( thermalReliefGap );
zc->SetThermalReliefCopperBridge( thermalReliefCopperBridge );
}
......@@ -2825,7 +2825,7 @@ void LEGACY_PLUGIN::saveGENERAL( const BOARD* aBoard ) const
fprintf( m_fp, "Ndraw %d\n", aBoard->m_Drawings.GetCount() );
fprintf( m_fp, "Ntrack %d\n", aBoard->GetNumSegmTrack() );
fprintf( m_fp, "Nzone %d\n", aBoard->GetNumSegmZone() );
fprintf( m_fp, "BoardThickness %s\n", fmtBIU( aBoard->GetDesignSettings().m_BoardThickness ).c_str() );
fprintf( m_fp, "BoardThickness %s\n", fmtBIU( aBoard->GetDesignSettings().GetBoardThickness() ).c_str() );
fprintf( m_fp, "Nmodule %d\n", aBoard->m_Modules.GetCount() );
fprintf( m_fp, "Nnets %d\n", aBoard->GetNetCount() );
fprintf( m_fp, "$EndGENERAL\n\n" );
......
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 CERN
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file pcb_parser.cpp
* @brief Pcbnew s-expression file format parser implementation.
*/
#include <common.h>
#include <macros.h>
#include <convert_from_iu.h>
#include <trigo.h>
#include <3d_struct.h>
#include <class_title_block.h>
#include <class_board.h>
#include <class_dimension.h>
#include <class_drawsegment.h>
#include <class_edge_mod.h>
#include <class_mire.h>
#include <class_module.h>
#include <class_netclass.h>
#include <class_pad.h>
#include <class_track.h>
#include <class_zone.h>
#include <zones.h>
#include <pcb_parser.h>
double PCB_PARSER::parseDouble() throw( IO_ERROR )
{
char* tmp;
errno = 0;
double fval = strtod( CurText(), &tmp );
if( errno )
{
wxString error;
error.Printf( _( "invalid floating point number in\nfile: '%s'\nline: %d\noffset: %d" ),
GetChars( CurSource() ), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
if( CurText() == tmp )
{
wxString error;
error.Printf( _( "missing floating point number in\nfile: '%s'\nline: %d\noffset: %d" ),
GetChars( CurSource() ), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
return fval;
}
bool PCB_PARSER::parseBool() throw( PARSE_ERROR )
{
T token = NextTok();
if( token == T_yes )
return true;
else if( token == T_no )
return false;
else
Expecting( "yes or no" );
return false;
}
wxPoint PCB_PARSER::parseXY() throw( PARSE_ERROR )
{
if( CurTok() != T_LEFT )
NeedLEFT();
wxPoint pt;
T token = NextTok();
if( token != T_xy )
Expecting( T_xy );
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
NeedRIGHT();
return pt;
}
void PCB_PARSER::parseXY( int* aX, int* aY ) throw( PARSE_ERROR )
{
wxPoint pt = parseXY();
if( aX )
*aX = pt.x;
if( aY )
*aY = pt.y;
}
void PCB_PARSER::parseEDA_TEXT( EDA_TEXT* aText ) throw( PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_effects,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as EDA_TEXT." ) );
T token;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_font:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_LEFT )
continue;
switch( token )
{
case T_size:
{
wxSize sz;
sz.SetHeight( parseBoardUnits( "text height" ) );
sz.SetWidth( parseBoardUnits( "text width" ) );
aText->SetSize( sz );
NeedRIGHT();
break;
}
case T_thickness:
aText->SetThickness( parseBoardUnits( "text thickness" ) );
NeedRIGHT();
break;
case T_bold:
aText->SetBold( true );
break;
case T_italic:
aText->SetItalic( true );
break;
default:
Expecting( "size, bold, or italic" );
}
}
break;
case T_justify:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_LEFT )
continue;
switch( token )
{
case T_left:
aText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
break;
case T_right:
aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
break;
case T_top:
aText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
break;
case T_bottom:
aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
break;
case T_mirror:
aText->SetMirrored( true );
break;
default:
Expecting( "left, right, top, bottom, or mirror" );
}
}
break;
case T_hide:
aText->SetVisible( false );
break;
default:
Expecting( "font, justify, or hide" );
}
}
}
S3D_MASTER* PCB_PARSER::parse3DModel() throw( PARSE_ERROR )
{
wxCHECK_MSG( CurTok() == T_model, NULL,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as S3D_MASTER." ) );
T token;
auto_ptr< S3D_MASTER > n3D( new S3D_MASTER( NULL ) );
NeedSYMBOL();
n3D->m_Shape3DName = FromUTF8();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_at:
NeedLEFT();
token = NextTok();
if( token != T_xyz )
Expecting( T_xyz );
n3D->m_MatPosition.x = parseDouble( "x value" );
n3D->m_MatPosition.y = parseDouble( "y value" );
n3D->m_MatPosition.z = parseDouble( "z value" );
NeedRIGHT();
break;
case T_scale:
NeedLEFT();
token = NextTok();
if( token != T_xyz )
Expecting( T_xyz );
n3D->m_MatScale.x = parseDouble( "x value" );
n3D->m_MatScale.y = parseDouble( "y value" );
n3D->m_MatScale.z = parseDouble( "z value" );
NeedRIGHT();
break;
case T_rotate:
NeedLEFT();
token = NextTok();
if( token != T_xyz )
Expecting( T_xyz );
n3D->m_MatRotation.x = parseDouble( "x value" );
n3D->m_MatRotation.y = parseDouble( "y value" );
n3D->m_MatRotation.z = parseDouble( "z value" );
NeedRIGHT();
break;
default:
Expecting( "at, scale, or rotate" );
}
NeedRIGHT();
}
return n3D.release();
}
BOARD_ITEM* PCB_PARSER::Parse() throw( IO_ERROR, PARSE_ERROR )
{
T token;
BOARD_ITEM* item;
token = NextTok();
if( token != T_LEFT )
Expecting( T_LEFT );
switch( NextTok() )
{
case T_kicad_pcb:
if( m_board == NULL )
m_board = new BOARD();
item = (BOARD_ITEM*) parseBOARD();
break;
default:
wxString err;
err.Printf( _( "unknown token \"%s\" " ), GetChars( FromUTF8() ) );
THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
return item;
}
BOARD* PCB_PARSER::parseBOARD() throw( IO_ERROR, PARSE_ERROR )
{
T token;
parseHeader();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_general:
parseGeneralSection();
break;
case T_page:
parsePAGE_INFO();
break;
case T_title_block:
parseTITLE_BLOCK();
break;
case T_layers:
parseLayers();
break;
case T_setup:
parseSetup();
break;
case T_net:
parseNETINFO_ITEM();
break;
case T_net_class:
parseNETCLASS();
break;
case T_gr_arc:
case T_gr_circle:
case T_gr_curve:
case T_gr_line:
case T_gr_poly:
parseDRAWSEGMENT();
break;
case T_gr_text:
parseTEXTE_PCB();
break;
case T_dimension:
parseDIMENSION();
break;
case T_module:
parseMODULE();
break;
case T_segment:
m_board->Add( parseTRACK(), ADD_APPEND );
break;
case T_via:
m_board->Add( parseSEGVIA(), ADD_APPEND );
break;
case T_zone:
m_board->Add( parseZONE_CONTAINER(), ADD_APPEND );
break;
case T_target:
m_board->Add( parsePCB_TARGET(), ADD_APPEND );
break;
default:
wxString err;
err.Printf( _( "unknown token \"%s\"" ), GetChars( FromUTF8() ) );
THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
}
return m_board;
}
void PCB_PARSER::parseHeader() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_kicad_pcb,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) );
T token;
NeedLEFT();
token = NextTok();
if( token != T_version )
Expecting( GetTokenText( T_version ) );
// Get the file version.
m_board->SetFileFormatVersionAtLoad( NeedNUMBER( GetTokenText( T_version ) ) );
// Skip the host name and host build version information.
NeedRIGHT();
NeedLEFT();
NeedSYMBOL();
NeedSYMBOL();
NeedSYMBOL();
NeedRIGHT();
}
void PCB_PARSER::parseGeneralSection() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_general,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
wxT( " as a general section." ) );
T token;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_thickness:
m_board->GetDesignSettings().SetBoardThickness( parseBoardUnits( T_thickness ) );
NeedRIGHT();
break;
case T_no_connects:
m_board->m_NbNoconnect = parseInt( "no connect count" );
NeedRIGHT();
break;
default: // Skip everything but the board thickness.
wxLogDebug( wxT( "Skipping general section token %s " ), GetTokenString( token ) );
while( ( token = NextTok() ) != T_RIGHT )
{
if( !IsSymbol( token ) && token != T_NUMBER )
Expecting( "symbol or number" );
}
}
}
}
void PCB_PARSER::parsePAGE_INFO() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_page,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
T token;
bool isPortrait = false;
NeedSYMBOL();
wxString pageType = FromUTF8();
if( pageType == PAGE_INFO::Custom )
{
PAGE_INFO::SetCustomWidthMils( Iu2Mils( NeedNUMBER( "width" ) ) );
PAGE_INFO::SetCustomHeightMils( Iu2Mils( NeedNUMBER( "height" ) ) );
}
token = NextTok();
if( token == T_portrait )
{
isPortrait = true;
NeedRIGHT();
}
else if( token != T_RIGHT )
{
Expecting( "portrait|)" );
}
PAGE_INFO pageInfo;
if( !pageInfo.SetType( pageType, isPortrait ) )
{
wxString err;
err.Printf( _( "page type \"%s\" is not valid " ), GetChars( FromUTF8() ) );
THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
m_board->SetPageSettings( pageInfo );
}
void PCB_PARSER::parseTITLE_BLOCK() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_title_block,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
wxT( " as TITLE_BLOCK." ) );
T token;
TITLE_BLOCK titleBlock;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_title:
NeedSYMBOL();
titleBlock.SetTitle( FromUTF8() );
break;
case T_date:
NeedSYMBOL();
titleBlock.SetDate( FromUTF8() );
break;
case T_rev:
NeedSYMBOL();
titleBlock.SetRevision( FromUTF8() );
break;
case T_comment:
{
int commentNumber = NeedNUMBER( "comment" );
switch( commentNumber )
{
case 1:
NeedSYMBOL();
titleBlock.SetComment1( FromUTF8() );
break;
case 2:
NeedSYMBOL();
titleBlock.SetComment2( FromUTF8() );
break;
case 3:
NeedSYMBOL();
titleBlock.SetComment3( FromUTF8() );
break;
case 4:
NeedSYMBOL();
titleBlock.SetComment4( FromUTF8() );
break;
default:
wxString err;
err.Printf( _( "%d is not a valid title block comment number" ), commentNumber );
THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
break;
}
default:
Expecting( "title, date, rev, company, or comment" );
}
NeedRIGHT();
}
m_board->SetTitleBlock( titleBlock );
}
void PCB_PARSER::parseLayers() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_layers,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layers." ) );
T token;
wxString name;
wxString type;
bool isVisible;
int visibleLayers = 0;
int enabledLayers = 0;
std::vector< LAYER > layers;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
#if !USE_LAYER_NAMES
NeedNUMBER( "layer index" );
#endif
NeedSYMBOL();
name = FromUTF8();
NeedSYMBOL();
type = FromUTF8();
token = NextTok();
if( token == T_hide )
{
isVisible = false;
NeedRIGHT();
}
else if( token == T_RIGHT )
{
isVisible = true;
}
else
{
Expecting( "hide or )" );
}
layers.push_back( LAYER( name, LAYER::ParseType( type.c_str() ), isVisible ) );
}
int copperLayerCount = 0;
for( unsigned i = 0; i < layers.size(); i++ )
{
if( layers[i].m_Type != LT_UNDEFINED )
copperLayerCount++;
}
// We need at least 2 copper layers and there must be an even number of them.
if( (copperLayerCount < 2) || ((copperLayerCount % 2) != 0) )
{
wxString err;
err.Printf( _( "%d is not a valid layer count" ), layers.size() );
THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
m_board->SetCopperLayerCount( copperLayerCount );
// Copper layers are sequential from front to back in the file but the current layer
// design uses sequential layers from back to front except for the front layer which
// is always vector index 15.
for( unsigned i = 0; i < layers.size(); i++ )
{
int layerIndex = i;
// The copper layers can have different names but they always are at the beginning
// and have a valid layer type. Non-copper layer name cannot be changed so the
// list index can be looked up by name.
if( layers[i].m_Type != LT_UNDEFINED )
{
if( i == 0 )
layerIndex = LAYER_N_FRONT;
else
layerIndex = copperLayerCount - 1 - i;
}
else
{
layerIndex = LAYER::GetDefaultIndex( layers[i].m_Name );
if( layerIndex == UNDEFINED_LAYER )
{
wxString error;
error.Printf( _( "Cannot determine fixed layer list index of layer name \"%s\"" ),
layers[i].m_Name );
THROW_IO_ERROR( error );
}
}
enabledLayers |= 1 << layerIndex;
if( layers[i].IsVisible() )
visibleLayers |= 1 << layerIndex;
layers[i].SetFixedListIndex( layerIndex );
m_board->SetLayer( layerIndex, layers[i] );
m_layerMap[ layers[i].m_Name ] = layerIndex;
wxLogDebug( wxT( "Mapping layer %s index index %d" ),
GetChars( layers[i].m_Name ), layerIndex );
}
m_board->SetVisibleLayers( visibleLayers );
m_board->SetEnabledLayers( enabledLayers );
}
int PCB_PARSER::lookUpLayer() throw( PARSE_ERROR, IO_ERROR )
{
#if USE_LAYER_NAMES
wxString name = FromUTF8();
const LAYER_HASH_MAP::iterator it = m_layerMap.find( name );
if( it == m_layerMap.end() )
{
wxString error;
error.Printf( _( "Layer %s in file <%s> at line %d, position %d was not defined in the layers section" ),
GetChars( name ), GetChars( CurSource() ), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
return m_layerMap[ name ];
#else
if( CurTok() != T_NUMBER )
Expecting( T_NUMBER );
int layerIndex = parseInt();
if( !m_board->IsLayerEnabled( layerIndex ) )
{
wxString error;
error.Printf( _( "Layer index %d in file <%s> at line, offset %d was not defined in the layers section" ),
layerIndex, GetChars( CurSource() ), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
return layerIndex;
#endif
}
int PCB_PARSER::parseBoardItemLayer() throw( PARSE_ERROR, IO_ERROR )
{
wxCHECK_MSG( CurTok() == T_layer, UNDEFINED_LAYER,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layer." ) );
NextTok();
int layerIndex = lookUpLayer();
// Handle closing ) in object parser.
return layerIndex;
}
int PCB_PARSER::parseBoardItemLayersAsMask() throw( PARSE_ERROR, IO_ERROR )
{
wxCHECK_MSG( CurTok() == T_layers, 0,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
wxT( " as item layer mask." ) );
int layerIndex;
int layerMask = 0;
T token;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
layerIndex = lookUpLayer();
layerMask |= ( 1 << layerIndex );
}
return layerMask;
}
void PCB_PARSER::parseSetup() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_setup,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as setup." ) );
T token;
int lastTraceWidth;
NETCLASS* defaultNetclass = m_board->m_NetClasses.GetDefault();
BOARD_DESIGN_SETTINGS designSettings = m_board->GetDesignSettings();
ZONE_SETTINGS zoneSettings = m_board->GetZoneSettings();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_last_trace_width:
lastTraceWidth = parseBoardUnits( T_last_trace_width );
break;
case T_user_trace_width:
m_board->m_TrackWidthList.push_back( parseBoardUnits( T_user_trace_width ) );
break;
case T_trace_clearance:
defaultNetclass->SetClearance( parseBoardUnits( T_trace_clearance ) );
break;
case T_zone_clearance:
zoneSettings.m_ZoneClearance = parseBoardUnits( T_zone_clearance );
break;
case T_zone_45_only:
zoneSettings.m_Zone_45_Only = parseBool();
break;
case T_trace_min:
designSettings.m_TrackMinWidth = parseBoardUnits( T_trace_min );
break;
case T_segment_width:
designSettings.m_DrawSegmentWidth = parseBoardUnits( T_segment_width );
break;
case T_edge_width:
designSettings.m_EdgeSegmentWidth = parseBoardUnits( T_edge_width );
break;
case T_via_size:
defaultNetclass->SetViaDiameter( parseBoardUnits( T_via_size ) );
break;
case T_via_drill:
defaultNetclass->SetViaDrill( parseBoardUnits( T_via_drill ) );
break;
case T_via_min_size:
designSettings.m_ViasMinSize = parseBoardUnits( T_via_min_size );
break;
case T_via_min_drill:
designSettings.m_ViasMinDrill = parseBoardUnits( T_via_min_drill );
break;
case T_user_via:
{
int viaSize = parseBoardUnits( "user via size" );
int viaDrill = parseBoardUnits( "user via drill" );
m_board->m_ViasDimensionsList.push_back( VIA_DIMENSION( viaSize, viaDrill ) );
}
break;
case T_uvia_size:
defaultNetclass->SetuViaDiameter( parseBoardUnits( T_uvia_size ) );
break;
case T_uvia_drill:
defaultNetclass->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
break;
case T_uvias_allowed:
designSettings.m_MicroViasAllowed = parseBool();
break;
case T_uvia_min_size:
designSettings.m_MicroViasMinSize = parseBoardUnits( T_uvia_min_size );
break;
case T_uvia_min_drill:
designSettings.m_MicroViasMinDrill = parseBoardUnits( T_uvia_min_drill );
break;
case T_pcb_text_width:
designSettings.m_PcbTextWidth = parseBoardUnits( T_pcb_text_width );
break;
case T_pcb_text_size:
designSettings.m_PcbTextSize.x = parseBoardUnits( "pcb text width" );
designSettings.m_PcbTextSize.y = parseBoardUnits( "pcb text height" );
break;
case T_mod_edge_width:
designSettings.m_ModuleSegmentWidth = parseBoardUnits( T_mod_edge_width );
break;
case T_mod_text_size:
designSettings.m_ModuleTextSize.x = parseBoardUnits( "module text width" );
designSettings.m_ModuleTextSize.y = parseBoardUnits( "module text height" );
break;
case T_mod_text_width:
designSettings.m_ModuleTextWidth = parseBoardUnits( T_mod_text_width );
break;
case T_pad_size:
{
wxSize sz;
sz.SetHeight( parseBoardUnits( "master pad height" ) );
sz.SetWidth( parseBoardUnits( "master pad width" ) );
designSettings.m_Pad_Master.SetSize( sz );
break;
}
case T_pad_drill:
{
int drillSize = parseBoardUnits( T_pad_drill );
designSettings.m_Pad_Master.SetDrillSize( wxSize( drillSize, drillSize ) );
}
break;
case T_pad_to_mask_clearance:
designSettings.m_SolderMaskMargin = parseBoardUnits( T_pad_to_mask_clearance );
break;
case T_pad_to_paste_clearance:
designSettings.m_SolderPasteMargin = parseBoardUnits( T_pad_to_paste_clearance );
break;
case T_pad_to_paste_clearance_ratio:
designSettings.m_SolderPasteMarginRatio = parseDouble( T_pad_to_paste_clearance_ratio );
break;
case T_aux_axis_origin:
m_board->SetOriginAxisPosition( wxPoint( parseBoardUnits( "auxilary origin X" ),
parseBoardUnits( "auxilary origin Y" ) ) );
break;
case T_visible_elements:
designSettings.SetVisibleElements( parseHex() );
break;
default:
Expecting( "valid setup token" );
}
NeedRIGHT();
}
m_board->SetDesignSettings( designSettings );
m_board->SetZoneSettings( zoneSettings );
// Until such time as the *.brd file does not have the
// global parameters:
// "last_trace_width", "trace_min_width", "via_size", "via_drill",
// "via_min_size", and "via_clearance", put those same global
// values into the default NETCLASS until later board load
// code should override them. *.kicad_brd files which have been
// saved with knowledge of NETCLASSes will override these
// defaults, old boards will not.
//
// @todo: I expect that at some point we can remove said global
// parameters from the *.brd file since the ones in the
// default netclass serve the same purpose. If needed
// at all, the global defaults should go into a preferences
// file instead so they are there to start new board
// projects.
m_board->m_NetClasses.GetDefault()->SetParams();
}
void PCB_PARSER::parseNETINFO_ITEM() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_net,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net." ) );
int number = parseInt( "net number" );
NeedSYMBOL();
wxString name = FromUTF8();
NeedRIGHT();
NETINFO_ITEM* net = new NETINFO_ITEM( m_board );
net->SetNet( number );
net->SetNetname( name );
m_board->AppendNet( net );
}
void PCB_PARSER::parseNETCLASS() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_net_class,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net class." ) );
T token;
auto_ptr<NETCLASS> nc( new NETCLASS( m_board, wxEmptyString ) );
NeedSYMBOL();
nc->SetName( FromUTF8() );
NeedSYMBOL();
nc->SetDescription( FromUTF8() );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_clearance:
nc->SetClearance( parseBoardUnits( T_clearance ) );
break;
case T_trace_width:
nc->SetTrackWidth( parseBoardUnits( T_trace_width ) );
break;
case T_via_dia:
nc->SetViaDiameter( parseBoardUnits( T_via_dia ) );
break;
case T_via_drill:
nc->SetViaDrill( parseBoardUnits( T_via_drill ) );
break;
case T_uvia_dia:
nc->SetuViaDiameter( parseBoardUnits( T_uvia_dia ) );
break;
case T_uvia_drill:
nc->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
break;
case T_add_net:
NeedSYMBOL();
nc->Add( FromUTF8() );
break;
default:
Expecting( "clearance, trace_width, via_dia, via_drill, uvia_dia, uvia_drill, or add_net" );
}
NeedRIGHT();
}
if( m_board->m_NetClasses.Add( nc.get() ) )
{
nc.release();
}
else
{
// Must have been a name conflict, this is a bad board file.
// User may have done a hand edit to the file.
// auto_ptr will delete nc on this code path
wxString error;
error.Printf( _( "duplicate NETCLASS name '%s' in file %s at line %d, offset %d" ),
nc->GetName().GetData(), CurSource(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
}
void PCB_PARSER::parseDRAWSEGMENT() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_gr_arc || CurTok() == T_gr_circle || CurTok() == T_gr_curve ||
CurTok() == T_gr_line || CurTok() == T_gr_poly,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as DRAWSEGMENT." ) );
T token;
auto_ptr< DRAWSEGMENT > segment( new DRAWSEGMENT( m_board ) );
switch( CurTok() )
{
case T_gr_arc:
segment->SetType( S_ARC );
NeedLEFT();
token = NextTok();
if( token != T_start )
Expecting( T_start );
segment->SetStart( parseXY() );
NeedRIGHT();
token = NextTok();
if( token != T_end )
Expecting( T_end );
segment->SetEnd( parseXY() );
NeedRIGHT();
token = NextTok();
if( token != T_angle )
Expecting( T_angle );
segment->SetAngle( parseDouble( "segment angle" ) );
NeedRIGHT();
break;
case T_gr_circle:
segment->SetType( S_CIRCLE );
NeedLEFT();
token = NextTok();
if( token != T_center )
Expecting( T_center );
segment->SetStart( parseXY() );
NeedRIGHT();
token = NextTok();
if( token != T_end )
Expecting( T_end );
segment->SetEnd( parseXY() );
NeedRIGHT();
break;
case T_gr_curve:
segment->SetType( S_CURVE );
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
segment->SetStart( parseXY() );
segment->SetBezControl1( parseXY() );
segment->SetBezControl2( parseXY() );
segment->SetEnd( parseXY() );
NeedRIGHT();
break;
case T_gr_line:
// Default DRAWSEGMENT type is S_SEGMENT.
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
segment->SetStart( parseXY() );
segment->SetEnd( parseXY() );
NeedRIGHT();
break;
case T_gr_poly:
{
segment->SetType( S_POLYGON );
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
std::vector< wxPoint > pts;
while( (token = NextTok()) != T_RIGHT )
pts.push_back( parseXY() );
segment->SetPolyPoints( pts );
}
break;
default:
Expecting( "gr_arc, gr_circle, gr_curve, gr_line, or gr_poly" );
}
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_layer:
segment->SetLayer( parseBoardItemLayer() );
break;
case T_width:
segment->SetWidth( parseBoardUnits( T_width ) );
break;
case T_tstamp:
segment->SetTimeStamp( parseHex() );
break;
case T_status:
segment->SetStatus( parseHex() );
break;
default:
Expecting( "layer, width, tstamp, or status" );
}
NeedRIGHT();
}
m_board->Add( segment.release() );
}
void PCB_PARSER::parseTEXTE_PCB( TEXTE_PCB* aText ) throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_gr_text,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as TEXTE_PCB." ) );
T token;
auto_ptr< TEXTE_PCB > text( new TEXTE_PCB( m_board ) );
NeedSYMBOLorNUMBER();
text->SetText( FromUTF8() );
NeedLEFT();
token = NextTok();
if( token != T_at )
Expecting( T_at );
wxPoint pt;
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
text->SetPosition( pt );
text->SetOrientation( parseDouble( "angle" ) * 10.0 );
NeedRIGHT();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_layer:
text->SetLayer( parseBoardItemLayer() );
NeedRIGHT();
break;
case T_tstamp:
text->SetTimeStamp( parseHex() );
NeedRIGHT();
break;
case T_effects:
parseEDA_TEXT( (EDA_TEXT*) text.get() );
break;
default:
Expecting( "layer, tstamp or effects" );
}
}
if( aText == NULL )
m_board->Add( text.release() );
else
*aText = *text;
}
void PCB_PARSER::parseDIMENSION() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_dimension,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as DIMENSION." ) );
T token;
auto_ptr< DIMENSION > dimension( new DIMENSION( m_board ) );
dimension->m_Value = parseBoardUnits( "dimension value" );
NeedLEFT();
token = NextTok();
if( token != T_width )
Expecting( T_width );
dimension->SetWidth( parseBoardUnits( "dimension width value" ) );
NeedRIGHT();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_layer:
dimension->SetLayer( parseBoardItemLayer() );
NeedRIGHT();
break;
case T_tstamp:
dimension->SetTimeStamp( parseHex() );
NeedRIGHT();
break;
case T_gr_text:
parseTEXTE_PCB( &dimension->m_Text );
break;
case T_feature1:
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
parseXY( &dimension->m_featureLineDOx, &dimension->m_featureLineDOy );
parseXY( &dimension->m_featureLineDFx, &dimension->m_featureLineDFy );
NeedRIGHT();
NeedRIGHT();
break;
case T_feature2:
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
parseXY( &dimension->m_featureLineGOx, &dimension->m_featureLineGOy );
parseXY( &dimension->m_featureLineGFx, &dimension->m_featureLineGFy );
NeedRIGHT();
NeedRIGHT();
break;
case T_crossbar:
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
parseXY( &dimension->m_crossBarOx, &dimension->m_crossBarOy );
parseXY( &dimension->m_crossBarFx, &dimension->m_crossBarFy );
NeedRIGHT();
NeedRIGHT();
break;
case T_arrow1a:
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
parseXY( &dimension->m_arrowD1Ox, &dimension->m_arrowD1Oy );
parseXY( &dimension->m_arrowD1Fx, &dimension->m_arrowD1Fy );
NeedRIGHT();
NeedRIGHT();
break;
case T_arrow1b:
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
parseXY( &dimension->m_arrowD2Ox, &dimension->m_arrowD2Oy );
parseXY( &dimension->m_arrowD2Fx, &dimension->m_arrowD2Fy );
NeedRIGHT();
NeedRIGHT();
break;
case T_arrow2a:
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
parseXY( &dimension->m_arrowG1Ox, &dimension->m_arrowG1Oy );
parseXY( &dimension->m_arrowG1Fx, &dimension->m_arrowG1Fy );
NeedRIGHT();
NeedRIGHT();
break;
case T_arrow2b:
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
parseXY( &dimension->m_arrowG2Ox, &dimension->m_arrowG2Oy );
parseXY( &dimension->m_arrowG2Fx, &dimension->m_arrowG2Fy );
NeedRIGHT();
NeedRIGHT();
break;
default:
Expecting( "layer, tstamp, gr_text, feature1, feature2 crossbar, arrow1a, "
"arrow1b, arrow2a, or arrow2b" );
}
}
m_board->Add( dimension.release() );
}
void PCB_PARSER::parseMODULE() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_RET( CurTok() == T_module,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as MODULE." ) );
wxPoint pt;
T token;
auto_ptr< MODULE > module( new MODULE( m_board ) );
NeedSYMBOL();
module->SetLibRef( FromUTF8() );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_LEFT )
token = NextTok();
switch( token )
{
case T_locked:
module->SetLocked( true );
break;
case T_placed:
module->SetIsPlaced( true );
break;
case T_layer:
module->SetLayer( parseBoardItemLayer() );
NeedRIGHT();
break;
case T_tedit:
module->SetLastEditTime( parseHex() );
NeedRIGHT();
break;
case T_tstamp:
module->SetTimeStamp( parseHex() );
NeedRIGHT();
break;
case T_at:
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
module->SetPosition( pt );
token = NextTok();
if( token == T_NUMBER )
{
module->SetOrientation( parseDouble() * 10.0 );
NeedRIGHT();
}
else if( token != T_RIGHT )
{
Expecting( T_RIGHT );
}
break;
case T_descr:
NeedSYMBOL();
module->SetDescription( FromUTF8() );
NeedRIGHT();
break;
case T_tags:
NeedSYMBOL();
module->SetKeywords( FromUTF8() );
NeedRIGHT();
break;
case T_path:
NeedSYMBOL();
module->SetPath( FromUTF8() );
NeedRIGHT();
break;
case T_autoplace_cost90:
module->m_CntRot90 = parseInt( "auto place cost at 90 degrees" );
NeedRIGHT();
break;
case T_autoplace_cost180:
module->m_CntRot180 = parseInt( "auto place cost at 180 degrees" );
NeedRIGHT();
break;
case T_solder_mask_margin:
module->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
NeedRIGHT();
break;
case T_solder_paste_margin:
module->SetLocalSolderPasteMargin(
parseBoardUnits( "local solder paste margin value" ) );
NeedRIGHT();
break;
case T_solder_paste_ratio:
module->SetLocalSolderPasteMarginRatio(
parseDouble( "local solder paste margin ratio value" ) );
NeedRIGHT();
break;
case T_clearance:
module->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
NeedRIGHT();
break;
case T_zone_connect:
module->SetZoneConnection( (ZoneConnection) parseInt( "zone connection value" ) );
NeedRIGHT();
break;
case T_thermal_width:
module->SetThermalWidth( parseBoardUnits( "thermal width value" ) );
NeedRIGHT();
break;
case T_thermal_gap:
module->SetThermalGap( parseBoardUnits( "thermal gap value" ) );
NeedRIGHT();
break;
case T_attr:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
switch( token )
{
case T_smd:
module->SetAttributes( module->GetAttributes() | MOD_CMS );
break;
case T_virtual:
module->SetAttributes( module->GetAttributes() | MOD_VIRTUAL );
break;
default:
Expecting( "smd and/or virtual" );
}
}
break;
case T_fp_text:
{
TEXTE_MODULE* text = parseTEXTE_MODULE();
text->SetParent( module.get() );
double orientation = text->GetOrientation();
orientation -= module->GetOrientation();
text->SetOrientation( orientation );
text->SetDrawCoord();
if( text->GetType() == TEXT_is_REFERENCE )
{
module->Reference() = *text;
delete text;
}
else if( text->GetType() == TEXT_is_VALUE )
{
module->Value() = *text;
delete text;
}
else
{
module->m_Drawings.PushBack( text );
}
break;
}
case T_fp_arc:
case T_fp_circle:
case T_fp_curve:
case T_fp_line:
case T_fp_poly:
{
EDGE_MODULE* em = parseEDGE_MODULE();
em->SetParent( module.get() );
em->SetDrawCoord();
module->m_Drawings.PushBack( em );
break;
}
case T_pad:
{
D_PAD* pad = parseD_PAD();
wxPoint pt = pad->GetPos0();
RotatePoint( &pt, module->GetOrientation() );
pad->SetPosition( pt + module->GetPosition() );
module->AddPad( pad );
break;
}
case T_model:
module->Add3DModel( parse3DModel() );
break;
default:
Expecting( "locked, placed, tedit, tstamp, at, descr, tags, path, "
"autoplace_cost90, autoplace_cost180, solder_mask_margin, "
"solder_paste_margin, solder_paste_ratio, clearance, "
"zone_connect, thermal_width, thermal_gap, attr, fp_text, "
"fp_arc, fp_circle, fp_curve, fp_line, fp_poly, pad, or model" );
}
}
m_board->Add( module.release(), ADD_APPEND );
}
TEXTE_MODULE* PCB_PARSER::parseTEXTE_MODULE() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_MSG( CurTok() == T_fp_text, NULL,
wxString::Format( wxT( "Cannot parse %s as TEXTE_MODULE at line %d, offset %d." ),
GetTokenString( CurTok() ), CurLineNumber(), CurOffset() ) );
T token = NextTok();
auto_ptr< TEXTE_MODULE > text( new TEXTE_MODULE( NULL ) );
switch( token )
{
case T_reference:
text->SetType( TEXT_is_REFERENCE );
break;
case T_value:
text->SetType( TEXT_is_VALUE );
break;
case T_user:
break; // Default type is user text.
default:
THROW_IO_ERROR( wxString::Format( _( "cannot handle module text type %s" ),
GetChars( FromUTF8() ) ) );
}
NeedSYMBOLorNUMBER();
text->SetText( FromUTF8() );
NeedLEFT();
token = NextTok();
if( token != T_at )
Expecting( T_at );
wxPoint pt;
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
text->SetPos0( pt );
text->SetOrientation( parseDouble( "angle" ) * 10.0 );
NeedRIGHT();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_LEFT )
token = NextTok();
switch( token )
{
case T_hide:
text->SetVisible( false );
break;
case T_effects:
parseEDA_TEXT( (EDA_TEXT*) text.get() );
break;
default:
Expecting( "hide or effects" );
}
}
return text.release();
}
EDGE_MODULE* PCB_PARSER::parseEDGE_MODULE() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_MSG( CurTok() == T_fp_arc || CurTok() == T_fp_circle || CurTok() == T_fp_curve ||
CurTok() == T_fp_line || CurTok() == T_fp_poly, NULL,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as EDGE_MODULE." ) );
T token;
auto_ptr< EDGE_MODULE > segment( new EDGE_MODULE( NULL ) );
switch( CurTok() )
{
case T_fp_arc:
segment->SetType( S_ARC );
NeedLEFT();
token = NextTok();
if( token != T_start )
Expecting( T_start );
segment->SetStart0( parseXY() );
NeedRIGHT();
token = NextTok();
if( token != T_end )
Expecting( T_end );
segment->SetEnd0( parseXY() );
NeedRIGHT();
token = NextTok();
if( token != T_angle )
Expecting( T_angle );
segment->SetAngle( parseDouble( "segment angle" ) );
NeedRIGHT();
break;
case T_fp_circle:
segment->SetType( S_CIRCLE );
NeedLEFT();
token = NextTok();
if( token != T_center )
Expecting( T_center );
segment->SetStart0( parseXY() );
NeedRIGHT();
NeedLEFT();
token = NextTok();
if( token != T_end )
Expecting( T_end );
segment->SetEnd0( parseXY() );
NeedRIGHT();
break;
case T_fp_curve:
segment->SetType( S_CURVE );
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
segment->SetStart0( parseXY() );
segment->SetBezControl1( parseXY() );
segment->SetBezControl2( parseXY() );
segment->SetEnd0( parseXY() );
NeedRIGHT();
break;
case T_fp_line:
// Default DRAWSEGMENT type is S_SEGMENT.
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
segment->SetStart0( parseXY() );
segment->SetEnd0( parseXY() );
NeedRIGHT();
break;
case T_fp_poly:
{
segment->SetType( S_POLYGON );
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
std::vector< wxPoint > pts;
while( (token = NextTok()) != T_RIGHT )
pts.push_back( parseXY() );
segment->SetPolyPoints( pts );
}
break;
default:
Expecting( "fp_arc, fp_circle, fp_curve, fp_line, or fp_poly" );
}
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_layer:
segment->SetLayer( parseBoardItemLayer() );
break;
case T_width:
segment->SetWidth( parseBoardUnits( T_width ) );
break;
case T_tstamp:
segment->SetTimeStamp( parseHex() );
break;
case T_status:
segment->SetStatus( parseHex() );
break;
default:
Expecting( "layer, width, tstamp, or status" );
}
NeedRIGHT();
}
return segment.release();
}
D_PAD* PCB_PARSER::parseD_PAD() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_MSG( CurTok() == T_pad, NULL,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as D_PAD." ) );
wxSize sz;
wxPoint pt;
auto_ptr< D_PAD > pad( new D_PAD( NULL ) );
NeedSYMBOLorNUMBER();
pad->SetPadName( FromUTF8() );
T token = NextTok();
switch( token )
{
case T_thru_hole:
pad->SetAttribute( PAD_STANDARD );
break;
case T_smd:
pad->SetAttribute( PAD_SMD );
break;
case T_connect:
pad->SetAttribute( PAD_CONN );
break;
case T_np_thru_hole:
pad->SetAttribute( PAD_HOLE_NOT_PLATED );
break;
default:
Expecting( "thru_hole, smd, connect, or np_thru_hole" );
}
token = NextTok();
switch( token )
{
case T_circle:
pad->SetShape( PAD_CIRCLE );
break;
case T_rectangle:
pad->SetShape( PAD_RECT );
break;
case T_oval:
pad->SetShape( PAD_OVAL );
break;
case T_trapezoid:
pad->SetShape( PAD_TRAPEZOID );
break;
default:
Expecting( "circle, rectangle, oval, or trapezoid" );
}
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_size:
sz.SetWidth( parseBoardUnits( "width value" ) );
sz.SetHeight( parseBoardUnits( "height value" ) );
pad->SetSize( sz );
NeedRIGHT();
break;
case T_at:
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
pad->SetPos0( pt );
token = NextTok();
if( token == T_NUMBER )
{
pad->SetOrientation( parseDouble() * 10.0 );
NeedRIGHT();
}
else if( token != T_RIGHT )
{
Expecting( ") or angle value" );
}
break;
case T_drill:
sz.SetWidth( parseBoardUnits( "drill size" ) );
sz.SetHeight( 0 );
token = NextTok();
if( token == T_NUMBER )
{
sz.SetHeight( parseBoardUnits() );
token = NextTok();
if( token == T_LEFT )
{
token = NextTok();
if( token != T_offset )
Expecting( T_offset );
pt.x = parseDouble( "drill offset X" );
pt.y = 0;
token = NextTok();
if( token == T_NUMBER )
{
pt.y = parseDouble();
NeedRIGHT();
}
else if( token != T_RIGHT )
{
Expecting( T_RIGHT );
}
pad->SetOffset( pt );
}
else if( token != T_RIGHT )
{
Expecting( T_RIGHT );
}
pad->SetDrillSize( sz );
}
else if( token != T_RIGHT )
{
Expecting( T_RIGHT );
}
break;
case T_layers:
pad->SetLayerMask( parseBoardItemLayersAsMask() );
break;
case T_net:
pad->SetNet( parseInt( "net number" ) );
NeedSYMBOL();
pad->SetNetname( FromUTF8() );
NeedRIGHT();
break;
case T_die_length:
pad->SetDieLength( parseBoardUnits( T_die_length ) );
NeedRIGHT();
break;
case T_solder_mask_margin:
pad->SetLocalSolderMaskMargin( parseBoardUnits( T_solder_mask_margin ) );
NeedRIGHT();
break;
case T_solder_paste_margin:
pad->SetLocalSolderPasteMargin( parseBoardUnits( T_solder_paste_margin ) );
NeedRIGHT();
break;
case T_solder_paste_margin_ratio:
pad->SetLocalSolderPasteMarginRatio( parseBoardUnits( T_solder_paste_margin_ratio ) );
NeedRIGHT();
break;
case T_clearance:
pad->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
NeedRIGHT();
break;
case T_zone_connect:
pad->SetZoneConnection( (ZoneConnection) parseInt( "zone connection value" ) );
NeedRIGHT();
break;
case T_thermal_width:
pad->SetThermalWidth( parseBoardUnits( T_thermal_width ) );
NeedRIGHT();
break;
case T_thermal_gap:
pad->SetThermalGap( parseBoardUnits( T_thermal_gap ) );
NeedRIGHT();
break;
default:
Expecting( "at, drill, layers, net, die_length, solder_mask_margin, "
"solder_paste_margin, solder_paste_margin_ratio, clearance, "
"zone_connect, thermal_width, or thermal_gap" );
}
}
return pad.release();
}
TRACK* PCB_PARSER::parseTRACK() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_MSG( CurTok() == T_segment, NULL,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as TRACK." ) );
wxPoint pt;
T token;
auto_ptr< TRACK > track( new TRACK( m_board ) );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_start:
pt.x = parseBoardUnits( "start x" );
pt.y = parseBoardUnits( "start y" );
track->SetStart( pt );
break;
case T_end:
pt.x = parseBoardUnits( "end x" );
pt.y = parseBoardUnits( "end y" );
track->SetEnd( pt );
break;
case T_width:
track->SetWidth( parseBoardUnits( "width" ) );
break;
case T_layer:
track->SetLayer( parseBoardItemLayer() );
break;
case T_net:
track->SetNet( parseInt( "net number" ) );
break;
case T_tstamp:
track->SetTimeStamp( parseHex() );
break;
case T_status:
track->SetStatus( parseHex() );
break;
default:
Expecting( "start, end, width, layer, net, tstamp, or status" );
}
NeedRIGHT();
}
return track.release();
}
SEGVIA* PCB_PARSER::parseSEGVIA() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_MSG( CurTok() == T_via, NULL,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as SEGVIA." ) );
wxPoint pt;
T token;
auto_ptr< SEGVIA > via( new SEGVIA( m_board ) );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_LEFT )
token = NextTok();
switch( token )
{
case T_thru:
via->SetShape( VIA_THROUGH );
break;
case T_blind:
via->SetShape( VIA_BLIND_BURIED );
break;
case T_micro:
via->SetShape( VIA_MICROVIA );
break;
case T_at:
pt.x = parseBoardUnits( "start x" );
pt.y = parseBoardUnits( "start y" );
via->SetStart( pt );
NeedRIGHT();
break;
case T_size:
via->SetWidth( parseBoardUnits( "via width" ) );
NeedRIGHT();
break;
case T_drill:
via->SetDrill( parseBoardUnits( "drill diameter" ) );
NeedRIGHT();
break;
case T_layers:
{
int layer1, layer2;
NextTok();
layer1 = lookUpLayer();
NextTok();
layer2 = lookUpLayer();
via->SetLayerPair( layer1, layer2 );
NeedRIGHT();
}
break;
case T_net:
via->SetNet( parseInt( "net number" ) );
NeedRIGHT();
break;
case T_tstamp:
via->SetTimeStamp( parseHex() );
NeedRIGHT();
break;
case T_status:
via->SetStatus( parseHex() );
NeedRIGHT();
break;
default:
Expecting( "at, size, drill, layers, net, tstamp, or status" );
}
}
return via.release();
}
ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_MSG( CurTok() == T_zone, NULL,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
wxT( " as ZONE_CONTAINER." ) );
int hatchStyle;
int hatchPitch;
wxPoint pt;
T token;
auto_ptr< ZONE_CONTAINER > zone( new ZONE_CONTAINER( m_board ) );
zone->SetPriority( 0 );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_LEFT )
token = NextTok();
switch( token )
{
case T_net:
zone->SetNet( parseInt( "net number" ) );
NeedRIGHT();
break;
case T_net_name:
NeedSYMBOL();
zone->SetNetName( FromUTF8() );
NeedRIGHT();
break;
case T_layer:
zone->SetLayer( parseBoardItemLayer() );
NeedRIGHT();
break;
case T_tstamp:
zone->SetTimeStamp( parseHex() );
NeedRIGHT();
break;
case T_hatch:
token = NextTok();
if( token != T_none && token != T_edge && token != T_full )
Expecting( "none, edge, or full" );
switch( token )
{
default:
case T_none: hatchStyle = CPolyLine::NO_HATCH; break;
case T_edge: hatchStyle = CPolyLine::DIAGONAL_EDGE; break;
case T_full: hatchStyle = CPolyLine::DIAGONAL_FULL;
}
hatchPitch = parseBoardUnits( "hatch pitch" );
NeedRIGHT();
break;
case T_priority:
zone->SetPriority( parseInt( "zone priority" ) );
NeedRIGHT();
break;
case T_connect_pads:
token = NextTok();
switch( token )
{
case T_yes: zone->SetPadConnection( PAD_IN_ZONE ); break;
case T_use_thermal: zone->SetPadConnection( THERMAL_PAD ); break;
case T_no: zone->SetPadConnection( PAD_NOT_IN_ZONE ); break;
default: Expecting( "yes, no, or use_thermal" );
}
NeedLEFT();
token = NextTok();
if( token != T_clearance )
Expecting( T_clearance );
zone->SetZoneClearance( parseBoardUnits( "zone clearance" ) );
NeedRIGHT();
NeedRIGHT();
break;
case T_min_thickness:
zone->SetMinThickness( parseBoardUnits( T_min_thickness ) );
NeedRIGHT();
break;
case T_fill:
token = NextTok();
if( token != T_yes && token != T_no )
Expecting( "yes or no" );
zone->SetIsFilled( token == T_yes );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_mode:
token = NextTok();
if( token != T_segment && token != T_polygon )
Expecting( "segment or polygon" );
// @todo Create an enum for fill modes. Using true/false is not clear.
zone->SetFillMode( ( T_segment ) ? true : false );
break;
case T_arc_segments:
zone->SetArcSegCount( parseInt( "arc segment count" ) );
break;
case T_thermal_gap:
zone->SetThermalReliefGap( parseBoardUnits( T_thermal_gap ) );
break;
case T_thermal_bridge_width:
zone->SetThermalReliefCopperBridge( parseBoardUnits( T_thermal_bridge_width ) );
break;
case T_smoothing:
switch( NextTok() )
{
case T_none:
zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_NONE );
break;
case T_chamfer:
zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_CHAMFER );
break;
case T_fillet:
zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_FILLET );
break;
default:
Expecting( "none, chamfer, or fillet" );
}
break;
case T_radius:
zone->SetCornerRadius( parseBoardUnits( "corner radius" ) );
break;
default:
Expecting( "mode, arc_segments, thermal_gap, thermal_bridge_width, "
"smoothing, or radius" );
}
NeedRIGHT();
}
break;
case T_polygon:
{
std::vector< wxPoint > pts;
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
pts.push_back( parseXY() );
}
NeedRIGHT();
zone->AddPolygon( pts );
}
break;
case T_filled_polygon:
{
wxPoint pt;
std::vector< CPolyPt > pts;
NeedLEFT();
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
pt = parseXY();
pts.push_back( CPolyPt( pt.x, pt.y ) );
}
NeedRIGHT();
pts.back().end_contour = true;
zone->AddFilledPolysList( pts );
}
break;
case T_fill_segments:
{
std::vector< SEGMENT > segs;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
SEGMENT segment( parseXY(), parseXY() );
NeedRIGHT();
segs.push_back( segment );
}
zone->AddFillSegments( segs );
}
break;
default:
Expecting( "net, layer, tstamp, hatch, priority, connect_pads, min_thickness, "
"fill, polygon, filled_polygon, or fill_segments" );
}
}
if( zone->GetNumCorners() > 2 )
{
if( !zone->IsOnCopperLayer() )
{
zone->SetFillMode( 0 );
zone->SetNet( 0 );
}
// Set hatch here, after outlines corners are read
zone->m_Poly->SetHatch( hatchStyle, hatchPitch );
}
return zone.release();
}
PCB_TARGET* PCB_PARSER::parsePCB_TARGET() throw( IO_ERROR, PARSE_ERROR )
{
wxCHECK_MSG( CurTok() == T_target, NULL,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TARGET." ) );
wxPoint pt;
T token;
auto_ptr< PCB_TARGET > target( new PCB_TARGET( NULL ) );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_LEFT )
token = NextTok();
switch( token )
{
case T_x:
target->SetShape( 1 );
break;
case T_plus:
target->SetShape( 0 );
break;
case T_at:
pt.x = parseBoardUnits( "target x position" );
pt.y = parseBoardUnits( "target y position" );
target->SetPosition( pt );
NeedRIGHT();
break;
case T_size:
target->SetSize( parseBoardUnits( "target size" ) );
NeedRIGHT();
break;
case T_width:
target->SetWidth( parseBoardUnits( "target thickness" ) );
NeedRIGHT();
break;
case T_tstamp:
target->SetTimeStamp( parseHex() );
NeedRIGHT();
break;
default:
Expecting( "x, plus, at, size, width, or tstamp" );
}
}
return target.release();
}
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 CERN
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file pcb_parser.h
* @brief Pcbnew s-expression file format parser definition.
*/
#ifndef _PCBNEW_PARSER_H_
#define _PCBNEW_PARSER_H_
#include <pcb_lexer.h>
#include <wx/hashmap.h>
using namespace PCB;
class BOARD;
class BOARD_ITEM;
class D_PAD;
class EDGE_MODULE;
class TEXTE_MODULE;
class TEXTE_PCB;
class MODULE;
class PCB_TARGET;
class S3D_MASTER;
class ZONE_CONTAINER;
WX_DECLARE_STRING_HASH_MAP( int, LAYER_HASH_MAP );
#define USE_LAYER_NAMES 1 // Set to 0 to format and parse layers by index number.
/**
* Class PCB_PARSER
* reads a Pcbnew s-expression fromatted #LINE_READER object and returns the appropriate
* #BOARD_ITEM object.
*/
class PCB_PARSER : public PCB_LEXER
{
BOARD* m_board;
LAYER_HASH_MAP m_layerMap; //< Map layer name to it's index saved in BOARD::m_Layer.
void parseHeader() throw( IO_ERROR, PARSE_ERROR );
void parseGeneralSection() throw( IO_ERROR, PARSE_ERROR );
void parsePAGE_INFO() throw( IO_ERROR, PARSE_ERROR );
void parseTITLE_BLOCK() throw( IO_ERROR, PARSE_ERROR );
void parseLayers() throw( IO_ERROR, PARSE_ERROR );
void parseSetup() throw( IO_ERROR, PARSE_ERROR );
void parseNETINFO_ITEM() throw( IO_ERROR, PARSE_ERROR );
void parseNETCLASS() throw( IO_ERROR, PARSE_ERROR );
void parseDRAWSEGMENT() throw( IO_ERROR, PARSE_ERROR );
void parseTEXTE_PCB( TEXTE_PCB* aText = NULL ) throw( IO_ERROR, PARSE_ERROR );
void parseDIMENSION() throw( IO_ERROR, PARSE_ERROR );
void parseMODULE() throw( IO_ERROR, PARSE_ERROR );
TEXTE_MODULE* parseTEXTE_MODULE() throw( IO_ERROR, PARSE_ERROR );
EDGE_MODULE* parseEDGE_MODULE() throw( IO_ERROR, PARSE_ERROR );
D_PAD* parseD_PAD() throw( IO_ERROR, PARSE_ERROR );
TRACK* parseTRACK() throw( IO_ERROR, PARSE_ERROR );
SEGVIA* parseSEGVIA() throw( IO_ERROR, PARSE_ERROR );
ZONE_CONTAINER* parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR );
PCB_TARGET* parsePCB_TARGET() throw( IO_ERROR, PARSE_ERROR );
BOARD* parseBOARD() throw( IO_ERROR, PARSE_ERROR );
/**
* Function lookUpLayer
* parses the current token for the layer definition of a #BOARD_ITEM object.
*
* @throw IO_ERROR if the layer is not valid.
* @throw PARSE_ERROR if the layer syntax is incorrect.
* @return The index the parsed #BOARD_ITEM layer.
*/
int lookUpLayer() throw( PARSE_ERROR, IO_ERROR );
/**
* Function parseBoardItemLayer
* parses the layer definition of a #BOARD_ITEM object.
*
* @throw IO_ERROR if the layer is not valid.
* @throw PARSE_ERROR if the layer syntax is incorrect.
* @return The index the parsed #BOARD_ITEM layer.
*/
int parseBoardItemLayer() throw( IO_ERROR, PARSE_ERROR );
/**
* Function parseBoardItemLayersAsMask
* parses the layers definition of a #BOARD_ITEM object.
*
* @throw IO_ERROR if any of the layers is not valid.
* @throw PARSE_ERROR if the layers syntax is incorrect.
* @return The mask of layers the parsed #BOARD_ITEM is on.
*/
int parseBoardItemLayersAsMask() throw( PARSE_ERROR, IO_ERROR );
/**
* Function parseXY
* parses a coordinate pair (xy X Y) in board units (mm).
*
* The parser checks if the previous token was T_LEFT and parses the remainder of
* the token syntax. This is used when parsing a list of coorinate points. This
* way the parser can be used in either case.
*
* @throw PARSE_ERROR if the coordinate pair syntax is incorrect.
* @return A wxPoint object containing the coordinate pair.
*/
wxPoint parseXY() throw( PARSE_ERROR );
void parseXY( int* aX, int* aY ) throw( PARSE_ERROR );
/**
* Function parseEDA_TEXT
* parses the common settings for any object derived from #EDA_TEXT.
*
* @throw PARSE_ERROR if the text syntax is not valid.
* @param aText A point to the #EDA_TEXT object to save the parsed settings into.
*/
void parseEDA_TEXT( EDA_TEXT* aText ) throw( PARSE_ERROR );
S3D_MASTER* parse3DModel() throw( PARSE_ERROR );
/**
* Function parseDouble
* parses the current token as an ASCII numeric string with possible leading whitespace into
* a double precision floating point number.
*
* @throw IO_ERROR if an error occurs attempting to convert the current token.
* @return The result of the parsed token.
*/
double parseDouble() throw( IO_ERROR );
inline double parseDouble( const char* aExpected ) throw( IO_ERROR )
{
NeedNUMBER( aExpected );
return parseDouble();
}
inline double parseDouble( T aToken ) throw( IO_ERROR )
{
return parseDouble( GetTokenText( aToken ) );
}
inline int parseBoardUnits() throw( IO_ERROR )
{
// There should be no rounding issues here, since the values in the file are in mm
// and get converted to nano-meters. This product should be an integer, exactly.
return int( parseDouble() * 1e6 );
}
inline int parseBoardUnits( const char* aExpected ) throw( PARSE_ERROR )
{
return KIROUND( parseDouble( aExpected ) * 1e6 );
}
inline int parseBoardUnits( T aToken ) throw( PARSE_ERROR )
{
return parseBoardUnits( GetTokenText( aToken ) );
}
inline int parseInt() throw( PARSE_ERROR )
{
return (int)strtol( CurText(), NULL, 10 );
}
inline int parseInt( const char* aExpected ) throw( PARSE_ERROR )
{
NeedNUMBER( aExpected );
return parseInt();
}
inline int parseHex() throw( PARSE_ERROR )
{
NeedSYMBOLorNUMBER();
return (int)strtol( CurText(), NULL, 16 );
}
bool parseBool() throw( PARSE_ERROR );
public:
PCB_PARSER( LINE_READER* aReader, BOARD* aBoard = NULL ) :
PCB_LEXER( aReader ),
m_board( aBoard )
{
}
BOARD_ITEM* Parse() throw( IO_ERROR, PARSE_ERROR );
};
#endif // _PCBNEW_PARSER_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment