Gerbview: Added support for complex definitions of parameters (like $3=$2+5/2) in aperture macros and primitives macro.

This commit is contained in:
jean-pierre charras 2010-12-01 20:31:58 +01:00
commit 8c4075216e
9 changed files with 374 additions and 66 deletions

View File

@ -4,6 +4,20 @@ KiCad ChangeLog 2010
Please add newer entries at the top, list the date and your name with
email address.
2010-dec-01, UPDATE Jean-Pierre Charras <jean-pierre.charras@gipsa-lab.inpg.fr>
================================================================================
Gerbview:
Added support for complex definitions of parameters in aperture macros and primitives macro
(a complex definition in a parameter that is calculated by an arithmetical expression)
Gerbview should now have a decent support of Gerber language.
Currently only the obscure knockout command is not supported (I have no motivation to do that)
Other "bug":
scale in A and B axis is poorly supported: coordinates are scaled, but shapes can have problem:
fro instance, a circle is drawn as a circle when A and B scales are different,
and perhaps should be an ellipse.
On the other hand, Gerber doc is not clear about the meaning of A and B scale.
(Alas! Gerber doc is not clear about most of advanced commands)
2010-nov-19 UPDATE Wayne Stambaugh <stambaughw@verizon.net>
================================================================================
++EESchema

View File

