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
This commit is contained in:
Jeff Young 2021-09-20 12:36:57 +01:00
parent aa85bb27fa
commit f66654247a
4 changed files with 161 additions and 25 deletions

View File

@ -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<wxString>( aPart, "technology" );
value = parseOptionalAttribute<wxString>( 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<wxString>(aDeviceSet, "name");
name = parseRequiredAttribute<wxString>( aDeviceSet, "name" );
prefix = parseOptionalAttribute<wxString>( aDeviceSet, "prefix" );
uservalue = parseOptionalAttribute<bool>( aDeviceSet, "uservalue" );
@ -1102,3 +1102,21 @@ EDEVICE_SET::EDEVICE_SET( wxXmlNode* aDeviceSet )
*/
}
ECLASS::ECLASS( wxXmlNode* aClass )
{
number = parseRequiredAttribute<wxString>( aClass, "number" );
name = parseRequiredAttribute<wxString>( aClass, "name" );
for( wxXmlNode* child = aClass->GetChildren(); child; child = child->GetNext() )
{
if( child->GetName() == "clearance" )
{
wxString to = parseRequiredAttribute<wxString>( child, "class" );
ECOORD value = parseRequiredAttribute<ECOORD>( child, "value" );
clearanceMap[to] = value;
}
}
}

View File

@ -1054,4 +1054,14 @@ struct EDEVICE_SET
};
struct ECLASS
{
wxString number;
wxString name;
std::map<wxString, ECOORD> clearanceMap;
ECLASS( wxXmlNode* aClass );
};
#endif // _EAGLE_PARSER_H_

View File

@ -68,7 +68,7 @@ Load() TODO's
#include <trigo.h>
#include <math/util.h> // for KiROUND
#include <progress_reporter.h>
#include <project.h>
#include <board.h>
#include <board_design_settings.h>
#include <footprint.h>
@ -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<const wxString, NETCLASSPTR>& 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<ECLASS> 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<const wxString&, ECOORD> 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;

View File

@ -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 <convert_to_biu.h>
#include <io_mgr.h>
#include <layer_ids.h>
#include <netclass.h>
#include <plugins/eagle/eagle_parser.h>
#include <plugins/common/plugin_common_layer_mapping.h>
@ -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<wxString, int> m_eagleLayersIds; ///< Eagle layer ids stored by layer name
std::map<wxString, PCB_LAYER_ID> m_layer_map; ///< Map of Eagle layers to KiCad layers
std::map<wxString, NETCLASSPTR> 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