From f66654247a3625f4c8d0f7351e1c87f5a6648cae Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Mon, 20 Sep 2021 12:36:57 +0100 Subject: [PATCH] Read netclasses when importing Eagle projects. Also creates a DRU file with the various netclass-to-netclass clearance rules from the Eagle clearance matrix. Fixes https://gitlab.com/kicad/code/kicad/issues/1774 --- common/plugins/eagle/eagle_parser.cpp | 36 ++++++-- common/plugins/eagle/eagle_parser.h | 10 +++ pcbnew/plugins/eagle/eagle_plugin.cpp | 123 +++++++++++++++++++++++--- pcbnew/plugins/eagle/eagle_plugin.h | 17 ++-- 4 files changed, 161 insertions(+), 25 deletions(-) diff --git a/common/plugins/eagle/eagle_parser.cpp b/common/plugins/eagle/eagle_parser.cpp index 5d091207ad..d215426aa9 100644 --- a/common/plugins/eagle/eagle_parser.cpp +++ b/common/plugins/eagle/eagle_parser.cpp @@ -278,8 +278,9 @@ wxPoint ConvertArcCenter( const wxPoint& aStart, const wxPoint& aEnd, double aAn if( !std::isnormal( dlen ) || !std::isnormal( aAngle ) ) { - THROW_IO_ERROR( - wxString::Format( _( "Invalid Arc with radius %f and angle %f" ), dlen, aAngle ) ); + THROW_IO_ERROR( wxString::Format( _( "Invalid Arc with radius %f and angle %f" ), + dlen, + aAngle ) ); } double dist = dlen / ( 2 * tan( DEG2RAD( aAngle ) / 2 ) ); @@ -919,15 +920,14 @@ EPART::EPART( wxXmlNode* aPart ) technology = parseOptionalAttribute( aPart, "technology" ); value = parseOptionalAttribute( aPart, "value" ); - for( auto child = aPart->GetChildren(); child; child = child->GetNext() ) + for( wxXmlNode* child = aPart->GetChildren(); child; child = child->GetNext() ) { - if( child->GetName() == "attribute" ) { std::string aname, avalue; - for( auto x = child->GetAttributes(); x; x = x->GetNext() ) - { + for( wxXmlAttribute* x = child->GetAttributes(); x; x = x->GetNext() ) + { if( x->GetName() == "name" ) aname = x->GetValue(); else if( x->GetName() == "value" ) @@ -940,9 +940,9 @@ EPART::EPART( wxXmlNode* aPart ) else if( child->GetName() == "variant" ) { std::string aname, avalue; - for( auto x = child->GetAttributes(); x; x = x->GetNext() ) - { + for( wxXmlAttribute* x = child->GetAttributes(); x; x = x->GetNext() ) + { if( x->GetName() == "name" ) aname = x->GetValue(); else if( x->GetName() == "value" ) @@ -1077,7 +1077,7 @@ EDEVICE_SET::EDEVICE_SET( wxXmlNode* aDeviceSet ) > */ - name = parseRequiredAttribute(aDeviceSet, "name"); + name = parseRequiredAttribute( aDeviceSet, "name" ); prefix = parseOptionalAttribute( aDeviceSet, "prefix" ); uservalue = parseOptionalAttribute( aDeviceSet, "uservalue" ); @@ -1102,3 +1102,21 @@ EDEVICE_SET::EDEVICE_SET( wxXmlNode* aDeviceSet ) */ } + + +ECLASS::ECLASS( wxXmlNode* aClass ) +{ + number = parseRequiredAttribute( aClass, "number" ); + name = parseRequiredAttribute( aClass, "name" ); + + for( wxXmlNode* child = aClass->GetChildren(); child; child = child->GetNext() ) + { + if( child->GetName() == "clearance" ) + { + wxString to = parseRequiredAttribute( child, "class" ); + ECOORD value = parseRequiredAttribute( child, "value" ); + + clearanceMap[to] = value; + } + } +} diff --git a/common/plugins/eagle/eagle_parser.h b/common/plugins/eagle/eagle_parser.h index 52339b8097..1472bea2ba 100644 --- a/common/plugins/eagle/eagle_parser.h +++ b/common/plugins/eagle/eagle_parser.h @@ -1054,4 +1054,14 @@ struct EDEVICE_SET }; +struct ECLASS +{ + wxString number; + wxString name; + std::map clearanceMap; + + ECLASS( wxXmlNode* aClass ); +}; + + #endif // _EAGLE_PARSER_H_ diff --git a/pcbnew/plugins/eagle/eagle_plugin.cpp b/pcbnew/plugins/eagle/eagle_plugin.cpp index 8dc38d1ae8..b14f532a93 100644 --- a/pcbnew/plugins/eagle/eagle_plugin.cpp +++ b/pcbnew/plugins/eagle/eagle_plugin.cpp @@ -68,7 +68,7 @@ Load() TODO's #include #include // for KiROUND #include - +#include #include #include #include @@ -402,13 +402,34 @@ BOARD* EAGLE_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe, designSettings.m_ViasMinAnnularWidth = m_min_annulus; if( m_rules->mdWireWire ) - { - NETCLASS* defaultNetclass = designSettings.GetDefault(); - int clearance = KiROUND( m_rules->mdWireWire ); + designSettings.m_MinClearance = KiROUND( m_rules->mdWireWire ); - if( clearance < defaultNetclass->GetClearance() ) - defaultNetclass->SetClearance( clearance ); - } + NETCLASS defaults( "dummy" ); + + auto finishNetclass = + [&]( NETCLASSPTR netclass ) + { + if( netclass->GetTrackWidth() == INT_MAX ) + netclass->SetTrackWidth( defaults.GetTrackWidth() ); + + if( netclass->GetViaDiameter() == INT_MAX ) + netclass->SetViaDiameter( defaults.GetViaDiameter() ); + + if( netclass->GetViaDrill() == INT_MAX ) + netclass->SetViaDrill( defaults.GetViaDrill() ); + }; + + finishNetclass( designSettings.GetNetClasses().GetDefault() ); + + for( const std::pair& entry : designSettings.GetNetClasses() ) + finishNetclass( entry.second ); + + m_board->m_LegacyNetclassesLoaded = true; + m_board->m_LegacyDesignSettingsLoaded = true; + + fn.SetExt( "kicad_dru" ); + wxFile rulesFile( fn.GetFullPath(), wxFile::write ); + rulesFile.Write( m_customRules ); // should be empty, else missing m_xpath->pop() wxASSERT( m_xpath->Contents().size() == 0 ); @@ -507,6 +528,7 @@ void EAGLE_PLUGIN::loadAllSections( wxXmlNode* aDoc ) wxXmlNode* designrules = boardChildren["designrules"]; wxXmlNode* layers = drawingChildren["layers"]; wxXmlNode* plain = boardChildren["plain"]; + wxXmlNode* classes = boardChildren["classes"]; wxXmlNode* signals = boardChildren["signals"]; wxXmlNode* libs = boardChildren["libraries"]; wxXmlNode* elems = boardChildren["elements"]; @@ -555,6 +577,7 @@ void EAGLE_PLUGIN::loadAllSections( wxXmlNode* aDoc ) m_xpath->push( "board" ); loadPlain( plain ); + loadClasses( classes ); loadSignals( signals ); loadLibraries( libs ); loadElements( elems ); @@ -2382,14 +2405,74 @@ void EAGLE_PLUGIN::deleteTemplates() } +void EAGLE_PLUGIN::loadClasses( wxXmlNode* aClasses ) +{ + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + + m_xpath->push( "classes.class", "number" ); + + std::vector eClasses; + wxXmlNode* classNode = aClasses->GetChildren(); + + while( classNode ) + { + checkpoint(); + + ECLASS eClass( classNode ); + NETCLASSPTR netclass; + + if( eClass.name.CmpNoCase( "default" ) == 0 ) + { + netclass = bds.GetNetClasses().GetDefault(); + } + else + { + netclass.reset( new NETCLASS( eClass.name ) ); + m_board->GetDesignSettings().GetNetClasses().Add( netclass ); + } + + netclass->SetTrackWidth( INT_MAX ); + netclass->SetViaDiameter( INT_MAX ); + netclass->SetViaDrill( INT_MAX ); + + eClasses.emplace_back( eClass ); + m_classMap[ eClass.number ] = netclass; + + // Get next class + classNode = classNode->GetNext(); + } + + m_customRules = "(version 1)"; + + for( ECLASS& eClass : eClasses ) + { + for( std::pair entry : eClass.clearanceMap ) + { + wxString rule; + rule.Printf( "(rule \"class %s:%s\"\n" + " (condition \"A.NetClass == '%s' && B.NetClass == '%s'\")\n" + " (constraint clearance (min %smm)))\n", + eClass.number, + entry.first, + eClass.name, + m_classMap[ entry.first ]->GetName(), + StringFromValue( EDA_UNITS::MILLIMETRES, entry.second.ToPcbUnits() ) ); + + m_customRules += "\n" + rule; + } + } + + m_xpath->pop(); // "classes.class" +} + + void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals ) { ZONES zones; // per net + int netCode = 1; m_xpath->push( "signals.signal", "name" ); - int netCode = 1; - // Get the first signal and iterate wxXmlNode* net = aSignals->GetChildren(); @@ -2402,7 +2485,18 @@ void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals ) zones.clear(); const wxString& netName = escapeName( net->GetAttribute( "name" ) ); - m_board->Add( new NETINFO_ITEM( m_board, netName, netCode ) ); + NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, netName, netCode ); + NETCLASSPTR netclass; + + if( net->HasAttribute( "class" ) ) + { + netclass = m_classMap[ net->GetAttribute( "class" ) ]; + + netclass->Add( netName ); + netInfo->SetNetClass( netclass ); + } + + m_board->Add( netInfo ); m_xpath->Value( netName.c_str() ); @@ -2435,6 +2529,9 @@ void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals ) if( width < m_min_trace ) m_min_trace = width; + if( netclass && width < netclass->GetTrackWidth() ) + netclass->SetTrackWidth( width ); + if( w.curve ) { center = ConvertArcCenter( wxPoint( kicad_x( w.x1 ), kicad_y( w.y1 ) ), @@ -2538,9 +2635,15 @@ void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals ) if( kidiam < m_min_via ) m_min_via = kidiam; + if( netclass && kidiam < netclass->GetViaDiameter() ) + netclass->SetViaDiameter( kidiam ); + if( drillz < m_min_hole ) m_min_hole = drillz; + if( netclass && drillz < netclass->GetViaDrill() ) + netclass->SetViaDrill( drillz ); + if( ( kidiam - drillz ) / 2 < m_min_annulus ) m_min_annulus = ( kidiam - drillz ) / 2; diff --git a/pcbnew/plugins/eagle/eagle_plugin.h b/pcbnew/plugins/eagle/eagle_plugin.h index 9a4830e34e..6f03f3cc05 100644 --- a/pcbnew/plugins/eagle/eagle_plugin.h +++ b/pcbnew/plugins/eagle/eagle_plugin.h @@ -1,6 +1,3 @@ -#ifndef EAGLE_PLUGIN_H_ -#define EAGLE_PLUGIN_H_ - /* * This program source code file is part of KiCad, a free EDA CAD application. * @@ -25,9 +22,13 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#ifndef EAGLE_PLUGIN_H +#define EAGLE_PLUGIN_H + #include #include #include +#include #include #include @@ -218,6 +219,7 @@ private: void loadDesignRules( wxXmlNode* aDesignRules ); void loadLayerDefs( wxXmlNode* aLayers ); void loadPlain( wxXmlNode* aPlain ); + void loadClasses( wxXmlNode* aClasses ); void loadSignals( wxXmlNode* aSignals ); /** @@ -286,6 +288,9 @@ private: std::map m_eagleLayersIds; ///< Eagle layer ids stored by layer name std::map m_layer_map; ///< Map of Eagle layers to KiCad layers + std::map m_classMap; ///< Eagle class number to KiCad netclass + wxString m_customRules; + ERULES* m_rules; ///< Eagle design rules. XPATH* m_xpath; ///< keeps track of what we are working on within ///< XML document during a Load(). @@ -299,8 +304,8 @@ private: ///< lookup key is either libname.packagename or simply ///< packagename if FootprintLoad() or FootprintEnumberate() - const PROPERTIES* m_props; ///< passed via Save() or Load(), no ownership, may be NULL. - BOARD* m_board; ///< which BOARD is being worked on, no ownership here + const PROPERTIES* m_props; ///< passed via Save() or Load(), no ownership, may be NULL. + BOARD* m_board; ///< which BOARD is being worked on, no ownership here PROGRESS_REPORTER* m_progressReporter; ///< optional; may be nullptr unsigned m_doneCount; @@ -316,4 +321,4 @@ private: wxDateTime m_mod_time; }; -#endif // EAGLE_PLUGIN_H_ +#endif // EAGLE_PLUGIN_H