Commit 938fc742 authored by jean-pierre charras's avatar jean-pierre charras
Browse files

Gerbview: Added support for complex definitions of parameters (like...

Gerbview:  Added support for complex definitions of parameters (like $3=$2+5/2)  in aperture macros and primitives macro.
parent 0134da4c
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -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
+176 −34
Original line number Diff line number Diff line
@@ -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.
 * 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++ )
    {
        AM_PARAM_ITEM item = m_paramStack[ii];
        switch( item.GetType() )
        {
        // the first one was numbered 1, not zero, as in $1, see page 19 of spec.
        unsigned ndx = GetIndex();
        wxASSERT( aDcode );
            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;
}
+115 −21
Original line number Diff line number Diff line
@@ -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;
+30 −1
Original line number Diff line number Diff line
@@ -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;
}
+27 −1
Original line number Diff line number Diff line
@@ -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.
Loading