Public Member Functions | Public Attributes | Friends

Expr Class Reference

Search for all occurrences

Detailed Description

See also:
Class Point3, Expression Types, Expression Variable Types, Expression Return Codes, Character Strings.

Description:
This class may be used by developers to parse mathematical expressions. The expression is created as a character string using a straightforward syntax. Expressions consist of operators (+, -, *, /, etc.), literal constants (numbers like 180, 2.718, etc.), variables (single floating point values or vector (Point3) values), and functions (mathematical functions that take one ore more arguments and return a result). The return value from the expression may be a floating point value or a vector. There are many built in functions, operators and constants available for use.

All methods of this class are implemented by the system.

Developers wishing to use these APIs should #include /MAXSDK/INCLUDE/EXPRLIB.H and should link to /MAXSDK/LIB/EXPR.LIB.

Sample code using these APIs is shown below, and is also available as part of the expression controller in /MAXSDK/SAMPLES/CONTROLLERS/EXPRCTRL.CPP.

Variables may be defined and used in expressions. Variable names are case sensitive, and must begin with a letter of the alphabet, but may include numbers. They may be any length. To create a named variable, you use the method defVar(). This takes a name and returns a register number. Defining the variable creates storage space in a list of variables maintained by the parser, and the register number is used as an array index into the variable value arrays passed into the expression evaluation method (eval()).

To use the variable in an expression just use its name. For example if you define a variable named radius, you can use it in an expression like: 2*pi*radius. To give the variable a value, you define two arrays of variables and pass them to the evaluation method (eval()). There is one array for scalar variables, and one for vector variables. You pass these arrays along with the number of variables in each list. See the sample code below for an example.

The order of calling the methods of this class to evaluate an expression is as follows:

Declare an expression instance (Expr expr;)

Define the expression (char e1[] = "2*pi*radius";).

Define any variables (expr.defVar(SCALAR_VAR, _M("radius"));)

Load the expression (expr.load(e1);)

Evaluate the expression (expr.eval(...);)

There are no restrictions on the use of white space in expressions -- it may be used freely to make expressions more readable. In certain instances, white space should be used to ensure non-ambiguous parsing. For example, the x operator is used for to compute the cross product of two vectors. If a developer has several vectors: Vec, Axis and xAxis and wanted to compute the cross product, VecxAxis is ambiguous while Vec x Axis is not.

All the necessary information to evaluate an expression is completely stored within an expression object. For example, if you are passed a pointer to an expression object for which some variables have been defined that you knew the value of, you could get all the information you needed from the expression object to completely evaluate the expression. This includes the expression string, variable names, variable types, and variable register indices.

For complete documentation of the built in functions please refer to the 3ds Max User's Guide under Using Expression Controllers. Below is an overview of the operators, constants and functions that are available:
Expression Operators:
Scalar Operators

Operator Use Meaning

+ p+q addition

- p-q subtraction

- -p additive inverse

* p*q multiplication

/ p/q division

^ p^q power (p to the power of q)

** p**q same as p^q

Boolean Operators

= p=q equal to

< p<q less than

> p>q greater than

<= p<=q less than or equal to

>= p>=q greater than or equal to

| p|q logical OR

& p&q logical AND

Vector Operators

+ V+W addition

- V-W subtraction

* p*V scalar multiplication

V*p "

* V*W dot product

x VxW cross product

/ V/p scalar division

. V.x first component (X)

. V.y second component (Y)

. V.z third component (Z)
Built-In Constants:
pi 3.1415...

e 2.7182...

TPS 4800 (ticks per second)
Expression Functions:
Trigonometric Functions

The angles are specified and returned in degrees.

sin(p) sine

cos(p) cosine

tan(p) tangent

asin(p) arc sine

acos(p) arc cosine

atan(p) arc tangent

Hyperbolic Functions

sinh(p) hyperbolic sine

cosh(p) hyperbolic cosine

tanh(p) hyperbolic tangent

Conversion between Radians and Degrees

radToDeg(p) takes p in radians and returns the same angle in degrees

degToRad(p) takes p in degrees and returns the same angle in radians

Rounding Functions

ceil(p) smallest integer greater than or equal to p.

floor(p) largest integer less than or equal to p.

Standard Calculations

ln(p) natural (base e) logarithm