@ -28,39 +28,126 @@
*/
#include "class_am_param.h"
#include "class_aperture_macro.h"
extern int ReadInt( char*& text, bool aSkipSeparator = true );
extern double ReadDouble( char*& text, bool aSkipSeparator = true );
/*Class AM_PARAM
* holds a parameter for an "aperture macro" as defined within
* standard RS274X. The \a value field can be a constant, i.e. "immediate"
* parameter or it may not be used if this param is going to defer to the
* referencing aperture macro. In that case, the \a index field is an index
* into the aperture macro's parameters.
/* Class AM_PARAM
* holds a parameter value for an "aperture macro" as defined within
* standard RS274X. The parameter can be a constant, i.e. "immediate" parameter,
* or depend on some defered values, defined in a D_CODE, by the ADD command.
* Note the actual value could need an evaluation from an arithmetical expression
* items in the expression are stored in .
* A simple definition is just a value stored in one item in m_paramStack
*/
AM_PARAM::AM_PARAM( )
{
m_index = -1;
}
/**
* Function IsImmediate
* tests if this AM_PARAM holds an immediate parameter or has parameter
* held by an owning D_CODE.
*/
bool AM_PARAM::IsImmediate() const
{
bool isimmediate = true;
for( unsigned ii = 0; ii < m_paramStack.size(); ii++ )
{
if( m_paramStack[ii].IsDefered() )
{ // a defered value is found in operand list,
// so the parameter is not immediate
isimmediate = false;
break;
}
}
return isimmediate;
}
double AM_PARAM::GetValue( const D_CODE* aDcode ) const
{
if( IsImmediate() )
return value;
else
double paramvalue = 0.0;
double curr_value = 0.0;
parm_item_type state = POPVALUE;
for( unsigned ii = 0; ii < m_paramStack.size(); ii++ )
{
// the first one was numbered 1, not zero, as in $1, see page 19 of spec.
unsigned ndx = GetIndex();
wxASSERT( aDcode );
AM_PARAM_ITEM item = m_paramStack[ii];
switch( item.GetType() )
{
case ADD:
case SUB:
case MUL:
case DIV: // just an operator for next parameter value: store it
state = item.GetType();
break;
case PUSHPARM:
// get the parameter from the aDcode
if( ndx <= aDcode->GetParamCount() )
return aDcode->GetParam( ndx );
else
if( aDcode && item.GetIndex() <= aDcode->GetParamCount() )
curr_value = aDcode->GetParam( item.GetIndex() );
else // Get parameter from local param definition
{
wxASSERT( GetIndex() <= aDcode->GetParamCount() );
return 0.0;
const APERTURE_MACRO * am_parent = aDcode->GetMacro();
curr_value = am_parent->GetLocalParam( aDcode, item.GetIndex() );
}
// Fall through
case PUSHVALUE: // a value is on the stack:
if( item.GetType() == PUSHVALUE )
curr_value = item.GetValue();
switch( state )
{
case POPVALUE:
paramvalue = curr_value;
break;
case ADD:
paramvalue += curr_value;
break;
case SUB:
paramvalue -= curr_value;
break;
case MUL:
paramvalue *= curr_value;
break;
case DIV:
paramvalue /= curr_value;
break;
default:
wxLogDebug( wxT( "AM_PARAM::GetValue() : unexpected operator\n" ) );
break;
}
break;
default:
wxLogDebug( wxT( "AM_PARAM::GetValue(): unexpected type\n" ) );
break;
}
}
return paramvalue;
}
/**
* add an operator/operand to the current stack
* aType = NOP, PUSHVALUE, PUSHPARM, ADD, SUB, MUL, DIV, EQUATE
* aValue required only for PUSHVALUE (double) or PUSHPARM (int) aType.
*/
void AM_PARAM::PushOperator( parm_item_type aType, double aValue )
{
AM_PARAM_ITEM item( aType, aValue);
m_paramStack.push_back( item );
}
void AM_PARAM::PushOperator( parm_item_type aType, int aValue )
{
AM_PARAM_ITEM item( aType, aValue);
m_paramStack.push_back( item );
}
/**
* Function ReadParam
@ -70,6 +157,7 @@ double AM_PARAM::GetValue( const D_CODE* aDcode ) const
* a reference to an aperture definition parameter value: $1 ot $3 ...
* a parameter definition can be complex and have operators between numbers and/or other parameter
* like $1+3 or $2x2..
* Note minus sign is not always an operator. It can be the sign of a value.
* Parameters are separated by a comma ( of finish by *)
* @param aText = pointer to the parameter to read. Will be modified to point to the next field
* @return true if a param is read, or false
@ -77,25 +165,79 @@ double AM_PARAM::GetValue( const D_CODE* aDcode ) const
bool AM_PARAM::ReadParam( char*& aText )
{
bool found = false;
int ivalue;
double dvalue;
bool end = false;
if( *aText == '$' ) // value defined later, in aperture description
while( !end )
{
switch( *aText )
{
case ',':
aText++;
// fall through
case 0: // EOL
case '*': // Terminator in a gerber command
end = true;
break;
case ' ':
aText++;
break;
case '$':
// defered value defined later, in ADD command which define defered parameters
++aText;
SetIndex( ReadInt( aText, false ) );
ivalue = ReadInt( aText, false );
if( m_index < 1 )
SetIndex( ivalue );
PushOperator( PUSHPARM, ivalue );
found = true;
break;
case '/':
PushOperator( DIV );
aText++;
break;
case 'x':
case 'X':
PushOperator( MUL );
aText++;
break;
case '-':
case '+':
// Test if this is an operator between 2 params, or the sign of a value
if( m_paramStack.size() > 0 && !m_paramStack.back().IsOperator() )
{ // Seems an operator
PushOperator( *aText == '+' ? ADD : SUB );
aText++;
}
else
{
SetValue( ReadDouble( aText, false ) );
{ // seems the sign of a value
dvalue = ReadDouble( aText, false );
PushOperator( PUSHVALUE, dvalue );
found = true;
}
break;
// Skip extra characters and separator
while( *aText && (*aText != ',') && (*aText != '*') )
case '=': // A local definition found like $4=$3/2
// At this point, one defered parameter is expected to be read.
// this parameter value (the index) is stored in m_index.
// The list of items is cleared
aText++;
m_paramStack.clear();
found = false;
break;
if( *aText == ',' )
aText++;
default:
dvalue = ReadDouble( aText, false );
PushOperator( PUSHVALUE, dvalue );
found = true;
break;
}
}
return found;
}

View File

@ -103,50 +103,151 @@
#include <vector>
#include "dcode.h"
/*
Values of a parameter can be the result of an arithmetic operation,
between immediate values and defered value.
From an idea found in Gerbv, here is the way to evaluate a parameter.
a AM_PARAM_ITEM holds info about operands and operators in a parameter definition
( a AM_PARAM ) like $2+$2-$3-$3/2
There is no precedence defined in gerber RS274X, so actual value is calculated step to step.
Parameter definition is described by a very primitive assembler.
This "program "should describe how to calculate the parameter.
The assembler consist of 8 instruction intended for a stackbased machine.
The instructions are:
NOP, PUSHVALUE, PUSHPARM, ADD, SUB, MUL, DIV, EQUATE
The instructions
----------------
NOP : The no operation. This is the default instruction and are
added as a security measure.
PUSHVALUE : Pushes an arithmetical value on the stack. This machine only works with floats
on the stack.
PUSHPARM: Pushes a defered parameter onto the stack. Gerber aperture macros accepts
parameters to be set when later declared, so the same macro can
be used at several instances. Which parameter to be set is an integer
and starts with 1. definition is like $1 or $3
ADD : The mathematical operation +. Takes the two uppermost values on the
the stack, adds them and pushes the result back onto the stack.
SUB : Same as ADD, but with -.
MUL : Same as ADD, but with *.
DIV : Same as ADD, but with /.
POPVALUE : used when evaluate the expression: store current calculated value
*/
enum parm_item_type
{
NOP, PUSHVALUE, PUSHPARM, ADD, SUB, MUL, DIV, POPVALUE
};
/**
* Class AM_PARAM
* holds a parameter for an "aperture macro" as defined within
* holds an operand for an AM_PARAM as defined within
* standard RS274X. The \a value field can be a constant, i.e. "immediate"
* parameter or it may not be used if this param is going to defer to the
* referencing aperture macro. In that case, the \a index field is an index
* into the aperture macro's parameters.
*/
class AM_PARAM
class AM_PARAM_ITEM
{
public: AM_PARAM() :
index( -1 ),
value( 0.0 )
{}
private:
parm_item_type m_type; // the type of item
double m_dvalue; // the value, for PUSHVALUE type item
int m_ivalue; // the integer value, for PUSHPARM type item
double GetValue( const D_CODE* aDcode ) const;
public:
AM_PARAM_ITEM( parm_item_type aType = NOP, double aValue = 0.0)
{
m_type = aType;
m_dvalue = aValue;
m_ivalue = 0;
}
AM_PARAM_ITEM( parm_item_type aType = NOP, int aValue = 0)
{
m_type = aType;
m_dvalue = 0.0;
m_ivalue = aValue;
}
void SetValue( double aValue )
{
value = aValue;
index = -1;
m_dvalue = aValue;
}
double GetValue( ) const
{
return m_dvalue;
}
parm_item_type GetType() const
{
return m_type;
}
unsigned GetIndex() const
{
return (unsigned) m_ivalue;
}
bool IsOperator() const
{
return m_type == ADD || m_type == SUB || m_type == MUL || m_type == DIV;
}
bool IsOperand() const
{
return m_type == PUSHVALUE || m_type == PUSHPARM;
}
bool IsDefered() const
{
return m_type == PUSHPARM;
}
};
/**
* Class AM_PARAM
* holds a parameter value for an "aperture macro" as defined within
* standard RS274X. The parameter can be a constant, i.e. "immediate" parameter,
* or depend on some defered values, defined in a D_CODE, by the ADD command.
* Note the actual value could need an evaluation from an arithmetical expression
* items in the expression are stored in .
* A simple definition is just a value stored in one item in m_paramStack
*/
class AM_PARAM
{
private:
int m_index; // has meaning to define parameter local to an aperture macro
std::vector<AM_PARAM_ITEM> m_paramStack; // list of operands/operators to evalutate the actual value
// if a par def is $3/2, there are 3 items in stack:
// 3 (type PUSHPARM) , / (type DIV), 2 (type PUSHVALUE)
public:
AM_PARAM();
/**
* function PushOperator
* add an operator/operand to the current stack
* @param aType = the type of item (NOP, PUSHVALUE, PUSHPARM, ADD, SUB, MUL, DIV, EQUATE)
* @param aValue = the item value, double for PUSHVALUE or int for PUSHPARM type.
*/
void PushOperator( parm_item_type aType, double aValue );
void PushOperator( parm_item_type aType, int aValue = 0);
double GetValue( const D_CODE* aDcode ) const;
/**
* Function IsImmediate
* tests if this AM_PARAM holds an immediate parameter or is a pointer
* into a parameter held by an owning D_CODE.
* @return true if the value is immediate, i.e. no defered value in operands used in its definition
*/
bool IsImmediate() const { return index == -1; }
bool IsImmediate() const;
unsigned GetIndex() const
{
return (unsigned) index;
return (unsigned) m_index;
}
void SetIndex( int aIndex )
{
index = aIndex;
m_index = aIndex;
}
/**
* Function ReadParam
* Read one aperture macro parameter
@ -160,13 +261,6 @@ public: AM_PARAM() :
* @return true if a param is read, or false
*/
bool ReadParam( char*& aText );
private:
int index; ///< if -1, then \a value field is an immediate value,
// else this is an index into parent's
// D_CODE.m_am_params.
double value; ///< if IsImmediate()==true then use the value, else
// not used.
};
typedef std::vector<AM_PARAM> AM_PARAMS;

