kicad-source/common/plugins/eagle/eagle_parser.cpp
Jeff Young 739f4d0a1e Improvements to Eagle dimension importing.
1) Import leaders
2) Drop radius and angle dimensions
3) Import dimension text sizes
4) Correctly interpret which side dimension offset is on
5) Correclty handle offsets for non-cardinal dimensions

Fixes https://gitlab.com/kicad/code/kicad/issues/10763
2022-04-12 15:47:39 +01:00

1104 lines
35 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2012-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2017 CERN.
*
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
*
* 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 <plugins/eagle/eagle_parser.h>
#include <string_utils.h>
#include <richio.h>
#include <wx/log.h>
#include <functional>
#include <cstdio>
constexpr auto DEFAULT_ALIGNMENT = ETEXT::BOTTOM_LEFT;
wxString escapeName( const wxString& aNetName )
{
wxString ret( aNetName );
ret.Replace( "!", "~" );
return ConvertToNewOverbarNotation( ret );
}
template<> template<>
OPTIONAL_XML_ATTRIBUTE<wxString>::OPTIONAL_XML_ATTRIBUTE( wxString aData )
{
m_isAvailable = !aData.IsEmpty();
if( m_isAvailable )
Set( aData );
}
ECOORD::ECOORD( const wxString& aValue, enum ECOORD::EAGLE_UNIT aUnit )
{
// This array is used to adjust the fraction part value basing on the number of digits
// in the fraction.
constexpr int DIVIDERS[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
constexpr unsigned int DIVIDERS_MAX_IDX = sizeof( DIVIDERS ) / sizeof( DIVIDERS[0] ) - 1;
int integer, fraction, pre_fraction, post_fraction;
// The following check is needed to handle correctly negative fractions where the integer
// part == 0.
bool negative = ( aValue[0] == '-' );
// %n is used to find out how many digits contains the fraction part, e.g. 0.001 contains 3
// digits.
int ret = sscanf( aValue.c_str(), "%d.%n%d%n", &integer, &pre_fraction, &fraction,
&post_fraction );
if( ret == 0 )
throw XML_PARSER_ERROR( "Invalid coordinate" );
// process the integer part
value = ConvertToNm( integer, aUnit );
// process the fraction part
if( ret == 2 )
{
int digits = post_fraction - pre_fraction;
// adjust the number of digits if necessary as we cannot handle anything smaller than
// nanometers (rounding).
if( (unsigned) digits > DIVIDERS_MAX_IDX )
{
int diff = digits - DIVIDERS_MAX_IDX;
digits = DIVIDERS_MAX_IDX;
fraction /= DIVIDERS[diff];
}
int frac_value = ConvertToNm( fraction, aUnit ) / DIVIDERS[digits];
// keep the sign in mind
value = negative ? value - frac_value : value + frac_value;
}
}
long long int ECOORD::ConvertToNm( int aValue, enum EAGLE_UNIT aUnit )
{
long long int ret;
switch( aUnit )
{
default:
case EU_NM: ret = aValue; break;
case EU_MM: ret = (long long) aValue * 1000000; break;
case EU_INCH: ret = (long long) aValue * 25400000; break;
case EU_MIL: ret = (long long) aValue * 25400; break;
}
if( ( ret > 0 ) != ( aValue > 0 ) )
wxLogError( _( "Invalid size %lld: too large" ), aValue );
return ret;
}
// Template specializations below parse wxString to the used types:
// - wxString (preferred)
// - string
// - double
// - int
// - bool
// - EROT
// - ECOORD
template <>
wxString Convert<wxString>( const wxString& aValue )
{
return aValue;
}
template <>
std::string Convert<std::string>( const wxString& aValue )
{
return std::string( aValue.ToUTF8() );
}
template <>
double Convert<double>( const wxString& aValue )
{
double value;
if( aValue.ToCDouble( &value ) )
return value;
else
throw XML_PARSER_ERROR( "Conversion to double failed. Original value: '" +
aValue.ToStdString() + "'." );
}
template <>
int Convert<int>( const wxString& aValue )
{
if( aValue.IsEmpty() )
throw XML_PARSER_ERROR( "Conversion to int failed. Original value is empty." );
return wxAtoi( aValue );
}
template <>
bool Convert<bool>( const wxString& aValue )
{
if( aValue != "yes" && aValue != "no" )
throw XML_PARSER_ERROR( "Conversion to bool failed. Original value, '" +
aValue.ToStdString() +
"', is neither 'yes' nor 'no'." );
return aValue == "yes";
}
/// parse an Eagle XML "rot" field. Unfortunately the DTD seems not to explain
/// this format very well. [S][M]R<degrees>. Examples: "R90", "MR180", "SR180"
template<>
EROT Convert<EROT>( const wxString& aRot )
{
EROT value;
value.spin = aRot.find( 'S' ) != aRot.npos;
value.mirror = aRot.find( 'M' ) != aRot.npos;
value.degrees = strtod( aRot.c_str()
+ 1 // skip leading 'R'
+ int( value.spin ) // skip optional leading 'S'
+ int( value.mirror ), // skip optional leading 'M'
nullptr );
return value;
}
template<>
ECOORD Convert<ECOORD>( const wxString& aCoord )
{
// Eagle uses millimeters as the default unit
return ECOORD( aCoord, ECOORD::EAGLE_UNIT::EU_MM );
}
/**
* Parse \a aAttribute of the XML node \a aNode.
*
* @param aNode is the node whose attribute will be parsed.
* @param aAttribute is the attribute that will be parsed.
* @throw XML_PARSER_ERROR - exception thrown if the required attribute is missing
* @return T - the attributed parsed as the specified type.
*/
template<typename T>
T parseRequiredAttribute( wxXmlNode* aNode, const wxString& aAttribute )
{
wxString value;
if( aNode->GetAttribute( aAttribute, &value ) )
return Convert<T>( value );
else
throw XML_PARSER_ERROR( "The required attribute " + aAttribute + " is missing." );
}
/**
* Parse option \a aAttribute of the XML node \a aNode.
*
* @param aNode is the node whose attribute will be parsed.
* @param aAttribute is the attribute that will be parsed.
* @return OPTIONAL_XML_ATTRIBUTE<T> - an optional XML attribute, parsed as the specified type if
* found.
*/
template<typename T>
OPTIONAL_XML_ATTRIBUTE<T> parseOptionalAttribute( wxXmlNode* aNode, const wxString& aAttribute )
{
return OPTIONAL_XML_ATTRIBUTE<T>( aNode->GetAttribute( aAttribute ) );
}
NODE_MAP MapChildren( wxXmlNode* aCurrentNode )
{
// Map node_name -> node_pointer
NODE_MAP nodesMap;
// Loop through all children mapping them in nodesMap
if( aCurrentNode )
aCurrentNode = aCurrentNode->GetChildren();
while( aCurrentNode )
{
// Create a new pair in the map
// key: current node name
// value: current node pointer
nodesMap[aCurrentNode->GetName()] = aCurrentNode;
// Get next child
aCurrentNode = aCurrentNode->GetNext();
}
return nodesMap;
}
VECTOR2I ConvertArcCenter( const VECTOR2I& aStart, const VECTOR2I& aEnd, double aAngle )
{
// Eagle give us start and end.
// S_ARC wants start to give the center, and end to give the start.
double dx = aEnd.x - aStart.x, dy = aEnd.y - aStart.y;
VECTOR2I mid = ( aStart + aEnd ) / 2;
double dlen = sqrt( dx*dx + dy*dy );
if( !std::isnormal( dlen ) || !std::isnormal( aAngle ) )
{
THROW_IO_ERROR( wxString::Format( _( "Invalid Arc with radius %f and angle %f" ),
dlen,
aAngle ) );
}
double dist = dlen / ( 2 * tan( DEG2RAD( aAngle ) / 2 ) );
VECTOR2I center(
mid.x + dist * ( dy / dlen ),
mid.y - dist * ( dx / dlen )
);
return center;
}
static int parseAlignment( const wxString& aAlignment )
{
// (bottom-left | bottom-center | bottom-right | center-left |
// center | center-right | top-left | top-center | top-right)
if( aAlignment == "center" )
return ETEXT::CENTER;
else if( aAlignment == "center-right" )
return ETEXT::CENTER_RIGHT;
else if( aAlignment == "top-left" )
return ETEXT::TOP_LEFT;
else if( aAlignment == "top-center" )
return ETEXT::TOP_CENTER;
else if( aAlignment == "top-right" )
return ETEXT::TOP_RIGHT;
else if( aAlignment == "bottom-left" )
return ETEXT::BOTTOM_LEFT;
else if( aAlignment == "bottom-center" )
return ETEXT::BOTTOM_CENTER;
else if( aAlignment == "bottom-right" )
return ETEXT::BOTTOM_RIGHT;
else if( aAlignment == "center-left" )
return ETEXT::CENTER_LEFT;
return DEFAULT_ALIGNMENT;
}
EWIRE::EWIRE( wxXmlNode* aWire )
{
/*
<!ELEMENT wire EMPTY>
<!ATTLIST wire
x1 %Coord; #REQUIRED
y1 %Coord; #REQUIRED
x2 %Coord; #REQUIRED
y2 %Coord; #REQUIRED
width %Dimension; #REQUIRED
layer %Layer; #REQUIRED
extent %Extent; #IMPLIED -- only applicable for airwires --
style %WireStyle; "continuous"
curve %WireCurve; "0"
cap %WireCap; "round" -- only applicable if 'curve' is not zero --
>
*/
x1 = parseRequiredAttribute<ECOORD>( aWire, "x1" );
y1 = parseRequiredAttribute<ECOORD>( aWire, "y1" );
x2 = parseRequiredAttribute<ECOORD>( aWire, "x2" );
y2 = parseRequiredAttribute<ECOORD>( aWire, "y2" );
width = parseRequiredAttribute<ECOORD>( aWire, "width" );
layer = parseRequiredAttribute<int>( aWire, "layer" );
curve = parseOptionalAttribute<double>( aWire, "curve" );
opt_wxString s = parseOptionalAttribute<wxString>( aWire, "style" );
if( s == "continuous" )
style = EWIRE::CONTINUOUS;
else if( s == "longdash" )
style = EWIRE::LONGDASH;
else if( s == "shortdash" )
style = EWIRE::SHORTDASH;
else if( s == "dashdot" )
style = EWIRE::DASHDOT;
s = parseOptionalAttribute<wxString>( aWire, "cap" );
if( s == "round" )
cap = EWIRE::ROUND;
else if( s == "flat" )
cap = EWIRE::FLAT;
}
EJUNCTION::EJUNCTION( wxXmlNode* aJunction )
{
/*
<!ELEMENT junction EMPTY>
<!ATTLIST junction
x %Coord; #REQUIRED
y %Coord; #REQUIRED
>
*/
x = parseRequiredAttribute<ECOORD>( aJunction, "x" );
y = parseRequiredAttribute<ECOORD>( aJunction, "y" );
}
ELABEL::ELABEL( wxXmlNode* aLabel, const wxString& aNetName )
{
/*
<!ELEMENT label EMPTY>
<!ATTLIST label
x %Coord; #REQUIRED
y %Coord; #REQUIRED
size %Dimension; #REQUIRED
layer %Layer; #REQUIRED
font %TextFont; "proportional"
ratio %Int; "8"
rot %Rotation; "R0"
xref %Bool; "no"
>
*/
x = parseRequiredAttribute<ECOORD>( aLabel, "x" );
y = parseRequiredAttribute<ECOORD>( aLabel, "y" );
size = parseRequiredAttribute<ECOORD>( aLabel, "size" );
layer = parseRequiredAttribute<int>( aLabel, "layer" );
rot = parseOptionalAttribute<EROT>( aLabel, "rot" );
xref = parseOptionalAttribute<wxString>( aLabel, "xref" );
netname = aNetName;
}
EVIA::EVIA( wxXmlNode* aVia )
{
/*
<!ELEMENT via EMPTY>
<!ATTLIST via
x %Coord; #REQUIRED
y %Coord; #REQUIRED
extent %Extent; #REQUIRED
drill %Dimension; #REQUIRED
diameter %Dimension; "0"
shape %ViaShape; "round"
alwaysstop %Bool; "no"
>
*/
x = parseRequiredAttribute<ECOORD>( aVia, "x" );
y = parseRequiredAttribute<ECOORD>( aVia, "y" );
wxString ext = parseRequiredAttribute<wxString>( aVia, "extent" );
sscanf( ext.c_str(), "%d-%d", &layer_front_most, &layer_back_most );
drill = parseRequiredAttribute<ECOORD>( aVia, "drill" );
diam = parseOptionalAttribute<ECOORD>( aVia, "diameter" );
shape = parseOptionalAttribute<wxString>( aVia, "shape" );
}
ECIRCLE::ECIRCLE( wxXmlNode* aCircle )
{
/*
<!ELEMENT circle EMPTY>
<!ATTLIST circle
x %Coord; #REQUIRED
y %Coord; #REQUIRED
radius %Coord; #REQUIRED
width %Dimension; #REQUIRED
layer %Layer; #REQUIRED
>
*/
x = parseRequiredAttribute<ECOORD>( aCircle, "x" );
y = parseRequiredAttribute<ECOORD>( aCircle, "y" );
radius = parseRequiredAttribute<ECOORD>( aCircle, "radius" );
width = parseRequiredAttribute<ECOORD>( aCircle, "width" );
layer = parseRequiredAttribute<int>( aCircle, "layer" );
}
ERECT::ERECT( wxXmlNode* aRect )
{
/*
<!ELEMENT rectangle EMPTY>
<!ATTLIST rectangle
x1 %Coord; #REQUIRED
y1 %Coord; #REQUIRED
x2 %Coord; #REQUIRED
y2 %Coord; #REQUIRED
layer %Layer; #REQUIRED
rot %Rotation; "R0"
>
*/
x1 = parseRequiredAttribute<ECOORD>( aRect, "x1" );
y1 = parseRequiredAttribute<ECOORD>( aRect, "y1" );
x2 = parseRequiredAttribute<ECOORD>( aRect, "x2" );
y2 = parseRequiredAttribute<ECOORD>( aRect, "y2" );
layer = parseRequiredAttribute<int>( aRect, "layer" );
rot = parseOptionalAttribute<EROT>( aRect, "rot" );
}
EATTR::EATTR( wxXmlNode* aTree )
{
/*
<!ELEMENT attribute EMPTY>
<!ATTLIST attribute
name %String; #REQUIRED
value %String; #IMPLIED
x %Coord; #IMPLIED
y %Coord; #IMPLIED
size %Dimension; #IMPLIED
layer %Layer; #IMPLIED
font %TextFont; #IMPLIED
ratio %Int; #IMPLIED
rot %Rotation; "R0"
display %AttributeDisplay; "value" -- only in <element> or <instance> context --
constant %Bool; "no" -- only in <device> context --
>
*/
name = parseRequiredAttribute<wxString>( aTree, "name" );
value = parseOptionalAttribute<wxString>( aTree, "value" );
x = parseOptionalAttribute<ECOORD>( aTree, "x" );
y = parseOptionalAttribute<ECOORD>( aTree, "y" );
size = parseOptionalAttribute<ECOORD>( aTree, "size" );
layer = parseOptionalAttribute<int>( aTree, "layer" );
ratio = parseOptionalAttribute<double>( aTree, "ratio" );
rot = parseOptionalAttribute<EROT>( aTree, "rot" );
opt_wxString stemp = parseOptionalAttribute<wxString>( aTree, "display" );
// (off | value | name | both)
if( stemp == "off" )
display = EATTR::Off;
else if( stemp == "name" )
display = EATTR::NAME;
else if( stemp == "both" )
display = EATTR::BOTH;
else // "value" is the default
display = EATTR::VALUE;
stemp = parseOptionalAttribute<wxString>( aTree, "align" );
align = stemp ? parseAlignment( *stemp ) : DEFAULT_ALIGNMENT;
}
EDIMENSION::EDIMENSION( wxXmlNode* aDimension )
{
/*
<!ELEMENT dimension EMPTY>
<!ATTLIST dimension
x1 %Coord; #REQUIRED
y1 %Coord; #REQUIRED
x2 %Coord; #REQUIRED
y2 %Coord; #REQUIRED
x3 %Coord; #REQUIRED
y3 %Coord; #REQUIRED
textsize %Coord;
layer %Layer; #REQUIRED
dtype %DimensionType; "parallel"
>
*/
x1 = parseRequiredAttribute<ECOORD>( aDimension, wxT( "x1" ) );
y1 = parseRequiredAttribute<ECOORD>( aDimension, wxT( "y1" ) );
x2 = parseRequiredAttribute<ECOORD>( aDimension, wxT( "x2" ) );
y2 = parseRequiredAttribute<ECOORD>( aDimension, wxT( "y2" ) );
x3 = parseRequiredAttribute<ECOORD>( aDimension, wxT( "x3" ) );
y3 = parseRequiredAttribute<ECOORD>( aDimension, wxT( "y3" ) );
textsize = parseOptionalAttribute<ECOORD>( aDimension, wxT( "textsize" ) );
layer = parseRequiredAttribute<int>( aDimension, wxT( "layer" ) );
dimensionType = parseOptionalAttribute<wxString>( aDimension, wxT( "dtype" ) );
}
ETEXT::ETEXT( wxXmlNode* aText )
{
/*
<!ELEMENT text (#PCDATA)>
<!ATTLIST text
x %Coord; #REQUIRED
y %Coord; #REQUIRED
size %Dimension; #REQUIRED
layer %Layer; #REQUIRED
font %TextFont; "proportional"
ratio %Int; "8"
rot %Rotation; "R0"
align %Align; "bottom-left"
>
*/
text = aText->GetNodeContent();
x = parseRequiredAttribute<ECOORD>( aText, "x" );
y = parseRequiredAttribute<ECOORD>( aText, "y" );
size = parseRequiredAttribute<ECOORD>( aText, "size" );
layer = parseRequiredAttribute<int>( aText, "layer" );
font = parseOptionalAttribute<wxString>( aText, "font" );
ratio = parseOptionalAttribute<double>( aText, "ratio" );
rot = parseOptionalAttribute<EROT>( aText, "rot" );
opt_wxString stemp = parseOptionalAttribute<wxString>( aText, "align" );
align = stemp ? parseAlignment( *stemp ) : DEFAULT_ALIGNMENT;
}
wxSize ETEXT::ConvertSize() const
{
wxSize textsize;
if( font )
{
const wxString& fontName = font.CGet();
if( fontName == "vector" )
{
textsize = wxSize( size.ToSchUnits(), size.ToSchUnits() );
}
else if( fontName == "fixed" )
{
textsize = wxSize( size.ToSchUnits(), size.ToSchUnits() * 0.80 );
}
else
{
textsize = wxSize( size.ToSchUnits(), size.ToSchUnits() );
}
}
else
{
textsize = wxSize( size.ToSchUnits() * 0.85, size.ToSchUnits() );
}
return textsize;
}
EFRAME::EFRAME( wxXmlNode* aFrameNode )
{
/*
* <!ELEMENT frame EMPTY>
* <!ATTLIST frame
* x1 %Coord; #REQUIRED
* y1 %Coord; #REQUIRED
* x2 %Coord; #REQUIRED
* y2 %Coord; #REQUIRED
* columns %Int; #REQUIRED
* rows %Int; #REQUIRED
* layer %Layer; #REQUIRED
* border-left %Bool; "yes"
* border-top %Bool; "yes"
* border-right %Bool; "yes"
* border-bottom %Bool; "yes"
* >
*/
border_left = true;
border_top = true;
border_right = true;
border_bottom = true;
x1 = parseRequiredAttribute<ECOORD>( aFrameNode, "x1" );
y1 = parseRequiredAttribute<ECOORD>( aFrameNode, "y1" );
x2 = parseRequiredAttribute<ECOORD>( aFrameNode, "x2" );
y2 = parseRequiredAttribute<ECOORD>( aFrameNode, "y2" );
columns = parseRequiredAttribute<int>( aFrameNode, "columns" );
rows = parseRequiredAttribute<int>( aFrameNode, "rows" );
layer = parseRequiredAttribute<int>( aFrameNode, "layer" );
border_left = parseOptionalAttribute<bool>( aFrameNode, "border-left" );
border_top = parseOptionalAttribute<bool>( aFrameNode, "border-top" );
border_right = parseOptionalAttribute<bool>( aFrameNode, "border-right" );
border_bottom = parseOptionalAttribute<bool>( aFrameNode, "border-bottom" );
}
EPAD_COMMON::EPAD_COMMON( wxXmlNode* aPad )
{
// #REQUIRED says DTD, throw exception if not found
name = parseRequiredAttribute<wxString>( aPad, "name" );
x = parseRequiredAttribute<ECOORD>( aPad, "x" );
y = parseRequiredAttribute<ECOORD>( aPad, "y" );
rot = parseOptionalAttribute<EROT>( aPad, "rot" );
stop = parseOptionalAttribute<bool>( aPad, "stop" );
thermals = parseOptionalAttribute<bool>( aPad, "thermals" );
}
EPAD::EPAD( wxXmlNode* aPad )
: EPAD_COMMON( aPad )
{
/*
<!ELEMENT pad EMPTY>
<!ATTLIST pad
name %String; #REQUIRED
x %Coord; #REQUIRED
y %Coord; #REQUIRED
drill %Dimension; #REQUIRED
diameter %Dimension; "0"
shape %PadShape; "round"
rot %Rotation; "R0"
stop %Bool; "yes"
thermals %Bool; "yes"
first %Bool; "no"
>
*/
// #REQUIRED says DTD, throw exception if not found
drill = parseRequiredAttribute<ECOORD>( aPad, "drill" );
// Optional attributes
diameter = parseOptionalAttribute<ECOORD>( aPad, "diameter" );
opt_wxString s = parseOptionalAttribute<wxString>( aPad, "shape" );
// (square | round | octagon | long | offset)
if( s == "square" )
shape = EPAD::SQUARE;
else if( s == "round" )
shape = EPAD::ROUND;
else if( s == "octagon" )
shape = EPAD::OCTAGON;
else if( s == "long" )
shape = EPAD::LONG;
else if( s == "offset" )
shape = EPAD::OFFSET;
first = parseOptionalAttribute<bool>( aPad, "first" );
}
ESMD::ESMD( wxXmlNode* aSMD )
: EPAD_COMMON( aSMD )
{
/*
<!ATTLIST smd
name %String; #REQUIRED
x %Coord; #REQUIRED
y %Coord; #REQUIRED
dx %Dimension; #REQUIRED
dy %Dimension; #REQUIRED
layer %Layer; #REQUIRED
roundness %Int; "0"
rot %Rotation; "R0"
stop %Bool; "yes"
thermals %Bool; "yes"
cream %Bool; "yes"
>
*/
// DTD #REQUIRED, throw exception if not found
dx = parseRequiredAttribute<ECOORD>( aSMD, "dx" );
dy = parseRequiredAttribute<ECOORD>( aSMD, "dy" );
layer = parseRequiredAttribute<int>( aSMD, "layer" );
roundness = parseOptionalAttribute<int>( aSMD, "roundness" );
cream = parseOptionalAttribute<bool>( aSMD, "cream" );
}
EPIN::EPIN( wxXmlNode* aPin )
{
/*
<!ELEMENT pin EMPTY>
<!ATTLIST pin
name %String; #REQUIRED
x %Coord; #REQUIRED
y %Coord; #REQUIRED
visible %PinVisible; "both"
length %PinLength; "long"
direction %PinDirection; "io"
function %PinFunction; "none"
swaplevel %Int; "0"
rot %Rotation; "R0"
>
*/
// DTD #REQUIRED, throw exception if not found
name = parseRequiredAttribute<wxString>( aPin, "name" );
x = parseRequiredAttribute<ECOORD>( aPin, "x" );
y = parseRequiredAttribute<ECOORD>( aPin, "y" );
visible = parseOptionalAttribute<wxString>( aPin, "visible" );
length = parseOptionalAttribute<wxString>( aPin, "length" );
direction = parseOptionalAttribute<wxString>( aPin, "direction" );
function = parseOptionalAttribute<wxString>( aPin, "function" );
swaplevel = parseOptionalAttribute<int>( aPin, "swaplevel" );
rot = parseOptionalAttribute<EROT>( aPin, "rot" );
}
EVERTEX::EVERTEX( wxXmlNode* aVertex )
{
/*
<!ELEMENT vertex EMPTY>
<!ATTLIST vertex
x %Coord; #REQUIRED
y %Coord; #REQUIRED
curve %WireCurve; "0" -- the curvature from this vertex to the next one --
>
*/
x = parseRequiredAttribute<ECOORD>( aVertex, "x" );
y = parseRequiredAttribute<ECOORD>( aVertex, "y" );
curve = parseOptionalAttribute<double>( aVertex, "curve" );
}
EPOLYGON::EPOLYGON( wxXmlNode* aPolygon )
{
/*
<!ATTLIST polygon
width %Dimension; #REQUIRED
layer %Layer; #REQUIRED
spacing %Dimension; #IMPLIED
pour %PolygonPour; "solid"
isolate %Dimension; #IMPLIED -- only in <signal> or <package> context --
orphans %Bool; "no" -- only in <signal> context --
thermals %Bool; "yes" -- only in <signal> context --
rank %Int; "0" -- 1..6 in <signal> context, 0 or 7 in
<package> context --
>
*/
width = parseRequiredAttribute<ECOORD>( aPolygon, "width" );
layer = parseRequiredAttribute<int>( aPolygon, "layer" );
spacing = parseOptionalAttribute<ECOORD>( aPolygon, "spacing" );
isolate = parseOptionalAttribute<ECOORD>( aPolygon, "isolate" );
opt_wxString s = parseOptionalAttribute<wxString>( aPolygon, "pour" );
// default pour to solid fill
pour = EPOLYGON::SOLID;
// (solid | hatch | cutout)
if( s == "hatch" )
pour = EPOLYGON::HATCH;
else if( s == "cutout" )
pour = EPOLYGON::CUTOUT;
orphans = parseOptionalAttribute<bool>( aPolygon, "orphans" );
thermals = parseOptionalAttribute<bool>( aPolygon, "thermals" );
rank = parseOptionalAttribute<int>( aPolygon, "rank" );
}
EHOLE::EHOLE( wxXmlNode* aHole )
{
/*
<!ELEMENT hole EMPTY>
<!ATTLIST hole
x %Coord; #REQUIRED
y %Coord; #REQUIRED
drill %Dimension; #REQUIRED
>
*/
// #REQUIRED:
x = parseRequiredAttribute<ECOORD>( aHole, "x" );
y = parseRequiredAttribute<ECOORD>( aHole, "y" );
drill = parseRequiredAttribute<ECOORD>( aHole, "drill" );
}
EELEMENT::EELEMENT( wxXmlNode* aElement )
{
/*
<!ELEMENT element (attribute*, variant*)>
<!ATTLIST element
name %String; #REQUIRED
library %String; #REQUIRED
package %String; #REQUIRED
value %String; #REQUIRED
x %Coord; #REQUIRED
y %Coord; #REQUIRED
locked %Bool; "no"
smashed %Bool; "no"
rot %Rotation; "R0"
>
*/
// #REQUIRED
name = parseRequiredAttribute<wxString>( aElement, "name" );
library = parseRequiredAttribute<wxString>( aElement, "library" );
value = parseRequiredAttribute<wxString>( aElement, "value" );
std::string p = parseRequiredAttribute<std::string>( aElement, "package" );
ReplaceIllegalFileNameChars( &p, '_' );
package = wxString::FromUTF8( p.c_str() );
x = parseRequiredAttribute<ECOORD>( aElement, "x" );
y = parseRequiredAttribute<ECOORD>( aElement, "y" );
// optional
locked = parseOptionalAttribute<bool>( aElement, "locked" );
smashed = parseOptionalAttribute<bool>( aElement, "smashed" );
rot = parseOptionalAttribute<EROT>( aElement, "rot" );
}
ELAYER::ELAYER( wxXmlNode* aLayer )
{
/*
<!ELEMENT layer EMPTY>
<!ATTLIST layer
number %Layer; #REQUIRED
name %String; #REQUIRED
color %Int; #REQUIRED
fill %Int; #REQUIRED
visible %Bool; "yes"
active %Bool; "yes"
>
*/
number = parseRequiredAttribute<int>( aLayer, "number" );
name = parseRequiredAttribute<wxString>( aLayer, "name" );
color = parseRequiredAttribute<int>( aLayer, "color" );
fill = 1; // Temporary value.
visible = parseOptionalAttribute<bool>( aLayer, "visible" );
active = parseOptionalAttribute<bool>( aLayer, "active" );
}
EPART::EPART( wxXmlNode* aPart )
{
/*
* <!ELEMENT part (attribute*, variant*)>
* <!ATTLIST part
* name %String; #REQUIRED
* library %String; #REQUIRED
* deviceset %String; #REQUIRED
* device %String; #REQUIRED
* technology %String; ""
* value %String; #IMPLIED
* >
*/
// #REQUIRED
name = parseRequiredAttribute<wxString>( aPart, "name" );
library = parseRequiredAttribute<wxString>( aPart, "library" );
deviceset = parseRequiredAttribute<wxString>( aPart, "deviceset" );
device = parseRequiredAttribute<wxString>( aPart, "device" );
technology = parseOptionalAttribute<wxString>( aPart, "technology" );
value = parseOptionalAttribute<wxString>( aPart, "value" );
for( wxXmlNode* child = aPart->GetChildren(); child; child = child->GetNext() )
{
if( child->GetName() == "attribute" )
{
std::string aname, avalue;
for( wxXmlAttribute* x = child->GetAttributes(); x; x = x->GetNext() )
{
if( x->GetName() == "name" )
aname = x->GetValue();
else if( x->GetName() == "value" )
avalue = x->GetValue();
}
if( aname.size() && avalue.size() )
attribute[aname] = avalue;
}
else if( child->GetName() == "variant" )
{
std::string aname, avalue;
for( wxXmlAttribute* x = child->GetAttributes(); x; x = x->GetNext() )
{
if( x->GetName() == "name" )
aname = x->GetValue();
else if( x->GetName() == "value" )
avalue = x->GetValue();
}
if( aname.size() && avalue.size() )
variant[aname] = avalue;
}
}
}
EINSTANCE::EINSTANCE( wxXmlNode* aInstance )
{
/*
* <!ELEMENT instance (attribute)*>
* <!ATTLIST instance
* part %String; #REQUIRED
* gate %String; #REQUIRED
* x %Coord; #REQUIRED
* y %Coord; #REQUIRED
* smashed %Bool; "no"
* rot %Rotation; "R0"
* >
*/
part = parseRequiredAttribute<wxString>( aInstance, "part" );
gate = parseRequiredAttribute<wxString>( aInstance, "gate" );
x = parseRequiredAttribute<ECOORD>( aInstance, "x" );
y = parseRequiredAttribute<ECOORD>( aInstance, "y" );
// optional
smashed = parseOptionalAttribute<bool>( aInstance, "smashed" );
rot = parseOptionalAttribute<EROT>( aInstance, "rot" );
}
EGATE::EGATE( wxXmlNode* aGate )
{
/*
* <!ELEMENT gate EMPTY>
* <!ATTLIST gate
* name %String; #REQUIRED
* symbol %String; #REQUIRED
* x %Coord; #REQUIRED
* y %Coord; #REQUIRED
* addlevel %GateAddLevel; "next"
* swaplevel %Int; "0"
* >
*/
name = parseRequiredAttribute<wxString>( aGate, "name" );
symbol = parseRequiredAttribute<wxString>( aGate, "symbol" );
x = parseRequiredAttribute<ECOORD>( aGate, "x" );
y = parseRequiredAttribute<ECOORD>( aGate, "y" );
opt_wxString stemp = parseOptionalAttribute<wxString>( aGate, "addlevel" );
// (off | value | name | both)
if( stemp == "must" )
addlevel = EGATE::MUST;
else if( stemp == "can" )
addlevel = EGATE::CAN;
else if( stemp == "next" )
addlevel = EGATE::NEXT;
else if( stemp == "request" )
addlevel = EGATE::REQUEST;
else if( stemp == "always" )
addlevel = EGATE::ALWAYS;
else
addlevel = EGATE::NEXT;
}
ECONNECT::ECONNECT( wxXmlNode* aConnect )
{
/*
* <!ELEMENT connect EMPTY>
* <!ATTLIST connect
* gate %String; #REQUIRED
* pin %String; #REQUIRED
* pad %String; #REQUIRED
* route %ContactRoute; "all"
* >
*/
gate = parseRequiredAttribute<wxString>( aConnect, "gate" );
pin = parseRequiredAttribute<wxString>( aConnect, "pin" );
pad = parseRequiredAttribute<wxString>( aConnect, "pad" );
}
EDEVICE::EDEVICE( wxXmlNode* aDevice )
{
/*
<!ELEMENT device (connects?, technologies?)>
<!ATTLIST device
name %String; ""
package %String; #IMPLIED
>
*/
name = parseRequiredAttribute<wxString>( aDevice, "name" );
opt_wxString pack = parseOptionalAttribute<wxString>( aDevice, "package" );
if( pack )
{
std::string p( pack->c_str() );
ReplaceIllegalFileNameChars( &p, '_' );
package.Set( wxString::FromUTF8( p.c_str() ) );
}
NODE_MAP aDeviceChildren = MapChildren( aDevice );
wxXmlNode* connectNode = getChildrenNodes( aDeviceChildren, "connects" );
while( connectNode )
{
connects.emplace_back( connectNode );
connectNode = connectNode->GetNext();
}
}
EDEVICE_SET::EDEVICE_SET( wxXmlNode* aDeviceSet )
{
/*
<!ELEMENT deviceset (description?, gates, devices)>
<!ATTLIST deviceset
name %String; #REQUIRED
prefix %String; ""
uservalue %Bool; "no"
>
*/
name = parseRequiredAttribute<wxString>( aDeviceSet, "name" );
prefix = parseOptionalAttribute<wxString>( aDeviceSet, "prefix" );
uservalue = parseOptionalAttribute<bool>( aDeviceSet, "uservalue" );
}
ECLASS::ECLASS( wxXmlNode* aClass )
{
number = parseRequiredAttribute<wxString>( aClass, "number" );
name = parseRequiredAttribute<wxString>( aClass, "name" );
for( wxXmlNode* child = aClass->GetChildren(); child; child = child->GetNext() )
{
if( child->GetName() == "clearance" )
{
wxString to = parseRequiredAttribute<wxString>( child, "class" );
ECOORD value = parseRequiredAttribute<ECOORD>( child, "value" );
clearanceMap[to] = value;
}
}
}