From 9cf934ef17f91a6135b9eedf92744d917ecc4e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Garc=C3=ADa=20Montoro?= Date: Fri, 7 Apr 2017 13:40:34 +0200 Subject: [PATCH] Moves Eagle XML parser to common, replacing boost with wx. All E'STRUCTS' are moved to common except for ERULES (which is specific to pcbnew and needs its internal units), still in pcbnew/eagle_plugin.{h,cpp} In order to get rid of another boost dependency, this also changes the parsing of the XML from Boost.PropertyTree to wxXml. To replace boost::optional, an OPTIONAL_XML_ATTRIBUTE class has been implemented. This could be replaced with std::optional when C++17 is ready. --- common/CMakeLists.txt | 1 + common/eagle_parser.cpp | 649 ++++++++++++++++++ include/eagle_parser.h | 690 +++++++++++++++++++ pcbnew/eagle_plugin.cpp | 1439 +++++++-------------------------------- pcbnew/eagle_plugin.h | 100 +-- 5 files changed, 1646 insertions(+), 1233 deletions(-) create mode 100644 common/eagle_parser.cpp create mode 100644 include/eagle_parser.h 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_