View File

@ -56,7 +56,6 @@ static wxPoint mapPt( double x, double y, bool isMetric )
return ret;
}
/**
* Function mapExposure
* translates the first parameter from an aperture macro into a current
@ -762,3 +761,33 @@ int APERTURE_MACRO::GetShapeDim( GERBER_DRAW_ITEM* aParent )
return dim;
}
/**
* function GetLocalParam
* Usually, parameters are defined inside the aperture primitive
* using immediate mode or defered mode.
* in defered mode the value is defined in a DCODE that want to use the aperture macro.
* But some parameters are defined outside the aperture primitive
* and are local to the aperture macro
* @return the value of a defered parameter defined inside the aperture macro
* @param aParamId = the param id (defined by $3 or $5 ..) to evaluate
*/
double APERTURE_MACRO::GetLocalParam( const D_CODE* aDcode, unsigned aParamId ) const
{
// find parameter descr.
const AM_PARAM * param = NULL;
for( unsigned ii = 0; ii < m_localparamStack.size(); ii ++ )
{
if( m_localparamStack[ii].GetIndex() == aParamId )
{
param = &m_localparamStack[ii];
break;
}
}
if ( param == NULL ) // not found
return 0.0;
// Evaluate parameter
double value = param->GetValue( aDcode );
return value;
}

