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:
parent
e7817f3e84
commit
27d6310cbf
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in New Issue