log(p) common (base 10) logarithm

exp(p) exponential function -- exp(e) = e^p

pow(p, q) p to the power of q -- p^q

sqrt(p) square root

abs(p) absolute value

min(p, q) minimum -- returns p or q depending on which is smaller

max(p, q) maximum -- returns p or q depending on which is larger

mod(p, q) remainder of p divided by q

Conditional

if (p, q, r) works like the common spreadsheet "if" -- if p is nonzero

then "if" returns q, otherwise r.

Vector Handling

length(V) the length of V

unit(V) returns a unit vector in the same direction as V.

comp(V, I) i-th component, where I=0, 1, or 2.

comp([5,6,7],1) = 6

Special Animation Functions

noise(p, q, r) 3D noise -- returns a randomly generated position.

p, q, and r are random values used as a seed.
Sample Code:
The following code shows how the expression parser can be used. This code evaluates several expressions and displays the results in a dialog box. Both scalar and vector variables are used. One expression contains an error to show how error handling is done.

void Utility::TestExpr()
{
// Declare an expression instance and variable storage
    Expr expr;
    float sRegs[2];                               // Must be at least getVarCount(SCALAR_VAR);
    Point3 vRegs[2];                              // Must be at least getVarCount(VECTOR_VAR);
    float ans[3];
    int status;

// Define a few expressions
    char e0[] = "2+2";
    char e1[] = "2.0 * pi * radius";
    char e2[] = "[1,1,0] + axis";
    char e3[] = "[sin(90.0), sin(radToDeg(0.5*pi)), axis.z]";
    char e4[] = "2+2*!@#$%";                      // Bad expression

// Define variables
    int radiusReg = expr.defVar(SCALAR_VAR, _M("radius"));
    int axisReg = expr.defVar(VECTOR_VAR, _M("axis"));
// Set the variable values
    sRegs[radiusReg] = 50.0f;
    vRegs[axisReg] = Point3(0.0f, 0.0f, 1.0f);
// Get the number of each we have defined so far
    int sCount = expr.getVarCount(SCALAR_VAR);
    int vCount = expr.getVarCount(VECTOR_VAR);

// Load and evaluate expression "e0"
    if (status = expr.load(e0))
        HandleLoadError(status, expr);
    else {
        status = expr.eval(ans, sCount, sRegs, vCount, vRegs);
        if (status != EXPR_NORMAL)
            HandleEvalError(status, expr);
        else
            DisplayExprResult(expr, ans);
    }
// Load and evaluate expression "e1"
    if (status = expr.load(e1))
        HandleLoadError(status, expr);
    else {
        status = expr.eval(ans, sCount, sRegs, vCount, vRegs);
        if (status != EXPR_NORMAL)
            HandleEvalError(status, expr);
        else
            DisplayExprResult(expr, ans);
    }
// Load and evaluate expression "e2"
    if (status = expr.load(e2))
        HandleLoadError(status, expr);
    else {
        status = expr.eval(ans, sCount, sRegs, vCount, vRegs);
        if (status != EXPR_NORMAL)
            HandleEvalError(status, expr);
        else
            DisplayExprResult(expr, ans);
    }
// Load and evaluate expression "e3"
    if (status = expr.load(e3))
        HandleLoadError(status, expr);
    else {
        status = expr.eval(ans, sCount, sRegs, vCount, vRegs);
        if (status != EXPR_NORMAL)
            HandleEvalError(status, expr);
        else
            DisplayExprResult(expr, ans);
    }
// Load and evaluate expression "e4"
    if (status = expr.load(e4))
        HandleLoadError(status, expr);
    else {
        status = expr.eval(ans, sCount, sRegs, vCount, vRegs);
        if (status != EXPR_NORMAL)
            HandleEvalError(status, expr);
        else
            DisplayExprResult(expr, ans);
    }
}


// Display the expression and the result
void Utility::DisplayExprResult(Expr expr, float *ans)
{
    MCHAR msg[128];

    if (expr.getExprType() == SCALAR_EXPR) {
        _stprintf(msg, _M("Answer to \"%s\" is %.1f"), expr.getExprStr(), *ans);
        Message(msg, _M("Expression Result"));
    }
    else {
        _stprintf(msg, _M("Answer to \"%s\" is [%.1f, %.1f, %.1f]"), expr.getExprStr(), ans[0], ans[1], ans[2]);
        Message(msg, _M("Expression Result"));
    }
}