View File

@ -45,6 +45,13 @@
* Each parameter can be an immediate value or a defered value.
* When value is defered, it is defined when the aperture macro is instancied by
* an ADD macro command
* Note also a defered parameter can be defined in aperture macro,
* but outside aperture primitives. Example
* %AMRECTHERM*
* $4=$3/2* parameter $4 is half value of parameter $3
* 21,1,$1-$3,$2-$3,0-$1/2-$4,0-$2/2-$4,0*
* For the aperture primitive, parameters $1 to $3 will be defined in ADD command,
* and $4 is defined inside the macro
*/
/**
@ -83,7 +90,6 @@ public:
// the primitive
bool m_GerbMetric; // units for this primitive:
// false = Inches, true = metric
public: AM_PRIMITIVE( bool aGerbMetric, AM_PRIMITIVE_ID aId = AMP_UNKNOWN )
{
primitive_id = aId;
@ -167,6 +173,26 @@ struct APERTURE_MACRO
wxString name; ///< The name of the aperture macro
AM_PRIMITIVES primitives; ///< A sequence of AM_PRIMITIVEs
/* A defered parameter can be defined in aperture macro,
* but outside aperture primitives. Example
* %AMRECTHERM*
* $4=$3/2* parameter $4 is half value of parameter $3
* m_localparamStack handle a list of local defered parameters
*/
AM_PARAMS m_localparamStack;
/**
* function GetLocalParam
* Usually, parameters are defined inside the aperture primitive
* using immediate mode or defered mode.
* in defered mode the value is defined in a DCODE that want to use the aperture macro.
* But some parameters are defined outside the aperture primitive
* and are local to the aperture macro
* @return the value of a defered parameter defined inside the aperture macro
* @param aParamId = the param id (defined by $3 or $5 ..) to evaluate
*/
double GetLocalParam( const D_CODE* aDcode, unsigned aParamId ) const;
/**
* Function DrawApertureMacroShape
* Draw the primitive shape for flashed items.

View File

@ -146,7 +146,7 @@ public:
}
APERTURE_MACRO* GetMacro() { return m_Macro; }
APERTURE_MACRO* GetMacro() const { return m_Macro; }
/**
* Function ShowApertureType

View File

@ -837,12 +837,15 @@ bool GERBER_IMAGE::ReadApertureMacro( char buff[GERBER_BUFZ],
// it can be: a parameter declaration like $1=$2/4
// or a digit (macro primitive selection)
// all other symbols are illegal.
if( *text == '$' ) // parameter declaration, not yet supported
if( *text == '$' ) // local parameter declaration, inside the aperture macro
{
msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": Operator $ not yet supported here, line: \"%s\"" ),
GetChars( am.name ), GetChars( CONV_FROM_UTF8( buff ) ) );
ReportMessage( msg );
primitive_type = AMP_COMMENT;
am.m_localparamStack.push_back( AM_PARAM() );
AM_PARAM& param = am.m_localparamStack.back();
text = GetNextLine( buff, text, gerber_file );
if( text == NULL) // End of File
return false;
param.ReadParam( text );
continue;
}
else if( !isdigit(*text) ) // Ill. symbol
{

View File

@ -48,7 +48,7 @@ typedef boost::ulong_long_type polygon_ulong_long_type;
#include <boost/mpl/or.hpp>
#else
#ifdef WIN32
#ifdef _WIN32
#define BOOST_POLYGON_MSVC
#endif
#ifdef __ICC
@ -290,7 +290,7 @@ namespace boost { namespace polygon{
template <typename T>
struct gtl_if {
#ifdef WIN32
#ifdef BOOST_POLYGON_MSVC
typedef gtl_no type;
#endif
};

View File

@ -1170,7 +1170,7 @@ namespace boost { namespace polygon{
//odd count implies boundary condition
if(counts[0] % 2 || counts[1] % 2) return consider_touch;
//an odd number of edges to the left implies interior pt
return counts[0] % 4 != 0;
return counts[winding(polygon) == COUNTERCLOCKWISE ? 0 : 1] % 4 != 0;
}
//TODO: refactor to expose as user APIs