Files
projectm/src/projectM-engine/Expr.cpp

547 lines
13 KiB
C++
Executable File

/**
* projectM -- Milkdrop-esque visualisation SDK
* Copyright (C)2003-2004 projectM Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* See 'LICENSE.txt' included within this release
*
*/
#include "wipemalloc.h"
#include "Expr.h"
#include "Eval.h"
#include <cassert>
float GenExpr::eval_gen_expr() {
float l;
switch(type) {
case VAL_T:
return ((ValExpr*)item)->eval_val_expr();
case PREFUN_T:
l = ((PrefunExpr *)item)->eval_prefun_expr();
//if (EVAL_DEBUG) DWRITE( "eval_gen_expr: prefix function return value: %f\n", l);
return l;
case TREE_T:
return ((TreeExpr*)(item))->eval_tree_expr();
default:
#ifdef EVAL_DEBUG
DWRITE( "eval_gen_expr: general expression matched no cases!\n");
#endif
return EVAL_ERROR;
}
}
/* Evaluates functions in prefix form */
float PrefunExpr::eval_prefun_expr() {
int i;
float rv;
/* This is slightly less than safe, since
who knows if the passed argument is valid. For
speed purposes we'll go with this */
float *arg_list = (float *)wipemalloc( sizeof( float ) * num_args );
#ifdef EVAL_DEBUG_DOUBLE
DWRITE( "fn[");
#endif
/* Evaluate each argument before calling the function itself */
for (i = 0; i < num_args; i++) {
arg_list[i] = expr_list[i]->eval_gen_expr();
#ifdef EVAL_DEBUG_DOUBLE
if (i < (num_args - 1))
DWRITE( ", ");
#endif
}
#ifdef EVAL_DEBUG_DOUBLE
DWRITE( "]");
#endif
/* Now we call the function, passing a list of
floats as its argument */
rv = (func_ptr)(arg_list);
free( arg_list );
arg_list = NULL;
return rv;
}
/* Evaluates a value expression */
float ValExpr::eval_val_expr() {
/* Shouldn't happen */
/* Value is a constant, return the float value */
if (type == CONSTANT_TERM_T) {
#ifdef EVAL_DEBUG
DWRITE( "%.4f", term.constant);
#endif
return (term.constant);
}
/* Value is variable, dereference it */
if (type == PARAM_TERM_T) {
switch (term.param->type) {
case P_TYPE_BOOL:
#ifdef EVAL_DEBUG
DWRITE( "(%s:%.4f)", term.param->name, (float)(*((int*)(term.param->engine_val))));
#endif
return (float)(*((int*)(term.param->engine_val)));
case P_TYPE_INT:
#ifdef EVAL_DEBUG
DWRITE( "(%s:%.4f)", term.param->name, (float)(*((int*)(term.param->engine_val))));
#endif
return (float)(*((int*)(term.param->engine_val)));
case P_TYPE_DOUBLE:
#ifdef EVAL_DEBUG_DOUBLE
DWRITE( "(%s:%.4f)", term.param->name, (*((float*)term.param->engine_val)));
#endif
if (term.param->matrix_flag | (term.param->flags & P_FLAG_ALWAYS_MATRIX)) {
/** Sanity check the matrix is there... */
assert(term.param->matrix != NULL );
if (projectM::currentEngine->mesh_i >= 0) {
if (projectM::currentEngine->mesh_j >= 0) {
return (((float**)term.param->matrix)[projectM::currentEngine->mesh_i][projectM::currentEngine->mesh_j]);
} else {
return (((float*)term.param->matrix)[projectM::currentEngine->mesh_i]);
}
}
}
return *((float*)(term.param->engine_val));
default:
return EVAL_ERROR;
}
}
/* Unknown type, return failure */
return PROJECTM_FAILURE;
}
/* Evaluates an expression tree */
float TreeExpr::eval_tree_expr() {
float left_arg, right_arg;
/* A leaf node, evaluate the general expression. If the expression is null as well, return zero */
if (infix_op == NULL) {
if (gen_expr == NULL)
return 0;
else
return gen_expr->eval_gen_expr();
}
/* Otherwise, this node is an infix operator. Evaluate
accordingly */
#ifdef EVAL_DEBUG
DWRITE( "(");
#endif
left_arg = left->eval_tree_expr();
#ifdef EVAL_DEBUG
switch (infix_op->type) {
case INFIX_ADD:
DWRITE( "+");
break;
case INFIX_MINUS:
DWRITE( "-");
break;
case INFIX_MULT:
DWRITE( "*");
break;
case INFIX_MOD:
DWRITE( "%%");
break;
case INFIX_OR:
DWRITE( "|");
break;
case INFIX_AND:
DWRITE( "&");
break;
case INFIX_DIV:
DWRITE( "/");
break;
default:
DWRITE( "?");
}
#endif
right_arg = right->eval_tree_expr();
#ifdef EVAL_DEBUG
DWRITE( ")");
#endif
#ifdef EVAL_DEBUG
DWRITE( "\n" );
#endif
switch (infix_op->type) {
case INFIX_ADD:
return (left_arg + right_arg);
case INFIX_MINUS:
return (left_arg - right_arg);
case INFIX_MULT:
return (left_arg * right_arg);
case INFIX_MOD:
if ((int)right_arg == 0) {
#ifdef EVAL_DEBUG
DWRITE( "eval_tree_expr: modulo zero!\n");
#endif
return PROJECTM_DIV_BY_ZERO;
}
return ((int)left_arg % (int)right_arg);
case INFIX_OR:
return ((int)left_arg | (int)right_arg);
case INFIX_AND:
return ((int)left_arg & (int)right_arg);
case INFIX_DIV:
if (right_arg == 0) {
#ifdef EVAL_DEBUG
DWRITE( "eval_tree_expr: division by zero!\n");
#endif
return MAX_DOUBLE_SIZE;
}
return (left_arg / right_arg);
default:
#ifdef EVAL_DEBUG
DWRITE( "eval_tree_expr: unknown infix operator!\n");
#endif
return EVAL_ERROR;
}
return EVAL_ERROR;
}
/* Converts a float value to a general expression */
GenExpr * GenExpr::const_to_expr(float val) {
GenExpr * gen_expr;
ValExpr * val_expr;
Term term;
term.constant = val;
if ((val_expr = ValExpr::new_val_expr(CONSTANT_TERM_T, &term)) == NULL)
return NULL;
gen_expr = GenExpr::new_gen_expr(VAL_T, (void*)val_expr);
if (gen_expr == NULL) {
delete val_expr;
}
return gen_expr;
}
/* Converts a regular parameter to an expression */
GenExpr * GenExpr::param_to_expr(Param * param) {
GenExpr * gen_expr = NULL;
ValExpr * val_expr = NULL;
Term term;
if (param == NULL)
return NULL;
/* This code is still a work in progress. We need
to figure out if the initial condition is used for
each per frame equation or not. I am guessing that
it isn't, and it is thusly implemented this way */
/* Current guess of true behavior (08/01/03) note from carm
First try to use the per_pixel_expr (with cloning).
If it is null however, use the engine variable instead. */
/* 08/20/03 : Presets are now objects, as well as per pixel equations. This ends up
making the parser handle the case where parameters are essentially per pixel equation
substitutions */
term.param = param;
if ((val_expr = ValExpr::new_val_expr(PARAM_TERM_T, &term)) == NULL)
return NULL;
if ((gen_expr = GenExpr::new_gen_expr(VAL_T, (void*)val_expr)) == NULL) {
delete val_expr;
return NULL;
}
return gen_expr;
}
/* Converts a prefix function to an expression */
GenExpr * GenExpr::prefun_to_expr(float (*func_ptr)(void *), GenExpr ** expr_list, int num_args) {
GenExpr * gen_expr;
PrefunExpr * prefun_expr;
/* Malloc a new prefix function expression */
prefun_expr = (PrefunExpr*)wipemalloc(sizeof(PrefunExpr));
if (prefun_expr == NULL)
return NULL;
prefun_expr->num_args = num_args;
prefun_expr->func_ptr =(float (*)(void*)) func_ptr;
prefun_expr->expr_list = expr_list;
gen_expr = new_gen_expr(PREFUN_T, (void*)prefun_expr);
if (gen_expr == NULL)
delete prefun_expr;
return gen_expr;
}
/* Creates a new tree expression */
TreeExpr *TreeExpr::new_tree_expr(InfixOp * infix_op, GenExpr * gen_expr, TreeExpr * left, TreeExpr * right) {
TreeExpr * tree_expr;
tree_expr = (TreeExpr*)wipemalloc(sizeof(TreeExpr));
if (tree_expr == NULL)
return NULL;
tree_expr->infix_op = infix_op;
tree_expr->gen_expr = gen_expr;
tree_expr->left = left;
tree_expr->right = right;
return tree_expr;
}
/* Creates a new value expression */
ValExpr *ValExpr::new_val_expr(int type, Term *term) {
ValExpr * val_expr;
val_expr = (ValExpr*)wipemalloc(sizeof(ValExpr));
if (val_expr == NULL)
return NULL;
val_expr->type = type;
val_expr->term.constant = term->constant;
val_expr->term.param = term->param;
return val_expr;
}
/* Creates a new general expression */
GenExpr * GenExpr::new_gen_expr(int type, void * item) {
GenExpr * gen_expr;
gen_expr = (GenExpr*)wipemalloc(sizeof(GenExpr));
if (gen_expr == NULL)
return NULL;
gen_expr->type = type;
gen_expr->item = item;
return gen_expr;
}
/* Frees a general expression */
GenExpr::~GenExpr() {
switch (type) {
case VAL_T:
delete ((ValExpr*)item);
break;
case PREFUN_T:
delete ((PrefunExpr*)item);
break;
case TREE_T:
delete ((TreeExpr*)item);
break;
}
}
/* Frees a function in prefix notation */
PrefunExpr::~PrefunExpr() {
int i;
/* Free every element in expression list */
for (i = 0 ; i < num_args; i++) {
delete expr_list[i];
}
free(expr_list);
}
/* Frees values of type VARIABLE and CONSTANT */
ValExpr::~ValExpr() {
}
/* Frees a tree expression */
TreeExpr::~TreeExpr() {
/* free left tree */
if ( left != NULL ) {
delete left;
}
/* free general expression object */
if ( gen_expr != NULL ) {
delete gen_expr;
}
/* Note that infix operators are always
stored in memory unless the program
exits, so we don't remove them here */
/* free right tree */
if ( right != NULL ) {
delete right;
}
}
/* Initializes an infix operator */
DLLEXPORT InfixOp::InfixOp(int type, int precedence) {
this->type = type;
this->precedence = precedence;
}
/* Clones a general expression */
GenExpr *GenExpr::clone_gen_expr() {
GenExpr * new_gen_expr;
ValExpr * val_expr;
TreeExpr * tree_expr;
PrefunExpr * prefun_expr;
/* Out of memory */
if ((new_gen_expr = (GenExpr*)wipemalloc(sizeof(GenExpr))) == NULL)
return NULL;
/* Case on the type of general expression */
switch (new_gen_expr->type = type) {
case VAL_T: /* val expression */
if ((val_expr = ((ValExpr*)item)->clone_val_expr()) == NULL) {
free(new_gen_expr);
new_gen_expr = NULL;
return NULL;
}
new_gen_expr->item = (void*)val_expr;
break;
case PREFUN_T: /* prefix function expression */
if ((prefun_expr = ((PrefunExpr*)item)->clone_prefun_expr()) == NULL) {
free(new_gen_expr);
new_gen_expr = NULL;
return NULL;
}
new_gen_expr->item = (void*)prefun_expr;
break;
case TREE_T: /* tree expression */
if ((tree_expr = ((TreeExpr*)item)->clone_tree_expr()) == NULL) {
free(new_gen_expr);
new_gen_expr = NULL;
return NULL;
}
new_gen_expr->item = (void*)tree_expr;
break;
default: /* unknown type, ut oh.. */
free(new_gen_expr);
new_gen_expr = NULL;
return NULL;
}
return new_gen_expr; /* Return the new (cloned) general expression */
}
/* Clones a tree expression */
TreeExpr *TreeExpr::clone_tree_expr() {
TreeExpr * new_tree_expr;
/* Out of memory */
if ((new_tree_expr = (TreeExpr*)wipemalloc(sizeof(TreeExpr))) == NULL)
return NULL;
/* Set each argument in TreeExpr struct */
new_tree_expr->infix_op = infix_op; /* infix operators are in shared memory */
new_tree_expr->gen_expr = gen_expr->clone_gen_expr(); /* clone the general expression */
new_tree_expr->left = left->clone_tree_expr(); /* clone the left tree expression */
new_tree_expr->right = right->clone_tree_expr(); /* clone the right tree expression */
return new_tree_expr; /* Return the new (cloned) tree expression */
}
/* Clones a value expression, currently only passes the pointer to
the value that this object represents, not a pointer to a copy of the value */
ValExpr *ValExpr::clone_val_expr() {
ValExpr * new_val_expr;
/* Allocate space, check for out of memory */
if ((new_val_expr = (ValExpr*)wipemalloc(sizeof(ValExpr))) == NULL)
return NULL;
/* Set the values in the ValExpr struct */
new_val_expr->type = type;
new_val_expr->term = term;
/* Return the new (cloned) value expression */
return new_val_expr;
}
/* Clones a prefix function with its arguments */
PrefunExpr *PrefunExpr::clone_prefun_expr() {
int i;
PrefunExpr * new_prefun_expr;
/* Out of memory */
if ((new_prefun_expr = (PrefunExpr*)wipemalloc(sizeof(PrefunExpr))) == NULL)
return NULL;
/* Set the function argument paired with its number of arguments */
new_prefun_expr->num_args = num_args;
new_prefun_expr->func_ptr = func_ptr;
/* Allocate space for the expression list pointers */
if ((new_prefun_expr->expr_list = (GenExpr**)wipemalloc(sizeof(GenExpr*)*new_prefun_expr->num_args)) == NULL) {
free( new_prefun_expr );
new_prefun_expr = NULL;
return NULL;
}
/* Now copy each general expression from the argument expression list */
for (i = 0; i < new_prefun_expr->num_args;i++)
new_prefun_expr->expr_list[i] = expr_list[i]->clone_gen_expr();
/* Finally, return the new (cloned) prefix function expression */
return new_prefun_expr;
}