// Display the load error message
void Utility::HandleLoadError(int status, Expr expr)
{
    MCHAR msg[128];

    if(status == EXPR_INST_OVERFLOW) {
        _stprintf(_M("Inst stack overflow: %s"), expr.getProgressStr());
        Message(msg, _M("Error"));
    }
    else if (status == EXPR_UNKNOWN_TOKEN) {
        _stprintf(msg, _M("Unknown token: %s"), expr.getProgressStr());
        Message(msg, _M("Error"));
    }
    else {
        _stprintf(msg, _M("Cannot parse \"%s\". Error begins at last char of: %s"),
        expr.getExprStr(), expr.getProgressStr());
        Message(msg, _M("Error"));
    }
}

// Display the evaluation error message
void Utility::HandleEvalError(int status, Expr expr)
{
    MCHAR msg[128];

    _stprintf(msg, _M("Can't parse expression \"%s\""), expr.getExprStr());
    Message(msg, _M("Error"));
}

// Display the specified message and title in a dialog box
void Utility::Message(MCHAR *msg, MCHAR *title)
{
    MessageBox(ip->GetMAXHWnd(), (LPCMSTR) msg, (LPCMSTR) title, MB_ICONINFORMATION|MB_OK);
}

#include <expr.h>

Inheritance diagram for Expr:
Inheritance graph
[legend]

List of all members.

Public Member Functions

  Expr ()
  ~Expr ()
DllExport int  load (MCHAR *s)
DllExport int  eval (float *ans, int sRegCt, float *sRegs, int vRegCt=0, Point3 *vRegs=NULL)
int  getExprType (void)
MCHAR *  getExprStr (void)
MCHAR *  getProgressStr (void)
DllExport int  defVar (int type, MCHAR *name)
DllExport int  getVarCount (int type)
DllExport MCHAR *  getVarName (int type, int i)
DllExport int  getVarRegNum (int type, int i)
DllExport BOOL  deleteAllVars ()
DllExport BOOL  deleteVar (MCHAR *name)
void  setExprType (int type)
void  pushInst (ExprFunc fn, float f)
void  pushSVal (float f)
float  popSVal ()
void  pushVVal (Point3 &v)
Point3 popVVal ()
int  getSRegCt (void)
float  getSReg (int index)
int  getVRegCt (void)
Point3 getVReg (int index)

Public Attributes

ExprVarTab  vars

Friends

int  yylex ()
int  yyerror (char *)

Constructor & Destructor Documentation

Expr ( ) [inline]
Remarks:
Constructor. Internal data structures are initialized as empty.
{ sValStk = vValStk = instStk = nextScalar = nextVector = 0; }
~Expr ( ) [inline]
Remarks:
Destructor. Any currently defined variables are deleted.
{ deleteAllVars(); }

Member Function Documentation

DllExport int load ( MCHAR *  s )
Remarks:
This method is used to load an expression for parsing. An error code is returned indicating if the expression was loaded. A successfully loaded expression is then ready for evaluation with the eval() method.
Parameters:
char *s

The expression to load.
Returns:
See Expression Return Codes.
DllExport int eval ( float *  ans,
int  sRegCt,
float *  sRegs,
int  vRegCt = 0,
Point3 vRegs = NULL 
)
Remarks:
This method is used to evaluate the expression loaded using load(). It returns either a scalar or vector result.
Parameters:
float *ans

The numeric result of the expression is returned here, i.e. the answer . For scalar values this is a pointer to a single float. For vector values, ans[0] is x, ans[1] = y, ans[2] = z. You can determine which type of result is returned using the method getExprType().

int sRegCt

The number of items in the sRegs array of scalar variables.

float *sRegs

Array of scalar variables.

int vRegCt=0

The number of items in the vRegs array of vector variables.

Point3 *vRegs=NULL

Array of vector variables.
Returns:
See Expression Return Codes.
int getExprType ( void  ) [inline]
Remarks:
Returns the type of expression. See Expression Types.
{ return exprType; }
MCHAR* getExprStr ( void  ) [inline]
Remarks:
Returns a pointer to the currently loaded expression string.
{ return origStr; }
MCHAR* getProgressStr ( void  ) [inline]
Remarks:
If there was an error parsing the expression, this method returns a string showing what portion of the expression was parsed before the error occurred.
{ return progressStr; }
DllExport int defVar ( int  type,
MCHAR *  name 
)
Remarks:
Defines a named variable that may be used in an expression.
Parameters:
int type

