EAGLE_PLUGIN: read a portion of the design rules and calculate

pad and via sizes from those settings when diameter is not given
in the local object.  Calculation algorithms are inferred from the
UI help of the Eagle program.

For edge connector type of footprints, some copper pads can be on the
back side even within packages.

Flipping of packages to back was not being done correctly, but I think this 
is working now.

There are still remaining issues with text positioning that I am working on.
This commit is contained in:
Dick Hollenbeck 2012-06-04 23:39:37 -05:00
parent e7817f3e84
commit 27d6310cbf
2 changed files with 228 additions and 48 deletions

View File

@ -77,6 +77,20 @@ Load() TODO's
using namespace boost::property_tree;
typedef EAGLE_PLUGIN::BIU BIU;
typedef PTREE::const_assoc_iterator CA_ITER;
typedef PTREE::const_iterator CITER;
typedef std::pair<CA_ITER, CA_ITER> CA_ITER_RANGE;
typedef MODULE_MAP::iterator MODULE_ITER;
typedef MODULE_MAP::const_iterator MODULE_CITER;
typedef boost::optional<std::string> opt_string;
typedef boost::optional<int> opt_int;
typedef boost::optional<double> opt_double;
typedef boost::optional<bool> opt_bool;
/// segment (element) of our XPATH into the Eagle XML document tree in PTREE form.
struct TRIPLET
{
@ -160,20 +174,6 @@ public:
};
typedef EAGLE_PLUGIN::BIU BIU;
typedef PTREE::const_assoc_iterator CA_ITER;
typedef PTREE::const_iterator CITER;
typedef std::pair<CA_ITER, CA_ITER> CA_ITER_RANGE;
typedef MODULE_MAP::iterator MODULE_ITER;
typedef MODULE_MAP::const_iterator MODULE_CITER;
typedef boost::optional<std::string> opt_string;
typedef boost::optional<int> opt_int;
typedef boost::optional<double> opt_double;
typedef boost::optional<bool> opt_bool;
/**
* Function parseOptionalBool
* returns an opt_bool and sets it true or false according to the presence
@ -205,13 +205,23 @@ struct EROT
bool spin;
double degrees;
EROT() : mirror( false ), spin( false ), degrees( 0 ) {}
EROT() :
mirror( false ),
spin( false ),
degrees( 0 )
{}
EROT( double aDegrees ) :
mirror( false ),
spin( false ),
degrees( aDegrees )
{}
};
typedef boost::optional<EROT> opt_erot;
/// parse an Eagle XML "rot" field. Unfortunately the DTD seems not to explain
/// this format very well. R[S][M]<degrees>. Examples: "R90", "MR180", "SR180"
/// this format very well. [S][M]R<degrees>. Examples: "R90", "MR180", "SR180"
static EROT erot( const std::string& aRot )
{
EROT rot;
@ -239,12 +249,30 @@ static opt_erot parseOptionalEROT( CPTREE& attribs )
/// Eagle wire
struct EWIRE
{
double x1;
double y1;
double x2;
double y2;
double width;
int layer;
double x1;
double y1;
double x2;
double y2;
double width;
int 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 );
};
@ -280,7 +308,30 @@ EWIRE::EWIRE( CPTREE& aWire )
width = attribs.get<double>( "width" );
layer = attribs.get<int>( "layer" );
// ignoring extent, style, curve and cap
curve = attribs.get_optional<double>( "curve" );
opt_string s = attribs.get_optional<std::string>( "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<std::string>( "cap" );
if( s )
{
if( !s->compare( "round" ) )
cap = EWIRE::ROUND;
else if( !s->compare( "flat" ) )
cap = EWIRE::FLAT;
}
// ignoring extent
}
@ -421,6 +472,11 @@ struct EATTR
};
EATTR( CPTREE& aTree );
EATTR( const opt_erot& aOptRot, double childAngle ) :
rot( aOptRot )
{
}
};
/**
@ -900,6 +956,85 @@ ELAYER::ELAYER( CPTREE& aLayer )
}
/// parse an eagle distance which is either straight mm or mils if there is "mil" suffix.
static double parseEagle( const std::string& aDistance )
{
double ret = strtod( aDistance.c_str(), NULL );
if( aDistance.npos != aDistance.find( "mil" ) )
ret = IU_PER_MILS * ret;
else
ret = IU_PER_MM * ret;
return ret;
}
/// subset of eagle.drawing.board.designrules in the XML document
struct ERULES
{
int psElongationLong; ///< percent over 100%. 0-> not elongated, 100->twice as wide as is tall
///< Goes into making a scaling factor for "long" pads.
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
ERULES() :
psElongationLong ( 100 ),
rvPadTop ( 0.25 ),
// rvPadBottom ( 0.25 ),
rlMinPadTop ( Mils2iu( 10 ) ),
rlMaxPadTop ( Mils2iu( 20 ) ),
rvViaOuter ( 0.25 ),
rlMinViaOuter ( Mils2iu( 10 ) ),
rlMaxViaOuter ( Mils2iu( 20 ) )
{}
void parse( CPTREE& aRules );
};
void ERULES::parse( CPTREE& aRules )
{
for( CITER it = aRules.begin(); it != aRules.end(); ++it )
{
if( it->first.compare( "param" ) )
continue;
CPTREE& attribs = it->second.get_child( "<xmlattr>" );
const std::string& name = attribs.get<std::string>( "name" );
if( !name.compare( "psElongationLong" ) )
psElongationLong = attribs.get<int>( "value" );
else if( !name.compare( "psElongationOffset" ) )
psElongationOffset = attribs.get<int>( "value" );
else if( !name.compare( "rvPadTop" ) )
rvPadTop = attribs.get<double>( "value" );
else if( !name.compare( "rlMinPadTop" ) )
rlMinPadTop = parseEagle( attribs.get<std::string>( "value" ) );
else if( !name.compare( "rlMaxPadTop" ) )
rlMaxPadTop = parseEagle( attribs.get<std::string>( "value" ) );
else if( !name.compare( "rvViaOuter" ) )
rvViaOuter = attribs.get<double>( "value" );
else if( !name.compare( "rlMinViaOuter" ) )
rlMinViaOuter = parseEagle( attribs.get<std::string>( "value" ) );
else if( !name.compare( "rlMaxViaOuter" ) )
rlMaxViaOuter = parseEagle( attribs.get<std::string>( "value" ) );
}
}
/// Assemble a two part key as a simple concatonation of aFirst and aSecond parts,
/// using a separator.
static inline std::string makeKey( const std::string& aFirst, const std::string& aSecond )
@ -918,6 +1053,7 @@ static inline unsigned long timeStamp( CPTREE& aTree )
EAGLE_PLUGIN::EAGLE_PLUGIN() :
m_rules( new ERULES() ),
m_xpath( new XPATH() )
{
init( NULL );
@ -926,6 +1062,7 @@ EAGLE_PLUGIN::EAGLE_PLUGIN() :
EAGLE_PLUGIN::~EAGLE_PLUGIN()
{
delete m_rules;
delete m_xpath;
}
@ -1026,21 +1163,34 @@ void EAGLE_PLUGIN::init( PROPERTIES* aProperties )
mm_per_biu = 1/IU_PER_MM;
biu_per_mm = IU_PER_MM;
delete m_rules;
m_rules = new ERULES();
}
void EAGLE_PLUGIN::loadAllSections( CPTREE& aDoc )
{
CPTREE& drawing = aDoc.get_child( "eagle.drawing" );
CPTREE& board = drawing.get_child( "board" );
m_xpath->push( "eagle.drawing" );
{
m_xpath->push( "board" );
CPTREE& designrules = board.get_child( "designrules" );
loadDesignRules( designrules );
m_xpath->pop();
}
{
CPTREE& layers = drawing.get_child( "layers" );
loadLayerDefs( layers );
}
{
CPTREE& board = drawing.get_child( "board" );
m_xpath->push( "board" );
CPTREE& plain = board.get_child( "plain" );
@ -1062,6 +1212,14 @@ void EAGLE_PLUGIN::loadAllSections( CPTREE& aDoc )
}
void EAGLE_PLUGIN::loadDesignRules( CPTREE& aDesignRules )
{
m_xpath->push( "designrules" );
m_rules->parse( aDesignRules );
m_xpath->pop(); // "designrules"
}
void EAGLE_PLUGIN::loadLayerDefs( CPTREE& aLayers )
{
m_xpath->push( "layers.layer" );
@ -1428,7 +1586,7 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements )
}
#if defined(DEBUG)
if( !e.name.compare( "ATMEGA328" ) )
if( !e.name.compare( "IC2" ) )
{
int breakhere = 1;
(void) breakhere;
@ -1460,14 +1618,17 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements )
if( e.rot )
{
m->SetOrientation( e.rot->degrees * 10 );
if( e.rot->mirror )
{
m->Flip( m->GetPosition() );
}
m->SetOrientation( e.rot->degrees * 10 );
}
// initalize these to default values incase the <attribute> elements are not present.
EATTR reference( e.rot, m->Reference().GetOrientation()/10 );
EATTR value( e.rot, m->Value().GetOrientation()/10 );
m_xpath->push( "attribute", "name" );
// VALUE and NAME can have something like our text "effects" overrides
@ -1483,19 +1644,20 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements )
EATTR a( ait->second );
TEXTE_MODULE* txt;
if( !a.name.compare( "NAME" ) )
txt = &m->Reference();
reference = a;
else if( !a.name.compare( "VALUE" ) )
txt = &m->Value();
else
{
// our understanding of file format is incomplete?
continue;
}
value = a;
}
m_xpath->Value( a.name.c_str() );
m_xpath->pop(); // "attribute"
for( int i=0; i<2; ++i )
{
const EATTR& a = i ? reference: value;
TEXTE_MODULE* txt = i ? &m->Reference() : &m->Value();
m_xpath->Value( a.name.c_str() ); // breadcrum: element[name=*]
if( a.value )
{
@ -1531,10 +1693,14 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements )
// present, and this zero rotation becomes an override to the
// package's text field. If they did not want zero, they specify
// what they want explicitly.
EROT rot;
EROT rot; // initialize degrees to zero here
if( a.rot )
{
rot = *a.rot;
txt->SetMirrored( rot.mirror );
}
if( rot.spin || (rot.degrees != 180 && rot.degrees != 270) )
{
@ -1548,11 +1714,7 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements )
txt->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
txt->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
}
txt->SetMirrored( rot.mirror );
}
m_xpath->pop(); // "attribute"
}
m_xpath->pop(); // "elements.element"
@ -1684,6 +1846,7 @@ void EAGLE_PLUGIN::packagePad( MODULE* aModule, CPTREE& aTree ) const
}
else
{
#if 0
// The pad size is optional in the eagle DTD, so we must guess.
// Supply something here that is a minimum copper surround, or otherwise
// 120% of drillz whichever is greater. But for PAD_OVAL, we can use
@ -1702,13 +1865,22 @@ void EAGLE_PLUGIN::packagePad( MODULE* aModule, CPTREE& aTree ) const
int diameter = std::max( drillz + 2 * min_copper, int( drillz * 1.2 ) );
pad->SetSize( wxSize( diameter, diameter ) );
#else
double drillz = pad->GetDrillSize().x;
double annulus = drillz * m_rules->rvPadTop; // copper annulus, eagle "restring"
annulus = Clamp( m_rules->rlMinPadTop, annulus, m_rules->rlMaxPadTop );
int diameter = KiROUND( drillz + 2 * annulus );
pad->SetSize( wxSize( KiROUND( diameter ), KiROUND( diameter ) ) );
#endif
}
if( pad->GetShape() == PAD_OVAL )
{
// The Eagle "long" pad seems to be tall, "width = height x 4/3" apparently.
// The Eagle "long" pad is wider than it is tall,
// m_elongation is percent elongation
wxSize sz = pad->GetSize();
sz.x = (sz.x * 4)/3;
sz.x = ( sz.x * ( 100 + m_rules->psElongationLong ) ) / 100;
pad->SetSize( sz );
}
@ -1995,7 +2167,11 @@ void EAGLE_PLUGIN::packageSMD( MODULE* aModule, CPTREE& aTree ) const
pad->SetSize( wxSize( kicad( e.dx ), kicad( e.dy ) ) );
pad->SetLayer( layer );
pad->SetLayerMask( LAYER_FRONT | SOLDERPASTE_LAYER_FRONT | SOLDERMASK_LAYER_FRONT );
if( layer == LAYER_N_FRONT )
pad->SetLayerMask( LAYER_FRONT | SOLDERPASTE_LAYER_FRONT | SOLDERMASK_LAYER_FRONT );
else if( layer == LAYER_N_BACK )
pad->SetLayerMask( LAYER_BACK | SOLDERPASTE_LAYER_BACK | SOLDERMASK_LAYER_BACK );
// Optional according to DTD
if( e.roundness ) // set set shape to PAD_RECT above, in case roundness is not present
@ -2082,8 +2258,6 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals )
via->SetLayerPair( layer_front_most, layer_back_most );
// via diameters are externally controllable, not usually in a board:
// http://www.eaglecentral.ca/forums/index.php/mv/msg/34704/119478/
if( v.diam )
{
int kidiam = kicad( *v.diam );
@ -2091,7 +2265,9 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals )
}
else
{
int diameter = std::max( drillz + 2 * Mils2iu( 6 ), int( drillz * 2.0 ) );
double annulus = drillz * m_rules->rvViaOuter; // eagle "restring"
annulus = Clamp( m_rules->rlMinViaOuter, annulus, m_rules->rlMaxViaOuter );
int diameter = KiROUND( drillz + 2 * annulus );
via->SetWidth( diameter );
}

View File

@ -63,6 +63,8 @@ typedef boost::property_tree::ptree PTREE;
typedef const PTREE CPTREE;
class XPATH;
struct ERULES;
/**
* Class EAGLE_PLUGIN
@ -107,6 +109,7 @@ public:
private:
ERULES* m_rules; ///< Eagle design rules.
XPATH* m_xpath; ///< keeps track of what we are working on within
///< XML document during a Load().
@ -173,6 +176,7 @@ 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 );