diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index e119d4b7e9..0c5d1beb4f 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -239,6 +239,7 @@ set( COMMON_SRCS draw_panel.cpp drawtxt.cpp dsnlexer.cpp + eagle_parser.cpp eda_dde.cpp eda_doc.cpp eda_pattern_match.cpp diff --git a/common/eagle_parser.cpp b/common/eagle_parser.cpp new file mode 100644 index 0000000000..54f3c41955 --- /dev/null +++ b/common/eagle_parser.cpp @@ -0,0 +1,649 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck + * Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2017 CERN. + * @author Alejandro García Montoro + * + * 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 + +#include +#include +#include + +#include + +using std::string; + +// Template specializations below parse wxString to the used types: +// - string +// - double +// - int +// - bool +// - EROT + + +template<> +string Convert( wxString aValue ) +{ + return aValue.ToStdString(); +} + + +template <> +double Convert( wxString aValue ) +{ + double value; + + if( aValue.ToDouble( &value ) ) + return value; + else + throw XML_PARSER_ERROR( "Conversion to double failed. Original value: '" + + aValue.ToStdString() + "'." ); +} + + +template <> +int Convert( wxString aValue ) +{ + if( aValue.IsEmpty() ) + throw XML_PARSER_ERROR( "Conversion to int failed. Original value is empty." ); + + return wxAtoi( aValue ); +} + + +template <> +bool Convert( 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. Examples: "R90", "MR180", "SR180" +template<> +EROT Convert( 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' + NULL ); + + return value; +} + +/** + * Function parseRequiredAttribute + * parsese the aAttribute of the XML node 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 +T parseRequiredAttribute( wxXmlNode* aNode, string aAttribute ) +{ + wxString value; + + if( aNode->GetAttribute( aAttribute, &value ) ) + return Convert( value ); + else + throw XML_PARSER_ERROR( "The required attribute " + aAttribute + " is missing." ); +} + +/** + * Function parseOptionalAttribute + * parses the aAttribute of the XML node aNode. + * @param aNode is the node whose attribute will be parsed. + * @param aAttribute is the attribute that will be parsed. + * @return OPTIONAL_XML_ATTRIBUTE - an optional XML attribute, parsed as the specified type if + * found. + */ +template +OPTIONAL_XML_ATTRIBUTE parseOptionalAttribute( wxXmlNode* aNode, string aAttribute ) +{ + return OPTIONAL_XML_ATTRIBUTE( aNode->GetAttribute( aAttribute ) ); +} + + +EWIRE::EWIRE( wxXmlNode* aWire ) +{ + /* + + + */ + + x1 = parseRequiredAttribute( aWire, "x1" ); + y1 = parseRequiredAttribute( aWire, "y1" ); + x2 = parseRequiredAttribute( aWire, "x2" ); + y2 = parseRequiredAttribute( aWire, "y2" ); + width = parseRequiredAttribute( aWire, "width" ); + layer = parseRequiredAttribute( aWire, "layer" ); + curve = parseOptionalAttribute( aWire, "curve" ); + + opt_string s = parseOptionalAttribute( 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( aWire, "cap" ); + + if( s == "round" ) + cap = EWIRE::ROUND; + else if( s == "flat" ) + cap = EWIRE::FLAT; +} + + +EVIA::EVIA( wxXmlNode* aVia ) +{ + /* + + + */ + + x = parseRequiredAttribute( aVia, "x" ); + y = parseRequiredAttribute( aVia, "y" ); + + string ext = parseRequiredAttribute( aVia, "extent" ); + + sscanf( ext.c_str(), "%d-%d", &layer_front_most, &layer_back_most ); + + drill = parseRequiredAttribute( aVia, "drill" ); + diam = parseOptionalAttribute( aVia, "diameter" ); + shape = parseOptionalAttribute( aVia, "shape" ); +} + + +ECIRCLE::ECIRCLE( wxXmlNode* aCircle ) +{ + /* + + + */ + + x = parseRequiredAttribute( aCircle, "x" ); + y = parseRequiredAttribute( aCircle, "y" ); + radius = parseRequiredAttribute( aCircle, "radius" ); + width = parseRequiredAttribute( aCircle, "width" ); + layer = parseRequiredAttribute( aCircle, "layer" ); +} + + +ERECT::ERECT( wxXmlNode* aRect ) +{ + /* + + + */ + + x1 = parseRequiredAttribute( aRect, "x1" ); + y1 = parseRequiredAttribute( aRect, "y1" ); + x2 = parseRequiredAttribute( aRect, "x2" ); + y2 = parseRequiredAttribute( aRect, "y2" ); + layer = parseRequiredAttribute( aRect, "layer" ); + rot = parseOptionalAttribute( aRect, "rot" ); +} + + +EATTR::EATTR( wxXmlNode* aTree ) +{ + /* + + or context -- + constant %Bool; "no" -- only in context -- + > + */ + + name = parseRequiredAttribute( aTree, "name" ); + value = parseOptionalAttribute( aTree, "value" ); + + x = parseOptionalAttribute( aTree, "x" ); + y = parseOptionalAttribute( aTree, "y" ); + size = parseOptionalAttribute( aTree, "size" ); + + // KiCad cannot currently put a TEXTE_MODULE on a different layer than the MODULE + // Eagle can it seems. + layer = parseOptionalAttribute( aTree, "layer" ); + ratio = parseOptionalAttribute( aTree, "ratio" ); + rot = parseOptionalAttribute( aTree, "rot" ); + + opt_string stemp = parseOptionalAttribute( aTree, "display" ); + + // (off | value | name | both) + if( stemp == "off" ) + display = EATTR::Off; + else if( stemp == "value" ) + display = EATTR::VALUE; + else if( stemp == "name" ) + display = EATTR::NAME; + else if( stemp == "both" ) + display = EATTR::BOTH; +} + + +EDIMENSION::EDIMENSION( wxXmlNode* aDimension ) +{ + /* + + + */ + + x1 = parseRequiredAttribute( aDimension, "x1" ); + y1 = parseRequiredAttribute( aDimension, "y1" ); + x2 = parseRequiredAttribute( aDimension, "x2" ); + y2 = parseRequiredAttribute( aDimension, "y2" ); + x3 = parseRequiredAttribute( aDimension, "x3" ); + y3 = parseRequiredAttribute( aDimension, "y3" ); + layer = parseRequiredAttribute( aDimension, "layer" ); + + opt_string dimType = parseOptionalAttribute( aDimension, "dtype" ); + + if( !dimType ) + { + // default type is parallel + } +} + + +ETEXT::ETEXT( wxXmlNode* aText ) +{ + /* + + + */ + + text = aText->GetNodeContent(); + x = parseRequiredAttribute( aText, "x" ); + y = parseRequiredAttribute( aText, "y" ); + size = parseRequiredAttribute( aText, "size" ); + layer = parseRequiredAttribute( aText, "layer" ); + + font = parseOptionalAttribute( aText, "font" ); + ratio = parseOptionalAttribute( aText, "ratio" ); + rot = parseOptionalAttribute( aText, "rot" ); + + opt_string stemp = parseOptionalAttribute( aText, "align" ); + + // (bottom-left | bottom-center | bottom-right | center-left | + // center | center-right | top-left | top-center | top-right) + if( stemp == "center" ) + align = ETEXT::CENTER; + else if( stemp == "center-right" ) + align = ETEXT::CENTER_RIGHT; + else if( stemp == "top-left" ) + align = ETEXT::TOP_LEFT; + else if( stemp == "top-center" ) + align = ETEXT::TOP_CENTER; + else if( stemp == "top-right" ) + align = ETEXT::TOP_RIGHT; + else if( stemp == "bottom-left" ) + align = ETEXT::BOTTOM_LEFT; + else if( stemp == "bottom-center" ) + align = ETEXT::BOTTOM_CENTER; + else if( stemp == "bottom-right" ) + align = ETEXT::BOTTOM_RIGHT; + else if( stemp == "center-left" ) + align = ETEXT::CENTER_LEFT; +} + + +EPAD::EPAD( wxXmlNode* aPad ) +{ + /* + + + */ + + // #REQUIRED says DTD, throw exception if not found + name = parseRequiredAttribute( aPad, "name" ); + x = parseRequiredAttribute( aPad, "x" ); + y = parseRequiredAttribute( aPad, "y" ); + drill = parseRequiredAttribute( aPad, "drill" ); + + // Optional attributes + diameter = parseOptionalAttribute( aPad, "diameter" ); + + opt_string s = parseOptionalAttribute( 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; + + rot = parseOptionalAttribute( aPad, "rot" ); + stop = parseOptionalAttribute( aPad, "stop" ); + thermals = parseOptionalAttribute( aPad, "thermals" ); + first = parseOptionalAttribute( aPad, "first" ); +} + + +ESMD::ESMD( wxXmlNode* aSMD ) +{ + /* + + */ + + // DTD #REQUIRED, throw exception if not found + name = parseRequiredAttribute( aSMD, "name" ); + x = parseRequiredAttribute( aSMD, "x" ); + y = parseRequiredAttribute( aSMD, "y" ); + dx = parseRequiredAttribute( aSMD, "dx" ); + dy = parseRequiredAttribute( aSMD, "dy" ); + layer = parseRequiredAttribute( aSMD, "layer" ); + + roundness = parseOptionalAttribute( aSMD, "roundness" ); + rot = parseOptionalAttribute( aSMD, "rot" ); + thermals = parseOptionalAttribute( aSMD, "thermals" ); + stop = parseOptionalAttribute( aSMD, "stop" ); + thermals = parseOptionalAttribute( aSMD, "thermals" ); + cream = parseOptionalAttribute( aSMD, "cream" ); +} + + +EVERTEX::EVERTEX( wxXmlNode* aVertex ) +{ + /* + + + */ + + x = parseRequiredAttribute( aVertex, "x" ); + y = parseRequiredAttribute( aVertex, "y" ); +} + + +EPOLYGON::EPOLYGON( wxXmlNode* aPolygon ) +{ + /* + or context -- + orphans %Bool; "no" -- only in context -- + thermals %Bool; "yes" -- only in context -- + rank %Int; "0" -- 1..6 in context, 0 or 7 in context -- + > + */ + + width = parseRequiredAttribute( aPolygon, "width" ); + layer = parseRequiredAttribute( aPolygon, "layer" ); + + spacing = parseOptionalAttribute( aPolygon, "spacing" ); + isolate = parseOptionalAttribute( aPolygon, "isolate" ); + opt_string s = parseOptionalAttribute( 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( aPolygon, "orphans" ); + thermals = parseOptionalAttribute( aPolygon, "thermals" ); + rank = parseOptionalAttribute( aPolygon, "rank" ); +} + + +EHOLE::EHOLE( wxXmlNode* aHole ) +{ + /* + + + */ + + // #REQUIRED: + x = parseRequiredAttribute( aHole, "x" ); + y = parseRequiredAttribute( aHole, "y" ); + drill = parseRequiredAttribute( aHole, "drill" ); +} + + +EELEMENT::EELEMENT( wxXmlNode* aElement ) +{ + /* + + + */ + + // #REQUIRED + name = parseRequiredAttribute( aElement, "name" ); + library = parseRequiredAttribute( aElement, "library" ); + value = parseRequiredAttribute( aElement, "value" ); + package = parseRequiredAttribute( aElement, "package" ); + ReplaceIllegalFileNameChars( &package ); + + x = parseRequiredAttribute( aElement, "x" ); + y = parseRequiredAttribute( aElement, "y" ); + + // optional + locked = parseOptionalAttribute( aElement, "locked" ); + smashed = parseOptionalAttribute( aElement, "smashed" ); + rot = parseOptionalAttribute( aElement, "rot" ); +} + + +ELAYER::ELAYER( wxXmlNode* aLayer ) +{ + /* + + + */ + + number = parseRequiredAttribute( aLayer, "number" ); + name = parseRequiredAttribute( aLayer, "name" ); + color = parseRequiredAttribute( aLayer, "color" ); + fill = 1; // Temporary value. + visible = parseOptionalAttribute( aLayer, "visible" ); + active = parseOptionalAttribute( aLayer, "active" ); +} + + +NODE_MAP MapChildren( wxXmlNode* currentNode ) +{ + // Map node_name -> node_pointer + NODE_MAP nodesMap; + + // Loop through all children mapping them in nodesMap + currentNode = currentNode->GetChildren(); + while( currentNode ) + { + // Create a new pair in the map + // key: current node name + // value: current node pointer + nodesMap[currentNode->GetName().ToStdString()] = currentNode; + + // Get next child + currentNode = currentNode->GetNext(); + } + + return nodesMap; +} + + +string makeKey( const string& aFirst, const string& aSecond ) +{ + string key = aFirst + '\x02' + aSecond; + return key; +} + + +unsigned long timeStamp( wxXmlNode* aTree ) +{ + // in this case from a unique tree memory location + return (unsigned long)(void*) aTree; +} + + +wxPoint kicad_arc_center( wxPoint start, wxPoint end, double angle ) +{ + // Eagle give us start and end. + // S_ARC wants start to give the center, and end to give the start. + double dx = end.x - start.x, dy = end.y - start.y; + wxPoint mid = (start + end) / 2; + + double dlen = sqrt( dx*dx + dy*dy ); + double dist = dlen / ( 2 * tan( DEG2RAD( angle ) / 2 ) ); + + wxPoint center( + mid.x + dist * ( dy / dlen ), + mid.y - dist * ( dx / dlen ) + ); + + return center; +} diff --git a/include/eagle_parser.h b/include/eagle_parser.h new file mode 100644 index 0000000000..2e1fdade55 --- /dev/null +++ b/include/eagle_parser.h @@ -0,0 +1,690 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck + * Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2017 CERN. + * @author Alejandro García Montoro + * + * 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 _EAGLE_PARSER_H_ +#define _EAGLE_PARSER_H_ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using std::string; + +class MODULE; + +typedef std::unordered_map< string, wxXmlNode* > NODE_MAP; +typedef std::map< string, MODULE* > MODULE_MAP; + + +/** + * Class XML_PARSER_ERROR + * implements a simple wrapper around runtime_error to isolate the errors thrown by the + * Eagle XML parser. + */ +struct XML_PARSER_ERROR : std::runtime_error +{ + /** + * Constructor XML_PARSER_ERROR + * build an XML error by just calling its parent class constructor, std::runtime_error, with + * the passed message. + * @param aMessage is an explanatory error message. + */ + XML_PARSER_ERROR( const string& aMessage ) noexcept : + std::runtime_error( "XML parser failed - " + aMessage ) + {} +}; + + +/// segment (element) of our XPATH into the Eagle XML document tree in PTREE form. +struct TRIPLET +{ + const char* element; + const char* attribute; + const char* value; + + TRIPLET( const char* aElement, const char* aAttribute = "", const char* aValue = "" ) : + element( aElement ), + attribute( aAttribute ), + value( aValue ) + {} +}; + + +/** + * Class XPATH + * keeps track of what we are working on within a PTREE. + * Then if an exception is thrown, the place within the tree that gave us + * grief can be reported almost accurately. To minimally impact + * speed, merely assign const char* pointers during the tree walking + * expedition. The const char* pointers must be to C strings residing either in + * the data or code segment (i.e. "compiled in") or within the XML document, but + * not on the stack, since the stack is unwound during the throwing of the + * exception. The XML document will not immediately vanish since we capture + * the xpath (using function Contents()) before the XML document tree (PTREE) + * is destroyed. + */ +class XPATH +{ + std::vector p; + +public: + void push( const char* aPathSegment, const char* aAttribute="" ) + { + p.push_back( TRIPLET( aPathSegment, aAttribute ) ); + } + + void clear() { p.clear(); } + + void pop() { p.pop_back(); } + + /// modify the last path node's value + void Value( const char* aValue ) + { + p.back().value = aValue; + } + + /// modify the last path node's attribute + void Attribute( const char* aAttribute ) + { + p.back().attribute = aAttribute; + } + + /// return the contents of the XPATH as a single string + string Contents() + { + typedef std::vector::const_iterator CITER_TRIPLET; + + string ret; + + for( CITER_TRIPLET it = p.begin(); it != p.end(); ++it ) + { + if( it != p.begin() ) + ret += '.'; + + ret += it->element; + + if( it->attribute[0] && it->value[0] ) + { + ret += '['; + ret += it->attribute; + ret += '='; + ret += it->value; + ret += ']'; + } + } + + return ret; + } +}; + + +/** + * Function Convert + * converts a wxString to a generic type T. + * @param aValue is a wxString containing the value that will be converted to type T. + * @throw XML_PARSER_ERROR - an exception is thrown if the parsing fails or if the conversion to + * type T is unknown. + */ +template +T Convert( wxString aValue ) +{ + throw XML_PARSER_ERROR( "Conversion failed. Unknown type." ); +} + + +/** + * Class OPTIONAL_XML_ATTRIBUTE + * models an optional XML attribute. + * This was implemented as an alternative to boost::optional. This class should be replaced with a + * simple typedef per type using std::optional when C++17 is published. + */ +template +class OPTIONAL_XML_ATTRIBUTE +{ +private: + /// A boolean indicating if the data is present or not. + bool m_isAvailable; + + /// The actual data if m_isAvailable is true; otherwise, garbage. + T m_data; + +public: + /** + * Constructor OPTIONAL_XML_ATTRIBUTE + * construct a default OPTIONAL_XML_ATTRIBUTE, whose data is not available. + */ + OPTIONAL_XML_ATTRIBUTE() : + m_isAvailable( false ) + {} + + /** + * Constructor OPTIONAL_XML_ATTRIBUTE + * @param aData is a wxString containing the value that should be converted to type T. If + * aData is empty, the attribute is understood as unavailable; otherwise, the + * conversion to T is tried. + */ + OPTIONAL_XML_ATTRIBUTE( wxString aData ) + { + m_isAvailable = !aData.IsEmpty(); + + if( m_isAvailable ) + Set( aData ); + } + + /** + * Constructor OPTIONAL_XML_ATTRIBUTE + * @param aData is the value of the XML attribute. If this constructor is called, the + * attribute is available. + */ + OPTIONAL_XML_ATTRIBUTE( T aData ) : + m_isAvailable( true ), + m_data( aData ) + {} + + /** + * Operator bool + * @return bool - the availability of the attribute. + */ + operator bool() const + { + return m_isAvailable; + } + + /** + * Assignment operator + * to a string (optionally) containing the data. + * @param aData is a wxString that should be converted to T. If the string is empty, the + * attribute is set to unavailable. + */ + OPTIONAL_XML_ATTRIBUTE& operator =( wxString aData ) + { + m_isAvailable = !aData.IsEmpty(); + + if( m_isAvailable ) + Set( aData ); + + return *this; + } + + /** + * Assignment operator + * to an object of the base type containing the data. + * @param aData is the actual value of the attribute. Calling this assignment, the attribute + * is automatically made available. + */ + OPTIONAL_XML_ATTRIBUTE& operator =( T aData ) + { + m_data = aData; + m_isAvailable = true; + + return *this; + } + + /** + * Equal operator + * to an object of the base type. + * @param aOther is the object of the base type that should be compared with this one. + */ + bool operator ==( const T& aOther ) const + { + return m_isAvailable && ( aOther == m_data ); + } + + /** + * Function Set + * tries to convert a string to the base type. + * @param aString is the string that will be converted to the base type. + */ + void Set( wxString aString ) + { + m_data = Convert( aString ); + } + + /** + * Function Get + * returns a reference to the value of the attribute assuming it is available. + * @return T& - the value of the attribute. + */ + T& Get() + { + assert( m_isAvailable ); + return m_data; + } + + /** + * Function CGet + * returns a constant reference to the value of the attribute assuming it is available. + * @return const T& - the value of the attribute. + */ + const T& CGet() const + { + assert( m_isAvailable ); + return m_data; + } + + /** + * Operator * + * returns a reference to the value of the attribute assuming it is available. + * @return T& - the value of the attribute. + */ + T& operator*() + { + return Get(); + } + + /** + * Operator * + * returns a constant reference to the value of the attribute assuming it is available. + * @return const T& - the value of the attribute. + */ + const T& operator*() const + { + return CGet(); + } + + /** + * Operator -> + * returns a pointer to the value of the attribute assuming it is available. + * @return T* - the value of the attribute. + */ + T* operator->() + { + return &Get(); + } + + /** + * Operator -> + * returns a constant pointer to the value of the attribute assuming it is available. + * @return const T* - the value of the attribute. + */ + const T* operator->() const + { + return &CGet(); + } +}; + +// Pre-declare for typedefs +struct EROT; + +typedef OPTIONAL_XML_ATTRIBUTE opt_string; +typedef OPTIONAL_XML_ATTRIBUTE opt_int; +typedef OPTIONAL_XML_ATTRIBUTE opt_double; +typedef OPTIONAL_XML_ATTRIBUTE opt_bool; +typedef OPTIONAL_XML_ATTRIBUTE opt_erot; + + +// All of the 'E'STRUCTS below merely hold Eagle XML information verbatim, in binary. +// For maintenance and troubleshooting purposes, it was thought that we'd need to +// separate the conversion process into distinct steps. There is no intent to have KiCad +// forms of information in these 'E'STRUCTS. They are only binary forms +// of the Eagle information in the corresponding Eagle XML nodes. + + +/// Eagle net +struct ENET +{ + int netcode; + std::string netname; + + ENET( int aNetCode, const std::string& aNetName ) : + netcode( aNetCode ), + netname( aNetName ) + {} + + ENET() : + netcode( 0 ) + {} +}; + + +/// Eagle rotation +struct EROT +{ + bool mirror; + bool spin; + double degrees; + + EROT() : + mirror( false ), + spin( false ), + degrees( 0 ) + {} + + EROT( double aDegrees ) : + mirror( false ), + spin( false ), + degrees( aDegrees ) + {} +}; + + +/// Eagle wire +struct EWIRE +{ + double x1; + double y1; + double x2; + double y2; + double width; + LAYER_NUM layer; + + // for style: (continuous | longdash | shortdash | dashdot) + enum { + CONTINUOUS, + LONGDASH, + SHORTDASH, + DASHDOT, + }; + opt_int style; + opt_double curve; ///< range is -359.9..359.9 + + // for cap: (flat | round) + enum { + FLAT, + ROUND, + }; + opt_int cap; + + EWIRE( wxXmlNode* aWire ); +}; + + +/// Eagle via +struct EVIA +{ + double x; + double y; + int layer_front_most; /// < extent + int layer_back_most; /// < inclusive + double drill; + opt_double diam; + opt_string shape; + + EVIA( wxXmlNode* aVia ); +}; + + +/// Eagle circle +struct ECIRCLE +{ + double x; + double y; + double radius; + double width; + LAYER_NUM layer; + + ECIRCLE( wxXmlNode* aCircle ); +}; + + +/// Eagle XML rectangle in binary +struct ERECT +{ + double x1; + double y1; + double x2; + double y2; + int layer; + opt_erot rot; + + ERECT( wxXmlNode* aRect ); +}; + + +/** + * Class EATTR + * parses an Eagle "attribute" XML element. Note that an attribute element + * is different than an XML element attribute. The attribute element is a + * full XML node in and of itself, and has attributes of its own. Blame Eagle. + */ +struct EATTR +{ + string name; + opt_string value; + opt_double x; + opt_double y; + opt_double size; + opt_int layer; + opt_double ratio; + opt_erot rot; + + enum { // for 'display' + Off, + VALUE, + NAME, + BOTH, + }; + opt_int display; + + EATTR( wxXmlNode* aTree ); + EATTR() {} +}; + + +/// Eagle dimension element +struct EDIMENSION +{ + double x1; + double y1; + double x2; + double y2; + double x3; + double y3; + int layer; + + opt_string dimensionType; + + EDIMENSION( wxXmlNode* aDimension ); +}; + + +/// Eagle text element +struct ETEXT +{ + string text; + double x; + double y; + double size; + int layer; + opt_string font; + opt_double ratio; + opt_erot rot; + + enum { // for align + CENTER, + CENTER_LEFT, + TOP_CENTER, + TOP_LEFT, + TOP_RIGHT, + + // opposites are -1 x above, used by code tricks in here + CENTER_RIGHT = -CENTER_LEFT, + BOTTOM_CENTER = -TOP_CENTER, + BOTTOM_LEFT = -TOP_RIGHT, + BOTTOM_RIGHT = -TOP_LEFT, + }; + + opt_int align; + + ETEXT( wxXmlNode* aText ); +}; + + +/// Eagle thru hol pad +struct EPAD +{ + string name; + double x; + double y; + double drill; + opt_double diameter; + + // for shape: (square | round | octagon | long | offset) + enum { + SQUARE, + ROUND, + OCTAGON, + LONG, + OFFSET, + }; + opt_int shape; + opt_erot rot; + opt_bool stop; + opt_bool thermals; + opt_bool first; + + EPAD( wxXmlNode* aPad ); +}; + + +/// Eagle SMD pad +struct ESMD +{ + string name; + double x; + double y; + double dx; + double dy; + int layer; + opt_int roundness; + opt_erot rot; + opt_bool stop; + opt_bool thermals; + opt_bool cream; + + ESMD( wxXmlNode* aSMD ); +}; + + +/// Eagle vertex +struct EVERTEX +{ + double x; + double y; + + EVERTEX( wxXmlNode* aVertex ); +}; + + +/// Eagle polygon, without vertices which are parsed as needed +struct EPOLYGON +{ + double width; + int layer; + opt_double spacing; + + // KiCad priority is opposite of Eagle rank, that is: + // - Eagle Low rank drawn first + // - KiCad high priority drawn first + // So since Eagle has an upper limit we define this, used for the cases + // where no rank is specified. + static const int max_priority = 6; + + enum { // for pour + SOLID, + HATCH, + CUTOUT, + }; + int pour; + opt_double isolate; + opt_bool orphans; + opt_bool thermals; + opt_int rank; + + EPOLYGON( wxXmlNode* aPolygon ); +}; + + +/// Eagle hole element +struct EHOLE +{ + double x; + double y; + double drill; + + EHOLE( wxXmlNode* aHole ); +}; + + +/// Eagle element element +struct EELEMENT +{ + string name; + string library; + string package; + string value; + double x; + double y; + opt_bool locked; + opt_bool smashed; + opt_erot rot; + + EELEMENT( wxXmlNode* aElement ); +}; + + +struct ELAYER +{ + int number; + string name; + int color; + int fill; + opt_bool visible; + opt_bool active; + + ELAYER( wxXmlNode* aLayer ); +}; + +/** + * Function MapChildren + * provides an easy access to the children of an XML node via their names. + * @param currentNode is a pointer to a wxXmlNode, whose children will be mapped. + * @return NODE_MAP - a map linking the name of each children to the children itself (via a + * wxXmlNode*) + */ +NODE_MAP MapChildren( wxXmlNode* currentNode ); + +/// Assemble a two part key as a simple concatenation of aFirst and aSecond parts, +/// using a separator. +string makeKey( const string& aFirst, const string& aSecond ); + +/// Make a unique time stamp +unsigned long timeStamp( wxXmlNode* aTree ); + +/// Convert an Eagle curve end to a KiCad center for S_ARC +wxPoint kicad_arc_center( wxPoint start, wxPoint end, double angle ); + +#endif // _EAGLE_PARSER_H_ diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index f3d35c9824..2a004c4bb7 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -54,10 +54,7 @@ Load() TODO's #include #include -#include -#include - -#include +#include #include #include @@ -76,948 +73,18 @@ Load() TODO's #include #include -using namespace boost::property_tree; +#include + using namespace std; -typedef EAGLE_PLUGIN::BIU BIU; -typedef PTREE::const_assoc_iterator CA_ITER; -typedef PTREE::const_iterator CITER; -typedef std::pair CA_ITER_RANGE; -typedef MODULE_MAP::iterator MODULE_ITER; -typedef MODULE_MAP::const_iterator MODULE_CITER; - -typedef boost::optional opt_string; -typedef boost::optional opt_int; -typedef boost::optional opt_double; -typedef boost::optional opt_bool; - - -/// segment (element) of our XPATH into the Eagle XML document tree in PTREE form. -struct TRIPLET -{ - const char* element; - const char* attribute; - const char* value; - - TRIPLET( const char* aElement, const char* aAttribute = "", const char* aValue = "" ) : - element( aElement ), - attribute( aAttribute ), - value( aValue ) - {} -}; - - -/** - * Class XPATH - * keeps track of what we are working on within a PTREE. - * Then if an exception is thrown, the place within the tree that gave us - * grief can be reported almost accurately. To minimally impact - * speed, merely assign const char* pointers during the tree walking - * expedition. The const char* pointers must be to C strings residing either in - * the data or code segment (i.e. "compiled in") or within the XML document, but - * not on the stack, since the stack is unwound during the throwing of the - * exception. The XML document will not immediately vanish since we capture - * the xpath (using function Contents()) before the XML document tree (PTREE) - * is destroyed. - */ -class XPATH -{ - std::vector p; - -public: - void push( const char* aPathSegment, const char* aAttribute="" ) - { - p.push_back( TRIPLET( aPathSegment, aAttribute ) ); - } - - void clear() { p.clear(); } - - void pop() { p.pop_back(); } - - /// modify the last path node's value - void Value( const char* aValue ) - { - p.back().value = aValue; - } - - /// modify the last path node's attribute - void Attribute( const char* aAttribute ) - { - p.back().attribute = aAttribute; - } - - /// return the contents of the XPATH as a single string - string Contents() - { - typedef std::vector::const_iterator CITER_TRIPLET; - - string ret; - - for( CITER_TRIPLET it = p.begin(); it != p.end(); ++it ) - { - if( it != p.begin() ) - ret += '.'; - - ret += it->element; - - if( it->attribute[0] && it->value[0] ) - { - ret += '['; - ret += it->attribute; - ret += '='; - ret += it->value; - ret += ']'; - } - } - - return ret; - } -}; - - -/** - * Function parseOptionalBool - * returns an opt_bool and sets it true or false according to the presence - * and value of an attribute within the CPTREE element. - */ -static opt_bool parseOptionalBool( CPTREE& attribs, const char* aName ) -{ - opt_bool ret; - opt_string stemp = attribs.get_optional( aName ); - - if( stemp ) - ret = !stemp->compare( "yes" ); - - return ret; -} - - -// All of the 'E'STRUCTS below merely hold Eagle XML information verbatim, in binary. -// For maintenance and troubleshooting purposes, it was thought that we'd need to -// separate the conversion process into distinct steps. There is no intent to have KiCad -// forms of information in these 'E'STRUCTS. They are only binary forms -// of the Eagle information in the corresponding Eagle XML nodes. - - -/// Eagle rotation -struct EROT -{ - bool mirror; - bool spin; - double degrees; - - EROT() : - mirror( false ), - spin( false ), - degrees( 0 ) - {} - - EROT( double aDegrees ) : - mirror( false ), - spin( false ), - degrees( aDegrees ) - {} -}; - -typedef boost::optional opt_erot; - -/// parse an Eagle XML "rot" field. Unfortunately the DTD seems not to explain -/// this format very well. [S][M]R. Examples: "R90", "MR180", "SR180" -static EROT erot( const string& aRot ) -{ - EROT rot; - - rot.spin = aRot.find( 'S' ) != aRot.npos; - rot.mirror = aRot.find( 'M' ) != aRot.npos; - rot.degrees = strtod( aRot.c_str() - + 1 // skip leading 'R' - + int( rot.spin ) // skip optional leading 'S' - + int( rot.mirror ), // skip optional leading 'M' - NULL ); - return rot; -} - -/// Eagle "rot" fields are optional, handle that by returning opt_erot. -static opt_erot parseOptionalEROT( CPTREE& attribs ) -{ - opt_erot ret; - opt_string stemp = attribs.get_optional( "rot" ); - if( stemp ) - ret = erot( *stemp ); - return ret; -} - -/// Eagle wire -struct EWIRE -{ - double x1; - double y1; - double x2; - double y2; - double width; - LAYER_NUM layer; - - // for style: (continuous | longdash | shortdash | dashdot) - enum { - CONTINUOUS, - LONGDASH, - SHORTDASH, - DASHDOT, - }; - opt_int style; - opt_double curve; ///< range is -359.9..359.9 - - // for cap: (flat | round) - enum { - FLAT, - ROUND, - }; - opt_int cap; - - EWIRE( CPTREE& aWire ); -}; - -/** - * Constructor EWIRE - * converts a "wire"'s xml attributes ( <wire> ) - * to binary without additional conversion. - * This result is an EWIRE with the <wire> textual data merely converted to binary. - */ -EWIRE::EWIRE( CPTREE& aWire ) -{ - CPTREE& attribs = aWire.get_child( "" ); - - /* - - - */ - - x1 = attribs.get( "x1" ); - y1 = attribs.get( "y1" ); - x2 = attribs.get( "x2" ); - y2 = attribs.get( "y2" ); - width = attribs.get( "width" ); - layer = attribs.get( "layer" ); - - curve = attribs.get_optional( "curve" ); - - opt_string s = attribs.get_optional( "style" ); - if( s ) - { - if( !s->compare( "continuous" ) ) - style = EWIRE::CONTINUOUS; - else if( !s->compare( "longdash" ) ) - style = EWIRE::LONGDASH; - else if( !s->compare( "shortdash" ) ) - style = EWIRE::SHORTDASH; - else if( !s->compare( "dashdot" ) ) - style = EWIRE::DASHDOT; - } - - s = attribs.get_optional( "cap" ); - if( s ) - { - if( !s->compare( "round" ) ) - cap = EWIRE::ROUND; - else if( !s->compare( "flat" ) ) - cap = EWIRE::FLAT; - } - // ignoring extent -} - - -/// Eagle via -struct EVIA -{ - double x; - double y; - int layer_front_most; /// < extent - int layer_back_most; /// < inclusive - double drill; - opt_double diam; - opt_string shape; - EVIA( CPTREE& aVia ); -}; - -EVIA::EVIA( CPTREE& aVia ) -{ - CPTREE& attribs = aVia.get_child( "" ); - - /* - - - */ - - x = attribs.get( "x" ); - y = attribs.get( "y" ); - - string ext = attribs.get( "extent" ); - - sscanf( ext.c_str(), "%d-%d", &layer_front_most, &layer_back_most ); - - drill = attribs.get( "drill" ); - diam = attribs.get_optional( "diameter" ); - shape = attribs.get_optional( "shape" ); -} - - -/// Eagle circle -struct ECIRCLE -{ - double x; - double y; - double radius; - double width; - LAYER_NUM layer; - - ECIRCLE( CPTREE& aCircle ); -}; - -ECIRCLE::ECIRCLE( CPTREE& aCircle ) -{ - CPTREE& attribs = aCircle.get_child( "" ); - - /* - - - */ - - x = attribs.get( "x" ); - y = attribs.get( "y" ); - radius = attribs.get( "radius" ); - width = attribs.get( "width" ); - layer = attribs.get( "layer" ); -} - - -/// Eagle XML rectangle in binary -struct ERECT -{ - double x1; - double y1; - double x2; - double y2; - int layer; - opt_erot rot; - - ERECT( CPTREE& aRect ); -}; - -ERECT::ERECT( CPTREE& aRect ) -{ - CPTREE& attribs = aRect.get_child( "" ); - - /* - - - */ - - x1 = attribs.get( "x1" ); - y1 = attribs.get( "y1" ); - x2 = attribs.get( "x2" ); - y2 = attribs.get( "y2" ); - layer = attribs.get( "layer" ); - rot = parseOptionalEROT( attribs ); -} - - -/// Eagle "attribute" XML element, no foolin'. -struct EATTR -{ - string name; - opt_string value; - opt_double x; - opt_double y; - opt_double size; - opt_int layer; - opt_double ratio; - opt_erot rot; - - enum { // for 'display' - Off, - VALUE, - NAME, - BOTH, - }; - opt_int display; - - EATTR( CPTREE& aTree ); - EATTR() {} -}; - -/** - * Constructor EATTR - * parses an Eagle "attribute" XML element. Note that an attribute element - * is different than an XML element attribute. The attribute element is a - * full XML node in and of itself, and has attributes of its own. Blame Eagle. - */ -EATTR::EATTR( CPTREE& aAttribute ) -{ - CPTREE& attribs = aAttribute.get_child( "" ); - - /* - - or context -- - constant %Bool; "no" -- only in context -- - > - */ - - name = attribs.get( "name" ); // #REQUIRED - value = attribs.get_optional( "value" ); - - x = attribs.get_optional( "x" ); - y = attribs.get_optional( "y" ); - size = attribs.get_optional( "size" ); - - // KiCad cannot currently put a TEXTE_MODULE on a different layer than the MODULE - // Eagle can it seems. - layer = attribs.get_optional( "layer" ); - - ratio = attribs.get_optional( "ratio" ); - rot = parseOptionalEROT( attribs ); - - opt_string stemp = attribs.get_optional( "display" ); - if( stemp ) - { - // (off | value | name | both) - if( !stemp->compare( "off" ) ) - display = EATTR::Off; - else if( !stemp->compare( "value" ) ) - display = EATTR::VALUE; - else if( !stemp->compare( "name" ) ) - display = EATTR::NAME; - else if( !stemp->compare( "both" ) ) - display = EATTR::BOTH; - } -} - -/// Eagle dimension element -struct EDIMENSION -{ - double x1; - double y1; - double x2; - double y2; - double x3; - double y3; - int layer; - - opt_string dimensionType; - - EDIMENSION( CPTREE& aDimension ); -}; - -EDIMENSION::EDIMENSION( CPTREE& aDimension ) -{ - CPTREE& attribs = aDimension.get_child( "" ); - - /* - - - */ - - x1 = attribs.get( "x1" ); - y1 = attribs.get( "y1" ); - x2 = attribs.get( "x2" ); - y2 = attribs.get( "y2" ); - x3 = attribs.get( "x3" ); - y3 = attribs.get( "y3" ); - layer = attribs.get( "layer" ); - - opt_string dimType = attribs.get_optional( "dtype" ); - - if(!dimType) - { - // default type is parallel - } -} - -/// Eagle text element -struct ETEXT -{ - string text; - double x; - double y; - double size; - int layer; - opt_string font; - opt_double ratio; - opt_erot rot; - - enum { // for align - CENTER, - CENTER_LEFT, - TOP_CENTER, - TOP_LEFT, - TOP_RIGHT, - - // opposites are -1 x above, used by code tricks in here - CENTER_RIGHT = -CENTER_LEFT, - BOTTOM_CENTER = -TOP_CENTER, - BOTTOM_LEFT = -TOP_RIGHT, - BOTTOM_RIGHT = -TOP_LEFT, - }; - - opt_int align; - - ETEXT( CPTREE& aText ); -}; - -ETEXT::ETEXT( CPTREE& aText ) -{ - CPTREE& attribs = aText.get_child( "" ); - - /* - - - */ - - text = aText.data(); - x = attribs.get( "x" ); - y = attribs.get( "y" ); - size = attribs.get( "size" ); - layer = attribs.get( "layer" ); - - font = attribs.get_optional( "font" ); - ratio = attribs.get_optional( "ratio" ); - rot = parseOptionalEROT( attribs ); - - opt_string stemp = attribs.get_optional( "align" ); - if( stemp ) - { - // (bottom-left | bottom-center | bottom-right | center-left | - // center | center-right | top-left | top-center | top-right) - if( !stemp->compare( "center" ) ) - align = ETEXT::CENTER; - else if( !stemp->compare( "center-right" ) ) - align = ETEXT::CENTER_RIGHT; - else if( !stemp->compare( "top-left" ) ) - align = ETEXT::TOP_LEFT; - else if( !stemp->compare( "top-center" ) ) - align = ETEXT::TOP_CENTER; - else if( !stemp->compare( "top-right" ) ) - align = ETEXT::TOP_RIGHT; - else if( !stemp->compare( "bottom-left" ) ) - align = ETEXT::BOTTOM_LEFT; - else if( !stemp->compare( "bottom-center" ) ) - align = ETEXT::BOTTOM_CENTER; - else if( !stemp->compare( "bottom-right" ) ) - align = ETEXT::BOTTOM_RIGHT; - else if( !stemp->compare( "center-left" ) ) - align = ETEXT::CENTER_LEFT; - } -} - - -/// Eagle thru hol pad -struct EPAD -{ - string name; - double x; - double y; - double drill; - opt_double diameter; - - // for shape: (square | round | octagon | long | offset) - enum { - SQUARE, - ROUND, - OCTAGON, - LONG, - OFFSET, - }; - opt_int shape; - opt_erot rot; - opt_bool stop; - opt_bool thermals; - opt_bool first; - - EPAD( CPTREE& aPad ); -}; - -EPAD::EPAD( CPTREE& aPad ) -{ - CPTREE& attribs = aPad.get_child( "" ); - - /* - - - */ - - // #REQUIRED says DTD, throw exception if not found - name = attribs.get( "name" ); - x = attribs.get( "x" ); - y = attribs.get( "y" ); - drill = attribs.get( "drill" ); - - diameter = attribs.get_optional( "diameter" ); - - opt_string s = attribs.get_optional( "shape" ); - if( s ) - { - // (square | round | octagon | long | offset) - if( !s->compare( "square" ) ) - shape = EPAD::SQUARE; - else if( !s->compare( "round" ) ) - shape = EPAD::ROUND; - else if( !s->compare( "octagon" ) ) - shape = EPAD::OCTAGON; - else if( !s->compare( "long" ) ) - shape = EPAD::LONG; - else if( !s->compare( "offset" ) ) - shape = EPAD::OFFSET; - } - - rot = parseOptionalEROT( attribs ); - stop = parseOptionalBool( attribs, "stop" ); - thermals = parseOptionalBool( attribs, "thermals" ); - first = parseOptionalBool( attribs, "first" ); -} - - -/// Eagle SMD pad -struct ESMD -{ - string name; - double x; - double y; - double dx; - double dy; - int layer; - opt_int roundness; - opt_erot rot; - opt_bool stop; - opt_bool thermals; - opt_bool cream; - - ESMD( CPTREE& aSMD ); -}; - -ESMD::ESMD( CPTREE& aSMD ) -{ - CPTREE& attribs = aSMD.get_child( "" ); - - /* - - */ - - // DTD #REQUIRED, throw exception if not found - name = attribs.get( "name" ); - x = attribs.get( "x" ); - y = attribs.get( "y" ); - dx = attribs.get( "dx" ); - dy = attribs.get( "dy" ); - layer = attribs.get( "layer" ); - rot = parseOptionalEROT( attribs ); - - roundness = attribs.get_optional( "roundness" ); - thermals = parseOptionalBool( attribs, "thermals" ); - stop = parseOptionalBool( attribs, "stop" ); - thermals = parseOptionalBool( attribs, "thermals" ); - cream = parseOptionalBool( attribs, "cream" ); -} - - -struct EVERTEX -{ - double x; - double y; - - EVERTEX( CPTREE& aVertex ); -}; - -EVERTEX::EVERTEX( CPTREE& aVertex ) -{ - CPTREE& attribs = aVertex.get_child( "" ); - - /* - - - */ - - x = attribs.get( "x" ); - y = attribs.get( "y" ); -} - - -/// Eagle polygon, without vertices which are parsed as needed -struct EPOLYGON -{ - double width; - int layer; - opt_double spacing; - - // KiCad priority is opposite of Eagle rank, that is: - // - Eagle Low rank drawn first - // - KiCad high priority drawn first - // So since Eagle has an upper limit we define this, used for the cases - // where no rank is specified. - static const int max_priority = 6; - - enum { // for pour - SOLID, - HATCH, - CUTOUT, - }; - int pour; - opt_double isolate; - opt_bool orphans; - opt_bool thermals; - opt_int rank; - - EPOLYGON( CPTREE& aPolygon ); -}; - -EPOLYGON::EPOLYGON( CPTREE& aPolygon ) -{ - CPTREE& attribs = aPolygon.get_child( "" ); - - /* - or context -- - orphans %Bool; "no" -- only in context -- - thermals %Bool; "yes" -- only in context -- - rank %Int; "0" -- 1..6 in context, 0 or 7 in context -- - > - */ - - width = attribs.get( "width" ); - layer = attribs.get( "layer" ); - spacing = attribs.get_optional( "spacing" ); - isolate = attribs.get_optional( "isolate" ); - // default pour to solid fill - pour = EPOLYGON::SOLID; - opt_string s = attribs.get_optional( "pour" ); - - if( s ) - { - // (solid | hatch | cutout) - if( !s->compare( "hatch" ) ) - pour = EPOLYGON::HATCH; - else if( !s->compare( "cutout" ) ) - pour = EPOLYGON::CUTOUT; - } - - orphans = parseOptionalBool( attribs, "orphans" ); - thermals = parseOptionalBool( attribs, "thermals" ); - rank = attribs.get_optional( "rank" ); -} - -/// Eagle hole element -struct EHOLE -{ - double x; - double y; - double drill; - - EHOLE( CPTREE& aHole ); -}; - -EHOLE::EHOLE( CPTREE& aHole ) -{ - CPTREE& attribs = aHole.get_child( "" ); - - /* - - - */ - - // #REQUIRED: - x = attribs.get( "x" ); - y = attribs.get( "y" ); - drill = attribs.get( "drill" ); -} - - -/// Eagle element element -struct EELEMENT -{ - string name; - string library; - string package; - string value; - double x; - double y; - opt_bool locked; - opt_bool smashed; - opt_erot rot; - - EELEMENT( CPTREE& aElement ); -}; - -EELEMENT::EELEMENT( CPTREE& aElement ) -{ - CPTREE& attribs = aElement.get_child( "" ); - - /* - - - */ - - // #REQUIRED - name = attribs.get( "name" ); - library = attribs.get( "library" ); - value = attribs.get( "value" ); - - package = attribs.get( "package" ); - ReplaceIllegalFileNameChars( &package ); - - x = attribs.get( "x" ); - y = attribs.get( "y" ); - - // optional - locked = parseOptionalBool( attribs, "locked" ); - smashed = parseOptionalBool( attribs, "smashed" ); - rot = parseOptionalEROT( attribs ); -} - - -struct ELAYER -{ - int number; - string name; - int color; - int fill; - opt_bool visible; - opt_bool active; - - ELAYER( CPTREE& aLayer ); -}; - -ELAYER::ELAYER( CPTREE& aLayer ) -{ - CPTREE& attribs = aLayer.get_child( "" ); - - /* - - - */ - - number = attribs.get( "number" ); - name = attribs.get( "name" ); - color = attribs.get( "color" ); - fill = 1; // Temporary value. - visible = parseOptionalBool( attribs, "visible" ); - active = parseOptionalBool( attribs, "active" ); -} +typedef MODULE_MAP::iterator MODULE_ITER; +typedef MODULE_MAP::const_iterator MODULE_CITER; /// Parse an eagle distance which is either mm, or mils if there is "mil" suffix. /// Return is in BIU. -static double parseEagle( const string& aDistance ) +static double parseEagle( const wxString& aDistance ) { double ret = strtod( aDistance.c_str(), NULL ); if( aDistance.npos != aDistance.find( "mil" ) ) @@ -1029,113 +96,42 @@ static double parseEagle( const string& aDistance ) } -/// subset of eagle.drawing.board.designrules in the XML document -struct ERULES +void ERULES::parse( wxXmlNode* aRules ) { - int psElongationLong; ///< percent over 100%. 0-> not elongated, 100->twice as wide as is tall - ///< Goes into making a scaling factor for "long" pads. + wxXmlNode* child = aRules->GetChildren(); - int psElongationOffset; ///< the offset of the hole within the "long" pad. - - double rvPadTop; ///< top pad size as percent of drill size - // double rvPadBottom; ///< bottom pad size as percent of drill size - - double rlMinPadTop; ///< minimum copper annulus on through hole pads - double rlMaxPadTop; ///< maximum copper annulus on through hole pads - - double rvViaOuter; ///< copper annulus is this percent of via hole - double rlMinViaOuter; ///< minimum copper annulus on via - double rlMaxViaOuter; ///< maximum copper annulus on via - double mdWireWire; ///< wire to wire spacing I presume. - - - ERULES() : - psElongationLong ( 100 ), - psElongationOffset ( 0 ), - rvPadTop ( 0.25 ), - // rvPadBottom ( 0.25 ), - rlMinPadTop ( Mils2iu( 10 ) ), - rlMaxPadTop ( Mils2iu( 20 ) ), - - rvViaOuter ( 0.25 ), - rlMinViaOuter ( Mils2iu( 10 ) ), - rlMaxViaOuter ( Mils2iu( 20 ) ), - mdWireWire ( 0 ) - {} - - void parse( CPTREE& aRules ); -}; - -void ERULES::parse( CPTREE& aRules ) -{ - for( CITER it = aRules.begin(); it != aRules.end(); ++it ) + while( child ) { - if( it->first != "param" ) - continue; + if( child->GetName() == "param" ) + { + const wxString& name = child->GetAttribute( "name" ); + const wxString& value = child->GetAttribute( "value" ); - CPTREE& attribs = it->second.get_child( "" ); + if( name == "psElongationLong" ) + psElongationLong = wxAtoi( value ); + else if( name == "psElongationOffset" ) + psElongationOffset = wxAtoi( value ); + else if( name == "rvPadTop" ) + value.ToDouble( &rvPadTop ); + else if( name == "rlMinPadTop" ) + rlMinPadTop = parseEagle( value ); + else if( name == "rlMaxPadTop" ) + rlMaxPadTop = parseEagle( value ); + else if( name == "rvViaOuter" ) + value.ToDouble( &rvViaOuter ); + else if( name == "rlMinViaOuter" ) + rlMinViaOuter = parseEagle( value ); + else if( name == "rlMaxViaOuter" ) + rlMaxViaOuter = parseEagle( value ); + else if( name == "mdWireWire" ) + mdWireWire = parseEagle( value ); + } - const string& name = attribs.get( "name" ); - - if( name == "psElongationLong" ) - psElongationLong = attribs.get( "value" ); - else if( name == "psElongationOffset" ) - psElongationOffset = attribs.get( "value" ); - else if( name == "rvPadTop" ) - rvPadTop = attribs.get( "value" ); - else if( name == "rlMinPadTop" ) - rlMinPadTop = parseEagle( attribs.get( "value" ) ); - else if( name == "rlMaxPadTop" ) - rlMaxPadTop = parseEagle( attribs.get( "value" ) ); - else if( name == "rvViaOuter" ) - rvViaOuter = attribs.get( "value" ); - else if( name == "rlMinViaOuter" ) - rlMinViaOuter = parseEagle( attribs.get( "value" ) ); - else if( name == "rlMaxViaOuter" ) - rlMaxViaOuter = parseEagle( attribs.get( "value" ) ); - else if( name == "mdWireWire" ) - mdWireWire = parseEagle( attribs.get( "value" ) ); + child = child->GetNext(); } } -/// Assemble a two part key as a simple concatonation of aFirst and aSecond parts, -/// using a separator. -static inline string makeKey( const string& aFirst, const string& aSecond ) -{ - string key = aFirst + '\x02' + aSecond; - return key; -} - - -/// Make a unique time stamp -static inline unsigned long timeStamp( CPTREE& aTree ) -{ - // in this case from a unique tree memory location - return (unsigned long)(void*) &aTree; -} - - -/// Convert an Eagle curve end to a KiCad center for S_ARC -wxPoint kicad_arc_center( wxPoint start, wxPoint end, double angle ) -{ - // Eagle give us start and end. - // S_ARC wants start to give the center, and end to give the start. - double dx = end.x - start.x, dy = end.y - start.y; - wxPoint mid = (start + end) / 2; - - double dlen = sqrt( dx*dx + dy*dy ); - double dist = dlen / ( 2 * tan( DEG2RAD( angle ) / 2 ) ); - - wxPoint center( - mid.x + dist * ( dy / dlen ), - mid.y - dist * ( dx / dlen ) - ); - - return center; -} - - EAGLE_PLUGIN::EAGLE_PLUGIN() : m_rules( new ERULES() ), m_xpath( new XPATH() ), @@ -1182,8 +178,8 @@ wxSize inline EAGLE_PLUGIN::kicad_fontz( double d ) const BOARD* EAGLE_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties ) { - LOCALE_IO toggle; // toggles on, then off, the C locale. - PTREE doc; + LOCALE_IO toggle; // toggles on, then off, the C locale. + wxXmlNode* doc; init( aProperties ); @@ -1198,12 +194,15 @@ BOARD* EAGLE_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe, const try { - // 8 bit "filename" should be encoded according to disk filename encoding, - // (maybe this is current locale, maybe not, its a filesystem issue), - // and is not necessarily utf8. - string filename = (const char*) aFileName.char_str( wxConvFile ); + // Load the document + wxXmlDocument xmlDocument; + wxFileName fn = aFileName; - read_xml( filename, doc, xml_parser::no_comments ); + if( !xmlDocument.Load( fn.GetFullPath() ) ) + THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'" ), + fn.GetFullPath() ) ); + + doc = xmlDocument.GetRoot(); m_min_trace = INT_MAX; m_min_via = INT_MAX; @@ -1234,21 +233,12 @@ BOARD* EAGLE_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe, const // should be empty, else missing m_xpath->pop() wxASSERT( m_xpath->Contents().size() == 0 ); } - - catch( file_parser_error fpe ) + // Catch all exceptions thrown from the parser. + catch( const XML_PARSER_ERROR &exc ) { - // for xml_parser_error, what() has the line number in it, - // but no byte offset. That should be an adequate error message. - THROW_IO_ERROR( fpe.what() ); - } + string errmsg = exc.what(); - // Class ptree_error is a base class for xml_parser_error & file_parser_error, - // so one catch should be OK for all errors. - catch( ptree_error pte ) - { - string errmsg = pte.what(); - - errmsg += " @\n"; + errmsg += "\n@ "; errmsg += m_xpath->Contents(); THROW_IO_ERROR( errmsg ); @@ -1305,17 +295,20 @@ void EAGLE_PLUGIN::clear_cu_map() } -void EAGLE_PLUGIN::loadAllSections( CPTREE& aDoc ) +void EAGLE_PLUGIN::loadAllSections( wxXmlNode* aDoc ) { - CPTREE& drawing = aDoc.get_child( "eagle.drawing" ); - CPTREE& board = drawing.get_child( "board" ); + wxXmlNode* drawing = MapChildren( aDoc )["drawing"]; + NODE_MAP drawingChildren = MapChildren( drawing ); + + wxXmlNode* board = drawingChildren["board"]; + NODE_MAP boardChildren = MapChildren( board ); m_xpath->push( "eagle.drawing" ); { m_xpath->push( "board" ); - CPTREE& designrules = board.get_child( "designrules" ); + wxXmlNode* designrules = boardChildren["designrules"]; loadDesignRules( designrules ); m_xpath->pop(); @@ -1324,7 +317,7 @@ void EAGLE_PLUGIN::loadAllSections( CPTREE& aDoc ) { m_xpath->push( "layers" ); - CPTREE& layers = drawing.get_child( "layers" ); + wxXmlNode* layers = drawingChildren["layers"]; loadLayerDefs( layers ); m_xpath->pop(); @@ -1333,16 +326,16 @@ void EAGLE_PLUGIN::loadAllSections( CPTREE& aDoc ) { m_xpath->push( "board" ); - CPTREE& plain = board.get_child( "plain" ); + wxXmlNode* plain = boardChildren["plain"]; loadPlain( plain ); - CPTREE& signals = board.get_child( "signals" ); + wxXmlNode* signals = boardChildren["signals"]; loadSignals( signals ); - CPTREE& libs = board.get_child( "libraries" ); + wxXmlNode* libs = boardChildren["libraries"]; loadLibraries( libs ); - CPTREE& elems = board.get_child( "elements" ); + wxXmlNode* elems = boardChildren["elements"]; loadElements( elems ); m_xpath->pop(); // "board" @@ -1352,7 +345,7 @@ void EAGLE_PLUGIN::loadAllSections( CPTREE& aDoc ) } -void EAGLE_PLUGIN::loadDesignRules( CPTREE& aDesignRules ) +void EAGLE_PLUGIN::loadDesignRules( wxXmlNode* aDesignRules ) { m_xpath->push( "designrules" ); m_rules->parse( aDesignRules ); @@ -1360,22 +353,27 @@ void EAGLE_PLUGIN::loadDesignRules( CPTREE& aDesignRules ) } -void EAGLE_PLUGIN::loadLayerDefs( CPTREE& aLayers ) +void EAGLE_PLUGIN::loadLayerDefs( wxXmlNode* aLayers ) { typedef std::vector ELAYERS; typedef ELAYERS::const_iterator EITER; ELAYERS cu; // copper layers + // Get the first layer and iterate + wxXmlNode* layerNode = aLayers->GetChildren(); + // find the subset of layers that are copper, and active - for( CITER layer = aLayers.begin(); layer != aLayers.end(); ++layer ) + while( layerNode ) { - ELAYER elayer( layer->second ); + ELAYER elayer( layerNode ); if( elayer.number >= 1 && elayer.number <= 16 && ( !elayer.active || *elayer.active ) ) { cu.push_back( elayer ); } + + layerNode = layerNode->GetNext(); } // establish cu layer map: @@ -1426,18 +424,23 @@ void EAGLE_PLUGIN::loadLayerDefs( CPTREE& aLayers ) } -void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) +void EAGLE_PLUGIN::loadPlain( wxXmlNode* aGraphics ) { m_xpath->push( "plain" ); + // Get the first graphic and iterate + wxXmlNode* gr = aGraphics->GetChildren(); + // (polygon | wire | text | circle | rectangle | frame | hole)* - for( CITER gr = aGraphics.begin(); gr != aGraphics.end(); ++gr ) + while( gr ) { - if( gr->first == "wire" ) + wxString grName = gr->GetName(); + + if( grName == "wire" ) { m_xpath->push( "wire" ); - EWIRE w( gr->second ); + EWIRE w( gr ); PCB_LAYER_ID layer = kicad_layer( w.layer ); wxPoint start( kicad_x( w.x1 ), kicad_y( w.y1 ) ); @@ -1463,17 +466,17 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) dseg->SetAngle( *w.curve * -10.0 ); // KiCad rotates the other way } - dseg->SetTimeStamp( timeStamp( gr->second ) ); + dseg->SetTimeStamp( timeStamp( gr ) ); dseg->SetLayer( layer ); dseg->SetWidth( Millimeter2iu( DEFAULT_PCB_EDGE_THICKNESS ) ); } m_xpath->pop(); } - else if( gr->first == "text" ) + else if( grName == "text" ) { m_xpath->push( "text" ); - ETEXT t( gr->second ); + ETEXT t( gr ); PCB_LAYER_ID layer = kicad_layer( t.layer ); if( layer != UNDEFINED_LAYER ) @@ -1482,7 +485,7 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) m_board->Add( pcbtxt, ADD_APPEND ); pcbtxt->SetLayer( layer ); - pcbtxt->SetTimeStamp( timeStamp( gr->second ) ); + pcbtxt->SetTimeStamp( timeStamp( gr ) ); pcbtxt->SetText( FROM_UTF8( t.text.c_str() ) ); pcbtxt->SetTextPos( wxPoint( kicad_x( t.x ), kicad_y( t.y ) ) ); @@ -1577,11 +580,11 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) } m_xpath->pop(); } - else if( gr->first == "circle" ) + else if( grName == "circle" ) { m_xpath->push( "circle" ); - ECIRCLE c( gr->second ); + ECIRCLE c( gr ); PCB_LAYER_ID layer = kicad_layer( c.layer ); if( layer != UNDEFINED_LAYER ) // unsupported layer @@ -1590,7 +593,7 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) m_board->Add( dseg, ADD_APPEND ); dseg->SetShape( S_CIRCLE ); - dseg->SetTimeStamp( timeStamp( gr->second ) ); + dseg->SetTimeStamp( timeStamp( gr ) ); dseg->SetLayer( layer ); dseg->SetStart( wxPoint( kicad_x( c.x ), kicad_y( c.y ) ) ); dseg->SetEnd( wxPoint( kicad_x( c.x + c.radius ), kicad_y( c.y ) ) ); @@ -1598,13 +601,13 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) } m_xpath->pop(); } - else if( gr->first == "rectangle" ) + else if( grName == "rectangle" ) { // This seems to be a simplified rectangular [copper] zone, cannot find any // net related info on it from the DTD. m_xpath->push( "rectangle" ); - ERECT r( gr->second ); + ERECT r( gr ); PCB_LAYER_ID layer = kicad_layer( r.layer ); if( IsCopperLayer( layer ) ) @@ -1613,7 +616,7 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) ZONE_CONTAINER* zone = new ZONE_CONTAINER( m_board ); m_board->Add( zone, ADD_APPEND ); - zone->SetTimeStamp( timeStamp( gr->second ) ); + zone->SetTimeStamp( timeStamp( gr ) ); zone->SetLayer( layer ); zone->SetNetCode( NETINFO_LIST::UNCONNECTED ); @@ -1630,10 +633,10 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) m_xpath->pop(); } - else if( gr->first == "hole" ) + else if( grName == "hole" ) { m_xpath->push( "hole" ); - EHOLE e( gr->second ); + EHOLE e( gr ); // Fabricate a MODULE with a single PAD_ATTRIB_HOLE_NOT_PLATED pad. // Use m_hole_count to gen up a unique name. @@ -1672,18 +675,18 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) pad->SetLayerSet( LSET::AllCuMask() ); m_xpath->pop(); } - else if( gr->first == "frame" ) + else if( grName == "frame" ) { // picture this } - else if( gr->first == "polygon" ) + else if( grName == "polygon" ) { // could be on a copper layer, could be on another layer. // copper layer would be done using netCode=0 type of ZONE_CONTAINER. } - else if( gr->first == "dimension" ) + else if( grName == "dimension" ) { - EDIMENSION d( gr->second ); + EDIMENSION d( gr ); DIMENSION* dimension = new DIMENSION( m_board ); m_board->Add( dimension, ADD_APPEND ); @@ -1714,29 +717,36 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) dimension->AdjustDimensionDetails(); } + + // Get next graphic + gr = gr->GetNext(); } m_xpath->pop(); } -void EAGLE_PLUGIN::loadLibrary( CPTREE& aLib, const string* aLibName ) +void EAGLE_PLUGIN::loadLibrary( wxXmlNode* aLib, const string* aLibName ) { m_xpath->push( "packages" ); // library will have node, skip that and get the single packages node - CPTREE& packages = aLib.get_child( "packages" ); + wxXmlNode* packages = MapChildren( aLib )["packages"]; + // Create a MODULE for all the eagle packages, for use later via a copy constructor // to instantiate needed MODULES in our BOARD. Save the MODULE templates in // a MODULE_MAP using a single lookup key consisting of libname+pkgname. - for( CITER package = packages.begin(); package != packages.end(); ++package ) + // Get the first package and iterate + wxXmlNode* package = packages->GetChildren(); + + while( package ) { m_xpath->push( "package", "name" ); - const string& pack_ref = package->second.get( ".name" ); + const wxString& pack_ref = package->GetAttribute( "name" ); - string pack_name( pack_ref ); + string pack_name( pack_ref.ToStdString() ); ReplaceIllegalFileNameChars( &pack_name ); @@ -1744,10 +754,10 @@ void EAGLE_PLUGIN::loadLibrary( CPTREE& aLib, const string* aLibName ) string key = aLibName ? makeKey( *aLibName, pack_name ) : pack_name; - MODULE* m = makeModule( package->second, pack_name ); + MODULE* m = makeModule( package, pack_name ); // add the templating MODULE to the MODULE template factory "m_templates" - std::pair r = m_templates.insert( key, m ); + std::pair r = m_templates.insert( {key, m} ); if( !r.second // && !( m_props && m_props->Value( "ignore_duplicates" ) ) @@ -1765,30 +775,37 @@ void EAGLE_PLUGIN::loadLibrary( CPTREE& aLib, const string* aLibName ) } m_xpath->pop(); + + package = package->GetNext(); } m_xpath->pop(); // "packages" } -void EAGLE_PLUGIN::loadLibraries( CPTREE& aLibs ) +void EAGLE_PLUGIN::loadLibraries( wxXmlNode* aLibs ) { m_xpath->push( "libraries.library", "name" ); - for( CITER library = aLibs.begin(); library != aLibs.end(); ++library ) + // Get the first library and iterate + wxXmlNode* library = aLibs->GetChildren(); + + while( library ) { - const string& lib_name = library->second.get( ".name" ); + const string& lib_name = library->GetAttribute( "name" ).ToStdString(); m_xpath->Value( lib_name.c_str() ); - loadLibrary( library->second, &lib_name ); + loadLibrary( library, &lib_name ); + + library = library->GetNext(); } m_xpath->pop(); } -void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) +void EAGLE_PLUGIN::loadElements( wxXmlNode* aElements ) { m_xpath->push( "elements.element", "name" ); @@ -1797,14 +814,15 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) bool refanceNamePresetInPackageLayout; bool valueNamePresetInPackageLayout; + // Get the first element and iterate + wxXmlNode* element = aElements->GetChildren(); - - for( CITER it = aElements.begin(); it != aElements.end(); ++it ) + while( element ) { - if( it->first != "element" ) + if( element->GetName() != "element" ) continue; - EELEMENT e( it->second ); + EELEMENT e( element ); // use "NULL-ness" as an indication of presence of the attribute: EATTR* nameAttr = 0; @@ -1880,13 +898,16 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) // EATTR override the ones established in the package only if they are // present here (except for rot, which if not present means angle zero). // So the logic is a bit different than in packageText() and in plain text. - for( CITER ait = it->second.begin(); ait != it->second.end(); ++ait ) - { - if( ait->first != "attribute" ) + // Get the first attribute and iterate + wxXmlNode* attribute = element->GetChildren(); + + while( attribute ) + { + if( attribute->GetName() != "attribute" ) continue; - EATTR a( ait->second ); + EATTR a( attribute ); if( a.name == "NAME" ) { @@ -1980,12 +1001,17 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) m->Value().SetVisible( true ); } + + attribute = attribute->GetNext(); } m_xpath->pop(); // "attribute" } orientModuleAndText( m, e, nameAttr, valueAttr ); + + // Get next element + element = element->GetNext(); } m_xpath->pop(); // "elements.element" @@ -2121,50 +1147,55 @@ void EAGLE_PLUGIN::orientModuleText( MODULE* m, const EELEMENT& e, } } -MODULE* EAGLE_PLUGIN::makeModule( CPTREE& aPackage, const string& aPkgName ) const + +MODULE* EAGLE_PLUGIN::makeModule( wxXmlNode* aPackage, const string& aPkgName ) const { std::unique_ptr m( new MODULE( m_board ) ); m->SetFPID( LIB_ID( aPkgName ) ); - opt_string description = aPackage.get_optional( "description" ); - if( description ) - m->SetDescription( FROM_UTF8( description->c_str() ) ); + // Get the first package item and iterate + wxXmlNode* packageItem = aPackage->GetChildren(); - for( CITER it = aPackage.begin(); it != aPackage.end(); ++it ) + while( packageItem ) { - CPTREE& t = it->second; + const wxString& itemName = packageItem->GetName(); - if( it->first == "wire" ) - packageWire( m.get(), t ); + if( itemName == "description" ) + m->SetDescription( FROM_UTF8( packageItem->GetNodeContent().c_str() ) ); - else if( it->first == "pad" ) - packagePad( m.get(), t ); + else if( itemName == "wire" ) + packageWire( m.get(), packageItem ); - else if( it->first == "text" ) - packageText( m.get(), t ); + else if( itemName == "pad" ) + packagePad( m.get(), packageItem ); - else if( it->first == "rectangle" ) - packageRectangle( m.get(), t ); + else if( itemName == "text" ) + packageText( m.get(), packageItem ); - else if( it->first == "polygon" ) - packagePolygon( m.get(), t ); + else if( itemName == "rectangle" ) + packageRectangle( m.get(), packageItem ); - else if( it->first == "circle" ) - packageCircle( m.get(), t ); + else if( itemName == "polygon" ) + packagePolygon( m.get(), packageItem ); - else if( it->first == "hole" ) - packageHole( m.get(), t ); + else if( itemName == "circle" ) + packageCircle( m.get(), packageItem ); - else if( it->first == "smd" ) - packageSMD( m.get(), t ); + else if( itemName == "hole" ) + packageHole( m.get(), packageItem ); + + else if( itemName == "smd" ) + packageSMD( m.get(), packageItem ); + + packageItem = packageItem->GetNext(); } return m.release(); } -void EAGLE_PLUGIN::packageWire( MODULE* aModule, CPTREE& aTree ) const +void EAGLE_PLUGIN::packageWire( MODULE* aModule, wxXmlNode* aTree ) const { EWIRE w( aTree ); PCB_LAYER_ID layer = kicad_layer( w.layer ); @@ -2203,7 +1234,7 @@ void EAGLE_PLUGIN::packageWire( MODULE* aModule, CPTREE& aTree ) const } -void EAGLE_PLUGIN::packagePad( MODULE* aModule, CPTREE& aTree ) const +void EAGLE_PLUGIN::packagePad( MODULE* aModule, wxXmlNode* aTree ) const { // this is thru hole technology here, no SMDs EPAD e( aTree ); @@ -2291,7 +1322,7 @@ void EAGLE_PLUGIN::packagePad( MODULE* aModule, CPTREE& aTree ) const } -void EAGLE_PLUGIN::packageText( MODULE* aModule, CPTREE& aTree ) const +void EAGLE_PLUGIN::packageText( MODULE* aModule, wxXmlNode* aTree ) const { ETEXT t( aTree ); PCB_LAYER_ID layer = kicad_layer( t.layer ); @@ -2397,7 +1428,7 @@ void EAGLE_PLUGIN::packageText( MODULE* aModule, CPTREE& aTree ) const } -void EAGLE_PLUGIN::packageRectangle( MODULE* aModule, CPTREE& aTree ) const +void EAGLE_PLUGIN::packageRectangle( MODULE* aModule, wxXmlNode* aTree ) const { ERECT r( aTree ); PCB_LAYER_ID layer = kicad_layer( r.layer ); @@ -2430,7 +1461,7 @@ void EAGLE_PLUGIN::packageRectangle( MODULE* aModule, CPTREE& aTree ) const } -void EAGLE_PLUGIN::packagePolygon( MODULE* aModule, CPTREE& aTree ) const +void EAGLE_PLUGIN::packagePolygon( MODULE* aModule, wxXmlNode* aTree ) const { EPOLYGON p( aTree ); PCB_LAYER_ID layer = kicad_layer( p.layer ); @@ -2459,16 +1490,22 @@ void EAGLE_PLUGIN::packagePolygon( MODULE* aModule, CPTREE& aTree ) const dwg->SetTimeStamp( timeStamp( aTree ) ); std::vector pts; - pts.reserve( aTree.size() ); + // TODO: I think there's no way to know a priori the number of children in wxXmlNode :() + // pts.reserve( aTree.size() ); - for( CITER vi = aTree.begin(); vi != aTree.end(); ++vi ) + // Get the first vertex and iterate + wxXmlNode* vertex = aTree->GetChildren(); + + while( vertex ) { - if( vi->first != "vertex" ) // skip node + if( vertex->GetName() != "vertex" ) // skip node continue; - EVERTEX v( vi->second ); + EVERTEX v( vertex ); pts.push_back( wxPoint( kicad_x( v.x ), kicad_y( v.y ) ) ); + + vertex = vertex->GetNext(); } dwg->SetPolyPoints( pts ); @@ -2479,7 +1516,7 @@ void EAGLE_PLUGIN::packagePolygon( MODULE* aModule, CPTREE& aTree ) const } -void EAGLE_PLUGIN::packageCircle( MODULE* aModule, CPTREE& aTree ) const +void EAGLE_PLUGIN::packageCircle( MODULE* aModule, wxXmlNode* aTree ) const { ECIRCLE e( aTree ); PCB_LAYER_ID layer = kicad_layer( e.layer ); @@ -2508,7 +1545,7 @@ void EAGLE_PLUGIN::packageCircle( MODULE* aModule, CPTREE& aTree ) const } -void EAGLE_PLUGIN::packageHole( MODULE* aModule, CPTREE& aTree ) const +void EAGLE_PLUGIN::packageHole( MODULE* aModule, wxXmlNode* aTree ) const { EHOLE e( aTree ); @@ -2538,7 +1575,7 @@ void EAGLE_PLUGIN::packageHole( MODULE* aModule, CPTREE& aTree ) const } -void EAGLE_PLUGIN::packageSMD( MODULE* aModule, CPTREE& aTree ) const +void EAGLE_PLUGIN::packageSMD( MODULE* aModule, wxXmlNode* aTree ) const { ESMD e( aTree ); PCB_LAYER_ID layer = kicad_layer( e.layer ); @@ -2602,7 +1639,7 @@ void EAGLE_PLUGIN::packageSMD( MODULE* aModule, CPTREE& aTree ) const typedef std::vector ZONES; -void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) +void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals ) { ZONES zones; // per net @@ -2610,32 +1647,40 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) int netCode = 1; - for( CITER net = aSignals.begin(); net != aSignals.end(); ++net ) + // Get the first signal and iterate + wxXmlNode* net = aSignals->GetChildren(); + + while( net ) { bool sawPad = false; zones.clear(); - const string& nname = net->second.get( ".name" ); + const string& nname = net->GetAttribute( "name" ).ToStdString(); wxString netName = FROM_UTF8( nname.c_str() ); m_board->Add( new NETINFO_ITEM( m_board, netName, netCode ) ); m_xpath->Value( nname.c_str() ); + // Get the first net item and iterate + wxXmlNode* netItem = net->GetChildren(); + // (contactref | polygon | wire | via)* - for( CITER it = net->second.begin(); it != net->second.end(); ++it ) + while( netItem ) { - if( it->first == "wire" ) + const wxString& itemName = netItem->GetName(); + if( itemName == "wire" ) { m_xpath->push( "wire" ); - EWIRE w( it->second ); + + EWIRE w( netItem ); PCB_LAYER_ID layer = kicad_layer( w.layer ); if( IsCopperLayer( layer ) ) { TRACK* t = new TRACK( m_board ); - t->SetTimeStamp( timeStamp( it->second ) ); + t->SetTimeStamp( timeStamp( netItem ) ); t->SetPosition( wxPoint( kicad_x( w.x1 ), kicad_y( w.y1 ) ) ); t->SetEnd( wxPoint( kicad_x( w.x2 ), kicad_y( w.y2 ) ) ); @@ -2658,10 +1703,10 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) m_xpath->pop(); } - else if( it->first == "via" ) + else if( itemName == "via" ) { m_xpath->push( "via" ); - EVIA v( it->second ); + EVIA v( netItem ); PCB_LAYER_ID layer_front_most = kicad_layer( v.layer_front_most ); PCB_LAYER_ID layer_back_most = kicad_layer( v.layer_back_most ); @@ -2704,7 +1749,7 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) else via->SetViaType( VIA_BLIND_BURIED ); - via->SetTimeStamp( timeStamp( it->second ) ); + via->SetTimeStamp( timeStamp( netItem ) ); wxPoint pos( kicad_x( v.x ), kicad_y( v.y ) ); @@ -2716,14 +1761,13 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) m_xpath->pop(); } - else if( it->first == "contactref" ) + else if( itemName == "contactref" ) { m_xpath->push( "contactref" ); // - CPTREE& attribs = it->second.get_child( "" ); - const string& reference = attribs.get( "element" ); - const string& pad = attribs.get( "pad" ); + const string& reference = netItem->GetAttribute( "element" ).ToStdString(); + const string& pad = netItem->GetAttribute( "pad" ).ToStdString(); string key = makeKey( reference, pad ) ; @@ -2736,11 +1780,11 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) sawPad = true; } - else if( it->first == "polygon" ) + else if( itemName == "polygon" ) { m_xpath->push( "polygon" ); - EPOLYGON p( it->second ); + EPOLYGON p( netItem ); PCB_LAYER_ID layer = kicad_layer( p.layer ); if( IsCopperLayer( layer ) ) @@ -2750,19 +1794,24 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) m_board->Add( zone, ADD_APPEND ); zones.push_back( zone ); - zone->SetTimeStamp( timeStamp( it->second ) ); + zone->SetTimeStamp( timeStamp( netItem ) ); zone->SetLayer( layer ); zone->SetNetCode( netCode ); - for( CITER vi = it->second.begin(); vi != it->second.end(); ++vi ) + // Get the first vertex and iterate + wxXmlNode* vertex = netItem->GetChildren(); + + while( vertex ) { - if( vi->first != "vertex" ) // skip node + if( vertex->GetName() != "vertex" ) // skip node continue; - EVERTEX v( vi->second ); + EVERTEX v( vertex ); // Append the corner zone->AppendCorner( wxPoint( kicad_x( v.x ), kicad_y( v.y ) ) ); + + vertex = vertex->GetNext(); } // If the pour is a cutout it needs to be set to a keepout @@ -2808,6 +1857,8 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) m_xpath->pop(); // "polygon" } + + netItem = netItem->GetNext(); } if( zones.size() && !sawPad ) @@ -2821,6 +1872,9 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) } else netCode++; + + // Get next signal + net = net->GetNext(); } m_xpath->pop(); // "signals.signal" @@ -3031,7 +2085,7 @@ void EAGLE_PLUGIN::cacheLib( const wxString& aLibPath ) if( aLibPath != m_lib_path || load ) { - PTREE doc; + wxXmlNode* doc; LOCALE_IO toggle; // toggles on, then off, the C locale. m_templates.clear(); @@ -3046,42 +2100,55 @@ void EAGLE_PLUGIN::cacheLib( const wxString& aLibPath ) // and is not necessarily utf8. string filename = (const char*) aLibPath.char_str( wxConvFile ); - read_xml( filename, doc, xml_parser::no_comments ); + // Load the document + wxXmlDocument xmlDocument; + wxFileName fn( filename ); + + if( !xmlDocument.Load( fn.GetFullPath() ) ) + THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'" ), + fn.GetFullPath() ) ); + + doc = xmlDocument.GetRoot(); + + wxXmlNode* drawing = MapChildren( doc )["drawing"]; + NODE_MAP drawingChildren = MapChildren( drawing ); // clear the cu map and then rebuild it. clear_cu_map(); m_xpath->push( "eagle.drawing.layers" ); - CPTREE& layers = doc.get_child( "eagle.drawing.layers" ); + wxXmlNode* layers = drawingChildren["layers"]; loadLayerDefs( layers ); m_xpath->pop(); m_xpath->push( "eagle.drawing.library" ); - CPTREE& library = doc.get_child( "eagle.drawing.library" ); + wxXmlNode* library = drawingChildren["library"]; loadLibrary( library, NULL ); m_xpath->pop(); m_mod_time = modtime; } } - catch( file_parser_error fpe ) - { - // for xml_parser_error, what() has the line number in it, - // but no byte offset. That should be an adequate error message. - THROW_IO_ERROR( fpe.what() ); - } - - // Class ptree_error is a base class for xml_parser_error & file_parser_error, - // so one catch should be OK for all errors. - catch( ptree_error pte ) - { - string errmsg = pte.what(); - - errmsg += " @\n"; - errmsg += m_xpath->Contents(); - - THROW_IO_ERROR( errmsg ); - } + catch(...){} + // TODO: Handle exceptions + // catch( file_parser_error fpe ) + // { + // // for xml_parser_error, what() has the line number in it, + // // but no byte offset. That should be an adequate error message. + // THROW_IO_ERROR( fpe.what() ); + // } + // + // // Class ptree_error is a base class for xml_parser_error & file_parser_error, + // // so one catch should be OK for all errors. + // catch( ptree_error pte ) + // { + // string errmsg = pte.what(); + // + // errmsg += " @\n"; + // errmsg += m_xpath->Contents(); + // + // THROW_IO_ERROR( errmsg ); + // } } diff --git a/pcbnew/eagle_plugin.h b/pcbnew/eagle_plugin.h index e550be6f5a..c46dde4fae 100644 --- a/pcbnew/eagle_plugin.h +++ b/pcbnew/eagle_plugin.h @@ -27,48 +27,54 @@ #include #include +#include - -// forward declaration on ptree template so we can confine use of big boost -// headers to only the implementation *.cpp file. - -#include -#include #include +#include -class MODULE; -typedef boost::ptr_map< std::string, MODULE > MODULE_MAP; +typedef std::map< std::string, MODULE* > MODULE_MAP; +typedef std::map< std::string, ENET > NET_MAP; +typedef NET_MAP::const_iterator NET_MAP_CITER; -struct ENET +/// subset of eagle.drawing.board.designrules in the XML document +struct ERULES { - int netcode; - std::string netname; + int psElongationLong; ///< percent over 100%. 0-> not elongated, 100->twice as wide as is tall + ///< Goes into making a scaling factor for "long" pads. - ENET( int aNetCode, const std::string& aNetName ) : - netcode( aNetCode ), - netname( aNetName ) + int psElongationOffset; ///< the offset of the hole within the "long" pad. + + double rvPadTop; ///< top pad size as percent of drill size + // double rvPadBottom; ///< bottom pad size as percent of drill size + + double rlMinPadTop; ///< minimum copper annulus on through hole pads + double rlMaxPadTop; ///< maximum copper annulus on through hole pads + + double rvViaOuter; ///< copper annulus is this percent of via hole + double rlMinViaOuter; ///< minimum copper annulus on via + double rlMaxViaOuter; ///< maximum copper annulus on via + double mdWireWire; ///< wire to wire spacing I presume. + + + ERULES() : + psElongationLong ( 100 ), + psElongationOffset ( 0 ), + rvPadTop ( 0.25 ), + // rvPadBottom ( 0.25 ), + rlMinPadTop ( Mils2iu( 10 ) ), + rlMaxPadTop ( Mils2iu( 20 ) ), + + rvViaOuter ( 0.25 ), + rlMinViaOuter ( Mils2iu( 10 ) ), + rlMaxViaOuter ( Mils2iu( 20 ) ), + mdWireWire ( 0 ) {} - ENET() : - netcode( 0 ) - {} + void parse( wxXmlNode* aRules ); }; -typedef std::map< std::string, ENET > NET_MAP; -typedef NET_MAP::const_iterator NET_MAP_CITER; - -typedef boost::property_tree::ptree PTREE; -typedef const PTREE CPTREE; - -struct EELEMENT; -class XPATH; -struct ERULES; -struct EATTR; -class TEXTE_MODULE; - - /** * Class EAGLE_PLUGIN * works with Eagle 6.x XML board files and footprints to implement the @@ -175,11 +181,11 @@ private: // all these loadXXX() throw IO_ERROR or ptree_error exceptions: - void loadAllSections( CPTREE& aDocument ); - void loadDesignRules( CPTREE& aDesignRules ); - void loadLayerDefs( CPTREE& aLayers ); - void loadPlain( CPTREE& aPlain ); - void loadSignals( CPTREE& aSignals ); + void loadAllSections( wxXmlNode* aDocument ); + void loadDesignRules( wxXmlNode* aDesignRules ); + void loadLayerDefs( wxXmlNode* aLayers ); + void loadPlain( wxXmlNode* aPlain ); + void loadSignals( wxXmlNode* aSignals ); /** * Function loadLibrary @@ -192,10 +198,10 @@ private: * we are loading a *.lbr not a *.brd file and the key used in m_templates is to exclude * the library name. */ - void loadLibrary( CPTREE& aLib, const std::string* aLibName ); + void loadLibrary( wxXmlNode* aLib, const std::string* aLibName ); - void loadLibraries( CPTREE& aLibs ); - void loadElements( CPTREE& aElements ); + void loadLibraries( wxXmlNode* aLibs ); + void loadElements( wxXmlNode* aElements ); void orientModuleAndText( MODULE* m, const EELEMENT& e, const EATTR* nameAttr, const EATTR* valueAttr ); void orientModuleText( MODULE* m, const EELEMENT& e, TEXTE_MODULE* txt, const EATTR* a ); @@ -216,16 +222,16 @@ private: * Function makeModule * creates a MODULE from an Eagle package. */ - MODULE* makeModule( CPTREE& aPackage, const std::string& aPkgName ) const; + MODULE* makeModule( wxXmlNode* aPackage, const std::string& aPkgName ) const; - void packageWire( MODULE* aModule, CPTREE& aTree ) const; - void packagePad( MODULE* aModule, CPTREE& aTree ) const; - void packageText( MODULE* aModule, CPTREE& aTree ) const; - void packageRectangle( MODULE* aModule, CPTREE& aTree ) const; - void packagePolygon( MODULE* aModule, CPTREE& aTree ) const; - void packageCircle( MODULE* aModule, CPTREE& aTree ) const; - void packageHole( MODULE* aModule, CPTREE& aTree ) const; - void packageSMD( MODULE* aModule, CPTREE& aTree ) const; + void packageWire( MODULE* aModule, wxXmlNode* aTree ) const; + void packagePad( MODULE* aModule, wxXmlNode* aTree ) const; + void packageText( MODULE* aModule, wxXmlNode* aTree ) const; + void packageRectangle( MODULE* aModule, wxXmlNode* aTree ) const; + void packagePolygon( MODULE* aModule, wxXmlNode* aTree ) const; + void packageCircle( MODULE* aModule, wxXmlNode* aTree ) const; + void packageHole( MODULE* aModule, wxXmlNode* aTree ) const; + void packageSMD( MODULE* aModule, wxXmlNode* aTree ) const; }; #endif // EAGLE_PLUGIN_H_