The type of variable. See Expression Variable Types.

MCHAR *name

The name of the variable. This name must begin with a letter, may include numbers and may be any length.
Returns:
The register number (into the sRegs or vRegs array passed to eval()) of the variable.
DllExport int getVarCount ( int  type )
Remarks:
This method returns the number of variables defined of the specified type. When you call eval() on an expression, you must make sure that the variable arrays (sRegs and vRegs) are at least the size returned from this method.
Parameters:
int type

See Expression Variable Types.
DllExport MCHAR* getVarName ( int  type,
int  i 
)
Remarks:
Returns the name of the variable whose index is passed, or NULL if the variable could not be found.
Parameters:
int type

The type the variable. See Expression Variable Types.

int i

The register number of the variable.
DllExport int getVarRegNum ( int  type,
int  i 
)
Remarks:
When you define a variable with defVar(), you get a back a register number. If your code is set up in such a way that saving that register number is not convenient in the block of code that defines it, you can use this method later on to find out what that return value had been. For example, one piece of code might have:

expr->defVar(SCALAR_VAR, "a"); // not saving return value...

expr->defVar(SCALAR_VAR, "b");

and then right before evaluating the expression, you might have some code such as:

for(i = 0; i < expr->getVarCount(SCALAR_VAR); i++)

if(_tcscmp("a", expr->getVarName(SCALAR_VAR, i) == 0)

aRegNum = expr->getVarRegNum(SCALAR_VAR, i);

Of course, this is a bit contrived -- most real examples would probably have tables to store the variable names, register numbers, etc. and thus would not need to call this method. It is available however, and this makes the expression object self-contained in that everything you need to evaluate an expression with variables (other than the variable values themselves) is stored by the expression object.
Parameters:
int type

See Expression Variable Types.

int i

The variable index returned from the method defVar().
Returns:
The register index for the variable whose type and index are passed.
DllExport BOOL deleteAllVars ( )
Remarks:
Deletes all the variables from the list maintained by the expression.
Returns:
TRUE if the variables were deleted; otherwise FALSE.
DllExport BOOL deleteVar ( MCHAR *  name )
Remarks:
Deletes the variable whose name is passed from the list maintained by the expression. Register numbers never get reassigned, even if a variable gets deleted. For example, if you delete variables 0-9, and keep variable 10, you're going to need to pass in an array of size at least 11 to the eval() method, even though the first 10 slots are unused.
Parameters:
MCHAR *name

The name of the variable to delete.
Returns:
TRUE if the variable was deleted; otherwise FALSE (the name was not found).
void setExprType ( int  type ) [inline]
{ exprType = type; }
void pushInst ( ExprFunc  fn,
float  f 
) [inline]
                    { if(instStk >= inst.Count()) inst.SetCount(instStk+30); 
                    inst[instStk].func = fn; inst[instStk++].sVal = f; }
void pushSVal ( float  f ) [inline]
{ if(sValStk>=sVal.Count())sVal.SetCount(sValStk+10);sVal[sValStk++]=f; }
float popSVal ( ) [inline]
{ return sVal[--sValStk]; }
void pushVVal ( Point3 v ) [inline]
{ if(vValStk>=vVal.Count())vVal.SetCount(vValStk+10);vVal[vValStk++]=v; }
Point3& popVVal ( ) [inline]
{ return vVal[--vValStk]; }
int getSRegCt ( void  ) [inline]
{ return sRegCt; }
float getSReg ( int  index ) [inline]
{ return sRegPtr[index]; }
int getVRegCt ( void  ) [inline]
{ return vRegCt; }
Point3& getVReg ( int  index ) [inline]
{ return vRegPtr[index]; }

Friends And Related Function Documentation

int yylex ( ) [friend]
int yyerror ( char *  ) [friend]

Member Data Documentation


Expr Expr Expr Expr Expr Expr Expr Expr Expr Expr
Expr Expr Expr Expr Expr Expr Expr Expr Expr Expr